[除錯逆向] [原創]360通殺5代機器狗工具驅動部分分析

gjden發表於2010-11-16
360通殺5代機器狗工具驅動部分分析

這個工具應該出好久了,由於入門比較晚,機器狗橫行時,我還在學校裡肯C++.這也是上週六,週日弄的,
這個工具也是無意間看到的,就把它下下來,想看看到底怎麼做的,也當是練習練習,感覺逆向中學到了不少東西.
完全是靜態分析,沒有一點除錯,如有錯誤,請指出!

OD載入執行工具,提取驅動檔案,檔名是隨機的,我這次提取出了檔名為01d73f48.sys,這個驅動做了很多HOOK,
而且HOOK型別也很豐富,有SSDT HOOK,IRP HOOK,EAT HOOK,INLINE HOOK等,MJ的物件劫持技術也用上了,看著貌似很強大.
這裡做一個功能簡單說明,由於程式比較大,很多地方為了偷懶,就直接F5了,呵呵.程式碼比較多,我就不貼程式碼了,具體看IDB.
本人水平有限,很多地方可能不準確,甚至是錯誤的,望大家指出.

1.驅動首先建立裝置名為"\Device\DRIECTX0"裝置,符號連線名為"\DosDevices\DRIECTX1",然後註冊如下幾個MajorFunction
IRP_MJ_CREATE,
IRP_MJ_CLOSE,
IRP_MJ_DEVICE_CONTROL
派遣例程均有sub_10520處理;
在派遣例程中,實際只處理了IRP_MJ_DEVICE_CONTROL,對IRP_MJ_CREATE和IRP_MJ_CLOSE是直接完成返回的.
此函式呼叫另外一個函式來完成IOCONTROL CODE的處理,有兩個需要響應的控制碼.
0xC07FE000
0xC07FE004
在0xC07FE000中根據使用者傳入的事件控制程式碼,獲取事件控制程式碼的物件,這個物件是在核心中是全域性的.
而在0xC07FE004是用來獲取驅動處理資訊的控制碼,這裡是獲取驅動得到執行緒ID和程式ID(在後面有說明),
還有一個標誌驅動處理型別的標誌(這個資料跟使用者態可能有很到關係,由於沒有去逆向使用者態程式,
也不能確定其具體功能,暫且這樣認為),具體看idb.

2.接著便呼叫sub_12602()函式,這個函式便是此驅動的核心,此函式做了如下工作.
HookIoFreeIrp
HookIofCallDriver
獲取檔案\SystemRoot\system32\drivers\atapi.sys相關資訊
獲取檔案\SystemRoot\system32\drivers\classpnp.sys相關資訊
HookDiskDriver
HookClasspnpClassInit()
HookTopDriver \Device\Harddisk0\\DR0
HookTopDriver \Device\HarddiskVolume1
如果上兩個裝置頂層驅動HOOK成功,則繼續做如下事情,否則恢復前面做的HOOK:
SaveDeviceObjectForDiskVolume
HookSCSI0
HookIoCreateFile
EAThookIofCallDriver
ObjectHijackAtapi
HookSSDTForSetInformationProcess
接下來,一個一個看

i.
對於HookIoFreeIRP這個函式,首先使用了指令cpuid來確定CPU的型別和支援.然後搜尋函式IoFreeIRP的跳轉
指令特徵0x25FF,根據不同型別的CPU來確定如何修改IoFreeIRP()函式的入口處,入口可能會有兩種,一種是
在入口處直接就是一個跳轉,另一種是在執行幾條指令後跳轉,我虛擬機器裡的就是這種情況,
nt!IoFreeIrp:
804ef3f4 8bff            mov     edi,edi
804ef3f6 55              push    ebp
804ef3f7 8bec            mov     ebp,esp
804ef3f9 5d              pop     ebp
804ef3fa ff250cd25480    jmp     dword ptr [nt!KeTickCount+0x146c (8054d20c)]
804ef400 cc              int     3
跳轉到8054d20c所指向的地址0x804ef406處
0x804ef406
真正的IoFreeIRP入口
804ef406 8bff            mov     edi,edi
804ef408 55              push    ebp
804ef409 8bec            mov     ebp,esp
804ef40b 53              push    ebx
804ef40c 56              push    esi
804ef40d 8b7508          mov     esi,dword ptr [ebp+8]
804ef410 66833e06        cmp     word ptr [esi],6
804ef414 57              push    edi
,至於如何修改入口這裡分很多情況,根據CPU型別不同做的操作不同,但是思路都差不多,這裡我就以具體一種情況分析,
這種情況是入口滿足上面的入口方式,CPU為奔騰以上並且支援SSE2和CPU擴充套件,修改入口處為eb 01 f9 90 8b ff 90 e9
也就是如下程式碼:
EB 01         JMP SHORT next
F9            STC
next:
90            NOP
8BFF          MOV EDI,EDI
90            NOP
E9 xxxxxxxx   JMP MyIoFreeIrp
再在MyIoFreeIrp刪除Irp Buffer中的Irp,然後呼叫原IoFreeIrp,這裡的BUFFER形如:
BUFFER
{
int IrpCount;
pIRP[MAX_Len];
}
buffer是動態分配的,長度為0x40004,在idb我改名為Globe_IrpBuffer,IrpCount站4個位元組,IRP部分可放0x1000個Irp指標.
這個buffer在disk,ftdisk驅動的裝置棧頂層驅動中,被hook的Write,InteralDeviceControl例程中和被hook的
IofCallDriver被填充,但是IRP不能重複.並且在disk驅動中被hook的Write,RoutineInternelDevice例程中和被hook的IoFreeIrp中刪除.

ii.IofCallDriver HOOK
也分兩種情況處理,如果開始處有跳轉,則將真的入口處到函式結束複製到一個分配的緩衝區中,如果開始出沒有跳轉,這
如果支援PAE,跳轉前有一個NOP.

nop
jmp MyIoCallDriver        
:
:
RealIoCallDriver:
push MyIoCallDriver
ret

不支援PAE

jmp MyIoCallDriver      
:
:
RealIoCallDriver:
push MyIoCallDriver
ret

然後在MyIoCallDriver中檢查HOOK是否被恢復,被回覆則重置.最後呼叫正真的RealIofCallDriver,這裡的RealRealIofCallDriver
指向的是那個分配的緩衝區.這裡有兩出執行點,一個jmp過去的,一個事push,ret過去的.
我虛擬機器上的原始入口處:
kd> u IofCallDriver
nt!IofCallDriver:
804ef120 ff2500d25480    jmp     dword ptr [nt!KeTickCount+0x1460 (8054d200)]
804ef126 cc              int     3
804ef127 cc              int     3

804ef0e8
nt!IoBuildPartialMdl+0xbc:
804ef0e8 fe4a23          dec     byte ptr [edx+23h]
804ef0eb 8a4223          mov     al,byte ptr [edx+23h]

iii.
獲取檔案\SystemRoot\system32\drivers\atapi.sys相關資訊
獲取檔案\SystemRoot\system32\drivers\classpnp.sys相關資訊
這裡有一個自定義的結構體,包含三個成員來標誌一個檔案,含有檔案index(這個不知道跟控制程式碼有什麼關係),檔案資訊類的資訊.
獲取這這個檔案的資訊以便在在HOOk的IoCreateFile判定當前開啟的是否是這個兩個檔案,是則設定檔案控制程式碼為NULL,返回.

iv
HOOK "Driver\Disk" 磁碟驅動,HOOK了Read,Write,DeviceControl,InternelDeviceRoutine.
在HOOK read中如果在當前棧上上層驅動存在classpnp.sys,atapi.sys則直接完成,其他放過.

v
HOOK classpnp,在這裡雖然說是HOOK classpnp,但是我們都知道classpnp是沒有裝置的,其實就是一個函式提供庫而已.
我們實際HOOK的DISK驅動的派遣例程.在此驅動的ClassInitialize()中搜尋disk驅動的例程,如果搜到DeviceControl,InternalDeviceControl,Write例程, 
則HOOK,新的例程跟disk驅動的是一樣的.

vi
Hook裝置
\Device\Harddisk0\DR0
\Device\HarddiskVolume
的裝置棧的頂層驅動的WriteRoutine,InteralDeviceControl,也就是DISK,FTDISK的過濾驅動如果機器狗過濾了這兩個裝置棧,
那就是HOOK機器狗的驅動例程.並且儲存:在New_WriteInteralDeviceControlRoutineTop中如果當前裝置的驅動為DISK驅動
裝置棧的頂層驅動或者是FTDISK驅動裝置棧的頂層驅動(既是在HOOk時儲存的DiskTopDriverObject,DiskVolumeTopDriverObject),
則放過其中Write,InteralDeviceControl請求,否則設定
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = 0xC0000013u;
IofCompleteRequest(Irp, 0);
return 0xC0000013u;

vii
如果VI步的兩次HOOK都成功的話,則繼續,否則恢復以上進行的HOOK.

1.儲存卷驅動ftdisk的其他磁碟卷裝置(不包含Device\HarddiskVolume1)到一個全域性陣列中,以供函式
CurrentDeviceIsInVolumeDevice(IRP)判定當前棧空間中是否存在此裝置.

2.HOOK scsi的DeviceControl,InteralDeviceControl
如果存在裝置\GLOBAL??\Scsi0: 或者\??\Scsi0:則進行HOOK DeviceControl,InteralDeviceControl.
在HOOK函式中根據IRP->IoStackLocatoion,找到完成例程,判定完成例程是是否在Classpnp.sys驅動模組中,
如果不在則完成IRP返回,同時儲存當前程式ID,執行緒ID,並設定一個全域性事件.

3,HookIoCreateFile
從開始搜尋函式IopCreateFile,搜尋特徵為ff7508e8.
8056cb69 56              push    esi
8056cb6a 56              push    esi
8056cb6b ff753c          push    dword ptr [ebp+3Ch]
8056cb6e ff7538          push    dword ptr [ebp+38h]
8056cb71 ff7534          push    dword ptr [ebp+34h]
8056cb74 ff7530          push    dword ptr [ebp+30h]
8056cb77 ff752c          push    dword ptr [ebp+2Ch]
8056cb7a ff7528          push    dword ptr [ebp+28h]
8056cb7d ff7524          push    dword ptr [ebp+24h]
8056cb80 ff7520          push    dword ptr [ebp+20h]
8056cb83 ff751c          push    dword ptr [ebp+1Ch]
8056cb86 ff7518          push    dword ptr [ebp+18h]
8056cb89 ff7514          push    dword ptr [ebp+14h]
8056cb8c ff7510          push    dword ptr [ebp+10h]
8056cb8f ff750c          push    dword ptr [ebp+0Ch]
8056cb92 ff7508          push    dword ptr [ebp+8]
8056cb95 e882f2ffff      call    nt!IopCreateFile (8056be1c)
8056cb9a 3bc6            cmp     eax,esi
然後將其修改為我們自己的函式地址NewIopCreateFile
在函式中如果開啟的檔案是classpnp.sys,atapi.sys,則設定返回控制程式碼為NULL.

4.EAThookIofCallDriver
這裡是從核心ntoskrnl.exe的Export table中獲取IoCallDriver,IofCallDriver的RVA,並把已經安排好的一個21位元組的
BufferBuffer_21_IofCallDriver相對於ntoskrnl.exe基址的RVA替換掉前面獲取的RVA,而在buffer裡執行16條nop指令後跳轉到
MyIofCallDriver,MyIoCallDriver中

EAT
|
|
|----RVA of function
|--->Buffer_21_IofCallDriver---+
|----RVA of function           |
|                              |
|                              |
              +----------------+
              |
              V
Buffer_21__IofCallDriver:
90909090
90909090
90909090
90909090
JMP MyIofCallDriver

EAT
| .
| .
|----RVA of function
|--->Buffer_21_IofCallDriver---+
|----RVA of function           |
| .                             |
| .                             |
              +----------------+
              |
              V
Buffer_21__IofCallDriver:
90909090
90909090
90909090
90909090
JMP MyIofCallDriver

其中MyIoCallDriver直接呼叫MyIofCallDriver.
MyIofCallDriver同時也就是前面HOOK IoCallDriver的新函式.在這裡主要作用在前面提到的IRP BUFFER中儲存當前IRP.

5.ObjectHijackAtapi
這裡使用了MJ在tophet中提到的物件劫持技術,物件劫持技術也不多說了,去看看tophet文件,物件劫持技術原始碼貌似網上有.
不過這裡僅僅用於獲取發出IRP請求的執行緒的程式ID和執行緒ID以供返回給使用者態請求.

6.HookSSDTForSetInformationProcess
 如果資訊類為16(ProcessUserModeIOPL),則返回初始化失敗0xC0000022,以防止使用者模式的程式透過ProcessUserModeIOPL
給當前程式賦予IO 操作許可權直接向磁碟控制器IO埠傳送讀寫指令.
上傳的附件:

相關文章