如何反转 DeviceIoControl?

逆向工程 恶意软件 内核模式
2021-06-25 20:27:51

我已经开始逆转这块恶意软件。在某个时候,它会创建一个服务并启动它,然后立即调用该函数DeviceIoControl,恶意软件在 ollydbg 下从“暂停”变为“运行”。我搜索了一下,我知道这个函数用于与它刚刚创建的服务进行通信。

但我究竟该如何逆转呢?我怎么知道它的作用?我怎样才能继续踩到 ollydbg 下?或者我是否必须转移到windbg 或其他一些内核模式调试器?

2个回答

你不能从 Ollydbg 进入内核模式。您需要一个内核调试器windbg,如ollydbg用户模式调试器。

既然您提出了这个问题,我假设您既没有内核调试连接,也没有发送控制代码以进行分析的驱动程序,正如 Jonathon 所回答的那样。

从这里开始假设和强调使用适当的安全措施来处理恶意软件。

我假设恶意软件已经在运行,因为您的查询表明您正在使用DeviceIoControl.

我将使用 ollydbg 2.01。

将 ollydbg 附加到未知进程

Ctrl+G并开始输入ntdll.ntDeviceIo,列表框将显示 ntdll.ntDeviceIoControl现在选择它和follow label

F2设置断点,并F9运行附加的进程。

INT3 breakpoints, item 0
  Address = 7C90D27E NtDeviceIoControlFile
  Module = ntdll
  Status = Active
  Disassembly = MOV     EAX, 42
  Comment = ntdll.NtDeviceIoControlFile(guessed Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8,Arg9,Arg10)

Ollydbg 应该在发送控制代码时中断,堆栈应该如下所示:

CPU Stack
    Address   Value      ASCII Comments
    0013EF50  [7C801675  u€|   ; /RETURN from ntdll.NtDeviceIoControlFile to kernel32.DeviceIoControl+4C
    0013EF54  /00000090        ; |Arg1 = 90
    0013EF58  |00000000        ; |Arg2 = 0
    0013EF5C  |00000000        ; |Arg3 = 0
    0013EF60  |00000000        ; |Arg4 = 0
    0013EF64  |0013EF88  ˆï    ; |Arg5 = 13EF88
    0013EF68  |83050024  $ ƒ   ; |Arg6 = 83050024
    0013EF6C  |00000000        ; |Arg7 = 0
    0013EF70  |00000000        ; |Arg8 = 0
    0013EF74  |0013EFEC  ìï    ; |Arg9 = 13EFEC
    0013EF78  |00000004        ; \Arg10 = 4

从 的规范来看DeviceIoControlCode,第一个参数指向,handle第六个参数是control code

安装windbg 可以让事情变得更容易,但我们现在不会使用windbg,因为它有一个陡峭的学习曲线。

打开 ollydbg 句柄窗口并找到句柄指向什么(0x90在上面的粘贴中) - 它指向一个设备:

Handles, item 9
  Handle = 00000090
  Type = File (dev)   <-------- it is a _FILE_OBJECT
  Refs =    2.
  Access = 0012019F (FILE_GENERIC_READ|FILE_GENERIC_WRITE)
  Tag =
  Info =
  Translated name = \Device\Dbgv

现在运行进程资源管理器SysInternals选择未知进程,然后按Ctrl+H使下方窗格显示句柄。

选择手柄90,右键单击并选择propertiesProcess Explorer将显示Ollydbg 记录设备对象的地址这个设备对象是一个文件对象

在此处输入图片说明

ollydbg 2.01可以在上面显示内存0x7fffffff(内核模式内存)我们将使用该功能来查找与此设备关联的驱动程序

阅读FILE_OBJECT与您的操作系统相关结构。在 Windows XP 中,您将在文件对象的 +4 处找到设备对象的地址。

选择转储窗口按Ctrl+G并输入地址进程资源管理器显示 viz

863c87f0 and follow expression 

CPU Dump
Address   Hex dump                                         ASCII
863C87F0  05 00 70 00|A8 D3 51 86|00 00 00 00|00 00 00 00|  p ¨ÓQ†

所以8651d3a8在上面的粘贴中指向这个文件对象的设备对象。

阅读DEVICE_OBJECT结构。在 Windows XP 中,驱动程序对象的地址在设备对象 + 8 处。跟在那里之后是Ctrl+ G->follow expression如前所述

CPU Dump
Address   Hex dump                                         ASCII
8651D3A8  03 00 B8 00|01 00 00 00|48 2C F1 86|00 00 00 00|  ¸    H,ñ†

通知86f12c48是驱动程序对象。阅读DRIVER_OBJECT结构。

+0xc is Driver Start+0x10 is Driver Size--> Ctrl+G跟随:

CPU Dump
Address   Hex dump                                         ASCII
86F12C48  04 00 A8 00|A8 D3 51 86|12 00 00 00|00 30 2A A9|  ¨ ¨ÓQ†    0*©
86F12C58  00 3D 00 00|48 9C D8 86|F0 2C F1 86|18 00 18 00|  =  HœØ†ð,ñ† 

a92a3000DriverStart,驱动程序的大小是3d00

Ctrl+G关注:

CPU Dump
Address   Hex dump                                         ASCII
A92A3000  4D 5A 90 00|03 00 00 00|04 00 00 00|FF FF 00 00| MZ       ÿÿ
A92A3010  B8 00 00 00|00 00 00 00|40 00 00 00|00 00 00 00| ¸       @

名气IMAGE_DOS_HEADER是有目共睹的。

选择a92a3000to a92a6d00, Right-Click-> Edit-> binary copy 并将字节粘贴到任何十六进制编辑器(例如 HxD)中的新文件中,并将其另存为malwaredriver.sys:)

foo:\>dir malwaredriver.sys

26/03/2014  14:04            15,616 malwaredriver.sys

\>set /a 0x3d00
15616
\>file malwaredriver.sys

malwaredriver.sys; PE32 executable for MS Windows (native) Intel 80386 32-bit

:\>

您现在可以静态分析驱动程序。

DRIVER_OBJECT仔细阅读结构,发现你可以很容易地知道IRP_MJ_DEVICE_IO_CONTROLollydbg 中处理程序的处理程序。它将+0x70来自 XP 中的 Driverobject。对于这个特定的驱动程序,IRP_MJ_DEVICE_IO_CONTROL_HANDLER

CPU Dump
Address   Value      ASCII Comments
86F12CB8   A92A4168  hA*©

你可以在ollydbg中跟踪和拆解这段内存。选择 cpu dump 按 ctrl+g 反汇编未知驱动程序

CPU Disasm
Address   Command                             Comments
A92A4168  MOV     EDI, EDI                    ; IRP_MJ_DEVICE_IO_CONTROL_HANDLER(pdevob,pirp)
A92A416A  PUSH    EBP
A92A416B  MOV     EBP, ESP
A92A416D  SUB     ESP, 24
A92A4170  PUSH    EBX
A92A4171  PUSH    ESI
A92A4172  MOV     ESI, DWORD PTR SS:[EBP+0C]  ; irp
A92A4175  MOV     EAX, DWORD PTR DS:[ESI+60]  ; nt!_irp -y Tail.overlay.cur->maj
A92A4178  MOV     EDX, DWORD PTR DS:[ESI+0C]  ; nt!_irp Tail.overlay.cur->par.DeviceIo.ty
A92A417B  XOR     EBX, EBX
A92A417D  PUSH    EDI
A92A417E  LEA     EDI, [ESI+18]
A92A4181  MOV     DWORD PTR DS:[EDI], EBX
A92A4183  MOV     DWORD PTR DS:[ESI+1C], EBX
A92A4186  MOVZX   ECX, BYTE PTR DS:[EAX]
A92A4189  SUB     ECX, EBX
A92A418B  JE      SHORT A92A41F7              ; case 0 create
A92A418D  DEC     ECX
A92A418E  DEC     ECX
A92A418F  JE      SHORT A92A41CA              ; case 2 close
A92A4191  SUB     ECX, 0C
A92A4194  JNE     A92A42BB                    ; unhandled
A92A419A  MOV     ECX, DWORD PTR DS:[EAX+0C]  ; ioctl
A92A419D  MOV     EBX, ECX
A92A419F  AND     EBX, 00000003
A92A41A2  CMP     BL, 3
A92A41A5  JNE     SHORT A92A41AC              ; buff align
A92A41A7  MOV     EBX, DWORD PTR DS:[ESI+3C]
A92A41AA  JMP     SHORT A92A41AE
A92A41AC  MOV     EBX, EDX                    ; inbuff
A92A41AE  PUSH    DWORD PTR SS:[EBP+8]        ; devobj
A92A41B1  PUSH    EDI
A92A41B2  PUSH    ECX
A92A41B3  PUSH    DWORD PTR DS:[EAX+4]        ; bufflen
A92A41B6  PUSH    EBX
A92A41B7  PUSH    DWORD PTR DS:[EAX+8]        ; ioctlcode
A92A41BA  PUSH    EDX
A92A41BB  PUSH    1
A92A41BD  PUSH    DWORD PTR DS:[EAX+18]       ; fobj
A92A41C0  CALL    A92A3EEC                    ; actual_handler

这里处理ioctl代码:

CPU Disasm
Address   Command                                  Comments
A92A3EF8  MOV     ESI, DWORD PTR SS:[EBP+24]
A92A3EFB  XOR     EBX, EBX
A92A3EFD  MOV     DWORD PTR DS:[ESI], EBX
A92A3EFF  MOV     DWORD PTR DS:[ESI+4], EBX
A92A3F02  MOV     ECX, DWORD PTR SS:[EBP+20]
A92A3F05  MOV     EAX, 83050020
A92A3F0A  CMP     ECX, EAX
A92A3F0C  JA      A92A40F1  this code will take this path as 24 > 20

============

CPU Disasm
Address     Hex dump                  Command                Comments
A92A40F1    81F9 24000583             CMP     ECX, 83050024
A92A40F7    74 3F                     JE      SHORT A92A4138 path taken

控制代码完成这项工作并在某些条件下发送一些 320

[code]
CPU Disasm
Address   Command                             Comments
A92A4138  PUSH    4
A92A413A  POP     EAX
A92A413B  CMP     DWORD PTR SS:[EBP+1C], EAX
A92A413E  JB      SHORT A92A4152
A92A4140  MOV     ECX, DWORD PTR SS:[EBP+18]
A92A4143  CMP     ECX, EBX
A92A4145  JE      SHORT A92A4152
A92A4147  MOV     DWORD PTR DS:[ECX], 320      <------------
A92A414D  MOV     DWORD PTR DS:[ESI+4], EAX
A92A4150  JMP     SHORT A92A4158
A92A4152  MOV     DWORD PTR DS:[ESI], C000000D
A92A4158  MOV     AL, 1
A92A415A  CALL    A92A46D5
A92A415F  RETN    24

这里分析的驱动是来自SysInternals的dbgview(dbgv.sys)加载的驱动。此控制代码检查内存中加载的不兼容版本的驱动程序。可以通过特定的驱动程序和特定版本来练习:

(4.75) build time Thu Aug 07 04:51:27 2008

编辑

安装windbg后,您只需使用下面发布的几个命令即可完成相同的操作

C:\>tlist | grep -i dbgview

3724 Dbgview.exe       DebugView on \\ (local)

C:\>kd -kl -c ".foreach /pS 1 /ps 4 (place  { .shell -ci \"!handle 0 3 0n3724 Fi
le \" grep File } ) {dt nt!_FILE_OBJECT -y Dev->Dri->DriverS place};q" | grep -i
 +0x00c
 +0x00c DriverStart       : 0xf7451000 Void  
 +0x00c DriverStart       : 0xf73c5000 Void   
 +0x00c DriverStart       : 0xa87f9000 Void   
 +0x00c DriverStart       : 0xf7451000 Void   
 +0x00c DriverStart       : 0xf7451000 Void   

C:\>kd -kl -c ".reload ; lm a (0xf7451000); lm a (0xf73c5000) ; lm a (0xa87f9000);q" | grep -i defer

 f7451000 f746f880   ftdisk     (deferred)    
 f73c5000 f73db880   KSecDD     (deferred)    
 a87f9000 a87fcd00   Dbgv       (deferred)    


C:\>kd -kl -c ".writemem  malware.sys a87f9000 a87fcfff ;q" | grep -A 1 lkd

 lkd> kd: Reading initial command '.writemem  malware.sys a87f9000 a87fcfff ;q'     
 Writing 4000 bytes........    

C:\>file malware.sys  

 malware.sys; PE32 executable for MS Windows (native) Intel 80386 32-bit   

对上述命令的解释如下

kd -kl is local kernel debugging

!handle command has the capability to search for kernel handle  
when you input a Pid 

The explanation for arguments for this commands are 

 0 = all handles 
 3 = default flag provides basic+object information
 0n3724 = pid (can be from task manager
 File = type of Object to search for 


!dt can display memebers of nested structures with ease 
-y with dt takes partial inputs (wild card entries ) 
 and displays  all matching structure members 

and the output is manipulatable with any text / stream handling 
software like grep , sed . awk . find etc

更具体地说,听起来您的可执行文件正在加载设备驱动程序。用户空间可执行文件通常通过IOCTL(或 I/O 控件)与驱动程序通信

DeviceIoControl就是这样:向驱动程序发送一个 IOCTL。请注意此函数的第二个参数:DWORD dwIoControlCode. 这是识别程序请求驱动程序执行哪个 IOCTL 的代码。它只是一个 32 位的 DWORD 值。

现在,在驾驶员方面,您需要了解一些事项。(准备好游过几个结构!)

首次加载驱动程序时,DriverEntry将调用函数。这会传递一个指向驱动程序的DRIVER_OBJECT. 在这个结构中有一个名为 的数组MajorFunction,它是一组函数指针,当用户空间试图对驱动程序做一些事情(例如打开、关闭或发送 IOCTL)时,内核将调用这些函数指针。这些功能由IRP 主要功能代码标识

在 ioctls 发送的情况下DeviceIoControl,该IRP_MJ_DEVICE_CONTROL函数被调用。任何ioctl都会调用此函数现在要确定dwIoControlCode传递给 的是DeviceIoControl,ioctl 处理函数将查看Parameters.DeviceIoControl.IoControlCode

根据这个值,通常有一个 switch 语句,它根据控制代码选择不同的行为。


因此,首先,您要将.sys驱动程序加载到 IDA 中。IDA 标记为的函数DriverEntry实际上是由 DDK 生成的包装器。真正DriverEntry的通常jmp是在这个存根的末尾。

在 real 中DriverEntry,您需要找到MajorFunction填充条目的位置。它可能看起来像这样:

在此处输入图片说明