Home 电脑技术 编程技术 软件保护壳技术专题 - 加密引入表
软件保护壳技术专题 - 加密引入表 E-mail
作者:洋葱圈   
周一, 23 6月 2008 08:11
导入表的加密应该是壳对应用程序加密时最重要的部分了。首先我们先了解一下怎样寻找引入表,并列出其中的导入的函数与DLL命。其次,进行讲解如何加密引入表。最后,讲解在我们自定义的节表中恢复引入表并重定位函数地址。
1.寻找引入表
在IMAGE_NT_HEADERS结构中的OptionalHeader字段中有一组数据目录,数据目录数组的第二个元素就是引入表的目录索引。
数据目录的结构是这样的
IMAGE_DATA_DIRECTORY STRUCT 
  VirtualAddress dd ? 
  isize dd ? 
IMAGE_DATA_DIRECTORY ENDS 
VirtualAddress 是此表的RVA,也就是相对于模块加载的偏移量,此地址指向一个由IMAGE_IMPORT_DESCRIPTOR结构组成的数组。
isize 是此表的大小
我们利用以下算法寻找引入表
1.从 DOS header 找到 PE header 
2.从 数据目录中 读取 data directory 的地址。 第二个索引就为引入表的地址。
3.IMAGE_DATA_DIRECTORY的虚拟地址偏移转化为文件偏移
4.文件偏移加上文件映射基址就为引入表的地址
寻找到引入表后,接下来的工作就是展开引入表,此时的指针指向一个IMAGE_IMPORT_DESCRIPTOR结构的数组,我们可以认为一个 IMAGE_IMPORT_DESCRIPTOR结构就是一个DLL,如果你的程序引用了5个DLL那么你的程序用就有5个 IMAGE_IMPORT_DESCRIPTOR结构,并且这个数组以一个全0的IMPORT_IMPORT_DESCRIPTOR为结束。为了让读者好 理解在以下称IMAGE_IMPORT_DESCRIPTOR为DLL结构。
对于加密导入表最重要的有三个属性,Name1,OriginalFirstThunk,FirstThunk。
Name1也是一个偏移量,指向这个DLL的DLL字符串的内存偏移。
OriginalFirstThunk与FirstThunk两个字段也同属于指针类型的。并且在文件中都指向一个位置。当加载到内存时, OriginalFirstThunk还指向原来指向的地方,FirstThunk指向一个API函数的地址的数据。(由PE加载器帮助定位并修改的)
在文件中,OriginalFirstThunk指向一组地址的偏移,这个地址偏移被称为IMAGE_THUNK_DATA
这个偏移值指向一组IMAGE_IMPORT_BY_NAME结构的数组。这个结构读者可以认为是这个DLL文件中的API。有几个API代表有几个这样的结构。
IMAGE_IMPORT_BY_NAME STRUCT 
  Hint dw ? 
  Name1 db ? 
IMAGE_IMPORT_BY_NAME ENDS
Hint:代表此API是DLL中的第几个函数
Name1:为此API的名字,最后以NULL结尾。我们加密API的名字就是加密Name1的字段。
另一个FirstThunk如同复制一样也一模一样的指向了与OrigFirstThunk一样的地方。但是当文件加载到内存中后,FirstThunk会指向函数的地址。这个转换由PE加载器加载。
列出引入表代码如下
代码:
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 

  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 
  24 
  25 
  26 
  27 
  28 
  29 

  30 
  31 
  32 
  33 
  34 
  35 
  36 
  37 

  38 
  39 
  40 
  41 
  42 
  43 
  44 
  45 
  46 
  47 
  48 
  49 
  50 
  51 
  52 
  53 
  54 
  55 
  56 
  57 
  58 
  59 
  60 
  61 
  62 
  63 
  64 
  65 
  66 


  67 


  68 
  69 
  70 
  71 
  72 

  73 
  74 
  75 
  76 
  77 
  78 

  79 
  80 

  81 
  82 
  83 

  84 
  85 

  86 

  87 
  88 
  89 
  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 

  98 
  99 
 100 

 101 
 102 
 103 
 104 
 105 

 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125 
 126 
 127 
 128 
 129 
 130 
 131 
 132 
 133 
 134 
 135 
 136 
 137 
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 
 146 
 147 
 148 
 149 
 150 

 151 
 152 
ListIID proc pFilename : LPSTR
    ;; map file to memory
    LOCAL hFile : HANDLE
    LOCAL hMap : HANDLE
    LOCAL pMem : LPVOID
    LOCAL dwNTHeaderAddr : DWORD
    LOCAL szTmpBuf[MAX_PATH] : BYTE
    
    ;; open file
    invoke CreateFile, pFilename,\
                      GENERIC_WRITE + GENERIC_READ,\
                      FILE_SHARE_WRITE + 
                        FILE_SHARE_READ,\
                      NULL,\
                      OPEN_EXISTING,\
                      FILE_ATTRIBUTE_NORMAL,\
                      0
    .IF eax == INVALID_HANDLE_VALUE
        jmp OpenFileFailed                
    .ENDIF
    mov hFile, eax 
    invoke GetFileSize, hFile, NULL
    .IF eax == 0
        invoke CloseHandle, hFile  
        jmp GetFileSizeFailed
    .ENDIF  
    
    ;; create memory map
    xor ebx, ebx     
    invoke CreateFileMapping, hFile, ebx, 
      PAGE_READWRITE, ebx, eax, ebx
    .IF eax == 0
        invoke CloseHandle, hFile
        jmp CreateMapFailed                
    .ENDIF
    mov hMap, eax
    ;; map file to memory
    invoke MapViewOfFile, hMap,
                       FILE_MAP_WRITE+FILE_MAP_READ+FIL
                         E_MAP_COPY, 
                       ebx, ebx, ebx
    .IF eax == 0
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp MapFileFailed
    .ENDIF
    mov pMem, eax                               
    ;; check it's PE file or not ?
    xchg eax, esi
    assume esi : ptr IMAGE_DOS_HEADER
    .IF [esi].e_magic != 'ZM'
        invoke UnmapViewOfFile, pMem
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp InvalidPE        
    .ENDIF       
    add esi, [esi].e_lfanew
    assume esi : ptr IMAGE_NT_HEADERS   
    .IF word ptr [esi].Signature != 'EP'
        invoke UnmapViewOfFile, pMem
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp InvalidPE        
    .ENDIF
    mov dwNTHeaderAddr, esi
    
    ;; 寻找引入表
    assume esi : ptr IMAGE_NT_HEADERS
    mov eax, dword ptr [esi].OptionalHeader.DataDirecto
      ry[IMAGE_DIRECTORY_ENTRY_IMPORT * sizeof 
      IMAGE_DATA_DIRECTORY].VirtualAddress
    ;; å°åå­å移转æ¢ä¸ºæä»¶åç§»,åå ä¸æ
      ä»¶å¤´çåå­æ å°å°±ç­äºå¼å¥è¡¨å¨æä»¶
      的地址了
    invoke RVA2Offset, pMem, eax
    xchg ebx, eax
    add ebx, pMem
    assume ebx : ptr IMAGE_IMPORT_DESCRIPTOR
    ;; è¿éç´å°ä¸ä¸ªå¨0çIMAGE_IMPORT_DESCRIPTOR
      结构为结束
    ;; 这里判断Name1是否为NULL
    ListIIDLoop:
        mov eax, dword ptr [ebx].Name1
        test eax, eax
        jz EndListIIDLoop
        ;; æ­¤æ¶eaxæåä¸ä¸ªDLLåå­çRVA,æä»¬å
          RVA转换为文件的偏移
        invoke RVA2Offset, pMem, eax
        ;; æä»¶çåç§»å ä¸æä»¶çèµ·å§æéå°
          为文件中的地址
        add eax, pMem
        ;; 打印DLL名
        invoke PrintLine, offset g_szOutFormat, offset 
          g_szOutLine        
        invoke PrintLine, offset g_szOutFormat, eax
        invoke PrintLine, offset g_szOutFormat, offset 
          g_szOutLine
        ;; æ£æ¥OriginalFirstThunkæ¯å¦ä¸º0ã妿ä
          0则使用FirstThunk        
        mov edx, dword ptr [ebx].OriginalFirstThunk
        test edx, edx
        jnz UseOrignalFirstThunk
        mov edx, dword ptr [ebx].FirstThunk
    UseOrignalFirstThunk:        
        ;; 转换RVA转换文件偏移
        invoke RVA2Offset, pMem, edx
        add eax, pMem
        mov edx, eax
    DisplayApiName:
        ;; æ¥çedxçæé«ä½æ¯å¦æ¯1ç¡®å®æ¯å¦ä
          序数引出
        test dword ptr [edx], IMAGE_ORDINAL_FLAG32
        jnz DisPlayOrd
        ;; è¿éedxæåIMAGE_IMPORT_BY_NAMEç»æç
          RVA,继续将它转换
        mov eax, dword ptr [edx]
        invoke RVA2Offset, pMem, eax
        add eax, pMem
        assume eax : ptr IMAGE_IMPORT_BY_NAME
        ;; æå°APIå­ç¬¦ä¸²,å°Name1çå°å设置ç»
          eax寄存器
        lea eax, [eax].Name1
        invoke PrintLine, offset g_szOutFormat, eax
        jmp NextAPI
        DisPlayOrd:
        ;; 取出序数,低2个字节为序数
        mov eax, dword ptr [edx]
        and eax, 0FFFFh        
        invoke PrintLine, offset g_szOutOrdFormat, eax
    NextAPI:
        ;; 取下一个IMAGE_THUNK_DATA的值
        add edx, 04h
        ;; 直到edx指向一个0
        mov eax, dword ptr [edx]
        test eax, eax
        jnz DisplayApiName
        ;; 指向下一个DLL
        add ebx, sizeof IMAGE_IMPORT_DESCRIPTOR
        jmp ListIIDLoop
    EndListIIDLoop:   
LogicShellExit:
    ;; close handle & write it
    invoke UnmapViewOfFile, pMem
    invoke CloseHandle, hMap
    invoke CloseHandle, hFile
    assume ebx : nothing
    assume esi : nothing
    ret
;; ----- Show error message ----- 
OpenFileFailed:
     lea eax, g_szOpenFileFailed
     jmp ShowErr
GetFileSizeFailed:
     lea eax, g_szGetFileSizeFailed
     jmp ShowErr    
CreateMapFailed:
     lea eax, g_szCreateMapFailed
     jmp ShowErr
MapFileFailed:
     lea eax, g_szMapFileFailed
     jmp ShowErr        
InvalidPE:          
     lea eax, g_szInvalidPE
     jmp ShowErr   
ShowErr:
     invoke MessageBox, NULL, eax, offset g_szErr, 
       MB_ICONERROR
     jmp LogicShellExit
ListIID endp



列出引入表不知道我讲的是否清楚,总之当从PE头结构中获取到引入表地址将它的RVA转换为FVA再加上文件映射基址就可以按照微软定义的引入表结构进行 加密与销毁。接下来我们要做的就是将引入表中每个DLL名字与API的名字都进行加密,后记录每个IMAGE_IMPORT_DESCRIPTOR中的 FirstThunk值。并记录到我们自己的定义的结构中。RVA2Offset函数是转换RVA到FVA。此函数在附件代码中定义。

前面的只是给加密引入表做个铺垫。加密引入表和列出引入表算法都一样。只不过将显示函数加密而已。这里直接将代码列出。加密字符串的算法也很简单只是异或 上99h而已。其次和列出不一样的地方是,加密引入表记录了引入表每个IMAGE_IMPORT_DESCRIPTOR的Name1, OrigFirstThunk,FirstThunk三个字段。然后在解密段中按此三个字段在解密引入表。并重定位API的地址表。
这里是我们自己的引入表结构。这个结构安置在解密体内
代码:
 1 
 2 
 3 
 4 
 5 
IID_PRIVATE_DATA struct
    Name1                   dd  0
    OriginalFirstThunk      dd  0
    FirstThunk              dd  0
IID_PRIVATE_DATA ends



下面列出其加密引入表的代码
代码:
   1 

   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 
  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 


  24 
  25 
  26 


  27 
  28 
  29 
  30 
  31 
  32 

  33 
  34 
  35 
  36 
  37 
  38 
  39 
  40 
  41 
  42 
  43 
  44 
  45 
  46 

  47 
  48 
  49 
  50 
  51 
  52 
  53 
  54 
  55 
  56 
  57 
  58 

  59 
  60 
  61 

  62 
  63 
  64 
  65 
  66 
  67 
  68 
  69 
  70 
  71 
  72 
  73 
  74 
  75 
  76 
  77 
  78 
  79 
  80 
  81 
  82 
  83 
  84 
  85 
  86 
  87 
  88 
  89 

  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 
  98 
  99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 

 115 
 116 

 117 

 118 
 119 
 120 
 121 


 122 

 123 
 124 
 125 
 126 
 127 
 128 
 129 
 130 
 131 
 132 
 133 
 134 
 135 
 136 
 137 
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 

 146 
 147 
 148 
 149 
 150 
 151 
 152 
 153 
 154 
 155 
 156 
 157 
 158 
 159 
 160 
 161 
 162 
 163 
 164 
 165 
 166 
 167 
 168 
 169 
 170 
 171 
 172 
 173 
 174 
 175 
 176 
 177 
 178 
 179 
 180 
 181 
 182 
 183 

 184 
 185 
 186 
 187 
 188 
 189 
 190 
 191 
 192 
 193 
 194 
 195 
 196 
 197 
 198 
 199 
 200 
 201 
 202 
 203 
 204 

 205 
 206 
 207 
 208 
 209 
 210 
 211 
 212 
 213 
 214 
 215 
 216 

 217 
 218 
 219 
 220 
 221 

 222 
 223 
 224 
 225 
 226 
 227 
 228 
 229 
 230 
 231 
 232 
 233 
 234 
 235 
 236 
 237 

 238 
 239 
 240 
 241 
 242 
 243 
 244 


 245 
 246 


 247 
 248 
 249 
 250 
 251 
 252 
 253 
 254 
 255 
EnCryptIID proc uses ebx ecx edx esi edi, pMem : 
  LPVOID, pCurrentIID : LPVOID
;; encrypt image import directory
    LOCAL pIID[IMAGE_IMPORT_TABLE_SIZE] : BYTE
    LOCAL pImportFVA : LPVOID
    LOCAL pImportRVA : DWORD
    LOCAL dwLoadLibraryThunkData : DWORD
    LOCAL dwGetProcAddressThunkData : DWORD
    
    ;; 将IID_PRIVATE_DATA结构清0
    mov edi, pCurrentIID
    mov eax, sizeof IID_PRVATE_DATA
    mov ecx, MAX_IID_NUM
    imul ecx
    xchg eax, ecx
    xor eax, eax
    cld
    rep stosb

    ;; 定位引入表的位置
    mov esi, pMem
    add esi, dword ptr [esi+03ch]
    assume esi : ptr IMAGE_NT_HEADERS
    mov ecx, dword ptr [esi].OptionalHeader.DataDirecto
      ry[IMAGE_DIRECTORY_ENTRY_IMPORT * sizeof 
      IMAGE_DATA_DIRECTORY].isize    
    test ecx, ecx
    jz ExitEnCryptIID
    mov esi, dword ptr [esi].OptionalHeader.DataDirecto
      ry[IMAGE_DIRECTORY_ENTRY_IMPORT * sizeof 
      IMAGE_DATA_DIRECTORY].VirtualAddress
    test esi, esi
    jz ExitEnCryptIID
    ;; change RVA to offset
    invoke RVA2Offset, pMem, esi
    add eax, pMem
    xchg eax, edi           ; esi = Import Symbol Table
      on file
    ;; 清除原始的引入表
    assume edi : ptr IMAGE_IMPORT_DESCRIPTOR
    mov esi, pCurrentIID
    assume esi : ptr IID_PRIVATE_DATA
    xor ebx, ebx            ; ebx = max of IID
ClearOrigIIDLoop:
    mov eax, dword ptr [edi].Name1
    test eax, eax           ; eax = dll name string RVA
    jz ExitClearOrigIIDLoop
    inc ebx
    test ebx, MAX_IID_NUM
    jnz ExitClearOrigIIDLoop
    
    ;; å¨å­åå§IIDçä¿¡æ¯,ä¿å­å°æä»¬èªå·±ç
      IID结构,供解密用
    push dword ptr [edi].Name1
    pop dword ptr [esi].Name1
    push dword ptr [edi].OriginalFirstThunk
    pop dword ptr [esi].OriginalFirstThunk
    push dword ptr [edi].FirstThunk
    pop dword ptr [esi].FirstThunk
    
    ;; change Name1 RVA to offset
    invoke RVA2Offset, pMem, eax
    add eax, pMem

    ;; 加密DLL库文件的文件名, è¿ä¸ªå½æ°å°å
      符串的每个字节异或上99h
    invoke EnCryptString, eax
    
    ;; å å¯å¼å¥è¡¨åæ¾ç¤ºå¼å¥è¡¨çæåµç¸å
      ,把显示换成加密即可
    mov eax, dword ptr [edi].OriginalFirstThunk
    test eax, eax
    jnz UseOriginalFirstThunk
    mov eax, dword ptr [edi].FirstThunk
UseOriginalFirstThunk:
    ;; change Api Name string RVA to offset
    invoke RVA2Offset, pMem, eax
    add eax, pMem
    xchg edx, eax       ; edx = Api offset in file
    push esi
EnCryptApiNameLoop:
    mov esi, dword ptr [edx]
    test esi, esi
    jz EndEnCryptApiNameLoop    
    ;; judege IMAGE_ORDINAL_FLAG32 flags
    test esi, IMAGE_ORDINAL_FLAG32
    jnz SkipEncrypt
    invoke RVA2Offset, pMem, esi
    test eax, eax
    jz SkipEncrypt 
    add eax, pMem
    add eax, 02h        ; skip HINT
    invoke EnCryptString, eax
SkipEncrypt:
    add edx, 04h   
    jmp EnCryptApiNameLoop
EndEnCryptApiNameLoop:
    pop esi             ; esi = the point of current 
      own IID
    ;; 这里是毁坏原始IID
    push 0
    pop dword ptr [edi].Name1
    push 0
    pop dword ptr [edi].OriginalFirstThunk
    push 0
    pop dword ptr [edi].FirstThunk
    push 0
    pop dword ptr [edi].TimeDateStamp
    push 0
    pop dword ptr [edi].ForwarderChain
    
    ;; mov to next IID
    add edi, sizeof IMAGE_IMPORT_DESCRIPTOR
    add esi, sizeof IID_PRIVATE_DATA
    jmp ClearOrigIIDLoop
ExitClearOrigIIDLoop:

    ;; clear the new IID
    lea edi, pIID
    mov ecx, IMAGE_IMPORT_TABLE_SIZE
    cld
    xor al, al
    rep stosb
    sub edi, IMAGE_IMPORT_TABLE_SIZE            ; set 
      edi back to start point
    
    ;; å¢å ä¸ä¸ªæä»¬èªå·±çIIDè,éåä¼å°æ
      建的IID写入到此节中
    invoke AddSection, pMem, NULL, 
      IMAGE_IMPORT_TABLE_SIZE
    mov pImportFVA, eax    
    assume eax : ptr IMAGE_SECTION_HEADER
    ;; 这里创建一个新的IID
    ;; æä»¬çIID表åªå¯¼å¥ä¸ä¸ªDLLé£å°±æ¯åºæ¬
      çkernel32.dllå¼å¥ç彿°ä¹åªæä¸¤ä¸ªå½æ
      
    ;; LoadLibraryA,GetProcAddressä¸¤ä¸ªå½æ°ãå¨è§£
      密节中利用这两个最基本的函数获取
    ;; 其他的API地址与DLL句柄
    ;; 结构如下
    ;; 构建新的IID按照以下结构填写即可
    ;; make new IID
    ;; ----- Image Import Descriptor -----
    ;; IMAGE_IMPORT_DESCRIPTOR
    ;; IMAGE_IMPORT_DESCRIPTOR(0)
    ;; IMAGE_THUNK_DATA
    ;; IMAGE_THUNK_DATA
    ;; IMAGE_THUNK_DATA(0)
    ;; kernel32.dll,0
    ;; IMAGE_IMPORT_BY_NAME(LoadLibraryA)
    ;; IMAGE_IMPORT_BY_NAME(GetProcAddress)
    lea edx, pIID
    assume edx : ptr IMAGE_IMPORT_DESCRIPTOR
    mov ecx, sizeof IMAGE_IMPORT_DESCRIPTOR
    add ecx, sizeof IMAGE_IMPORT_DESCRIPTOR
    add ecx, sizeof IMAGE_THUNK_DATA
    add ecx, sizeof IMAGE_THUNK_DATA
    add ecx, sizeof IMAGE_THUNK_DATA
    mov eax, dword ptr [eax].VirtualAddress
    mov pImportRVA, eax
    add eax, ecx                                ; ecx =
      kernel32.dll string offset
    mov dword ptr [edx].Name1, eax
    
    ;; 拷贝kernel32.dll 字符串到 文件
    mov esi, offset g_szKernelDll
    add edi, ecx
CopyKernel32StrLoop:
    mov al, byte ptr [esi]
    test al, al
    jz EndCopyKernel32StrLoop
    mov byte ptr [edi], al
    inc esi
    inc edi
    jmp CopyKernel32StrLoop
EndCopyKernel32StrLoop:
    mov byte ptr [edi], al
    inc edi
    
    ;; 设置LoadLibraryA的IMAGE_IMPORT_BY_NAME
    mov eax, edi
    sub eax, edx
    mov dwLoadLibraryThunkData, eax
    xor eax, eax
    mov word ptr [edi], ax
    add edi, 02h
    mov esi, offset g_szLoadLibrary
CopyLoadLibraryLoop:
    mov al, byte ptr [esi]
    test al, al
    jz EndCopyLoadLibraryLoop
    mov byte ptr [edi], al
    inc esi
    inc edi
    jmp CopyLoadLibraryLoop
EndCopyLoadLibraryLoop:
    mov byte ptr [edi], al
    inc edi
    
    ;; 
      设置GetProcAddress的IMAGE_IMPORT_BY_NAME结构
    mov eax, edi
    sub eax, edx
    mov dwGetProcAddressThunkData, eax  
    xor eax, eax
    mov word ptr [edi], ax
    add edi, 02h
    mov esi, offset g_szGetProcAddress
CopyGetProcAddressLoop:
    mov al, byte ptr [esi]
    test al, al
    jz EndCopyGetProcAddressLoop
    mov byte ptr [edi], al
    inc esi
    inc edi
    jmp CopyGetProcAddressLoop
EndCopyGetProcAddressLoop:   
    mov byte ptr [edi], al
    inc edi
     
    ;; 设置IMAGE_IMPORT_DESCRIPTOR
    ;; æä»¬è¿é设置èªå·±çIMAGE_IMPORT_DESCRIPT
      OR结构,单使用FirstThunk字段
    ;; 将OriginalFirstThunk设置为0
    xor eax, eax
    mov dword ptr [edx].Characteristics, eax
    mov dword ptr [edx].TimeDateStamp, eax
    mov dword ptr [edx].ForwarderChain, eax
    push 0
    pop dword ptr [edx].OriginalFirstThunk
    mov eax, pImportRVA
    add eax, sizeof IMAGE_IMPORT_DESCRIPTOR
    add eax, sizeof IMAGE_IMPORT_DESCRIPTOR
    mov dword ptr [edx].FirstThunk, eax
    sub eax, pImportRVA                         ;  eax 
      = 2 of IMAGE_IMPORT_DESCRIPTOR SIZE
    add edx, eax
    mov eax, dwLoadLibraryThunkData
    add eax, pImportRVA
    mov dword ptr [edx], eax
    add edx, 04h                                ; move 
      to next point
    mov eax, dwGetProcAddressThunkData
    add eax, pImportRVA
    mov dword ptr [edx], eax
    
    ;; 拷贝新的IID到文件
    lea esi, pIID
    mov edi, pImportFVA
    assume edi : ptr IMAGE_SECTION_HEADER
    mov edi, dword ptr [edi].PointerToRawData    
    add edi, pMem
    mov ecx, IMAGE_IMPORT_TABLE_SIZE
    cld
    rep movsb
    
    ;; 修改IT表的虚拟地址和尺寸
    ;; è®¾ç½®è¿ä¸ªæ°æ®ç®å½ä»¥ä¾¿PEå è½½å¨å¯ä»¥å
      找到IID表
    mov esi, pMem
    add esi, dword ptr [esi+03ch]
    assume esi : ptr IMAGE_NT_HEADERS
    mov eax, pImportFVA
    assume eax : ptr IMAGE_SECTION_HEADER
    push dword ptr [eax].VirtualAddress
    pop dword ptr [esi].OptionalHeader.DataDirectory[IM
      AGE_DIRECTORY_ENTRY_IMPORT * sizeof 
      IMAGE_DATA_DIRECTORY].VirtualAddress
    push dword ptr [eax].Misc.VirtualSize
    pop dword ptr [esi].OptionalHeader.DataDirectory[IM
      AGE_DIRECTORY_ENTRY_IMPORT * sizeof 
      IMAGE_DATA_DIRECTORY].isize
    
    assume edx : nothing
    assume eax : nothing
    assume esi : nothing
    assume edi : nothing
    xor eax, eax
ExitEnCryptIID:
    ret
EnCryptIID endp



最后要讲解的就是解密引入表了。解密引入表比起加密来要复杂一些。我们的具体思路是这样的。建立两个节,一个为新的引入表,一个为解密节,这个节也设置为起始的节。
解密引入表,两种解密的方式,一是直接解密后将获取的API地址填写入FirstThunk字段指向的区域,另一种方式是重定位引入地址表。
例如,在我们的程序中调用CreateFileA这个函数。call dword ptr [XXXX], 这个XXXX其实就是FirstThunk指 向的那片区域中的一个地址而已,XXXX指向的地址,如果没有经过我们重定位那么直接就是跟的这个API的地址,如果经过我们重定位将这个XXXX指向的 地址指到我们分配的内存中例如YYYY,那么最终将会转到YYYY指向,我们再在YYYY这处地址写入例如JMP ZZZZ样的跳转指令,其中ZZZZ代 表的是系统API地址到YYYY这个地址的偏移量。那么当程序调用API时最终通过我们设置的一篇代理代码将跳转到API内执行。
这里主要的是,我们只重定位系统自身的DLL,如果是第三方的DLL,我们直接将它的地址设置到其FirstThunk指向的区域就好了。在NT系统下,加载第三方DLL是在小于070000000h,大于077FFFFFFh,9x下为小于080000000h。
解密节的代码如下
代码:
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 

  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 
  24 
  25 
  26 
  27 
  28 
  29 

  30 
  31 
  32 
  33 
  34 
  35 
  36 
  37 

  38 
  39 
  40 
  41 
  42 
  43 
  44 
  45 
  46 
  47 
  48 
  49 
  50 
  51 
  52 
  53 
  54 
  55 
  56 
  57 
  58 
  59 
  60 
  61 
  62 
  63 
  64 
  65 
  66 


  67 


  68 
  69 
  70 
  71 
  72 

  73 
  74 
  75 
  76 
  77 
  78 

  79 
  80 

  81 
  82 
  83 

  84 
  85 

  86 

  87 
  88 
  89 
  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 

  98 
  99 
 100 

 101 
 102 
 103 
 104 
 105 

 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125 
 126 
 127 
 128 
 129 
 130 
 131 
 132 
 133 
 134 
 135 
 136 
 137 
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 
 146 
 147 
 148 
 149 
 150 

 151 
 152 
ListIID proc pFilename : LPSTR
    ;; map file to memory
    LOCAL hFile : HANDLE
    LOCAL hMap : HANDLE
    LOCAL pMem : LPVOID
    LOCAL dwNTHeaderAddr : DWORD
    LOCAL szTmpBuf[MAX_PATH] : BYTE
    
    ;; open file
    invoke CreateFile, pFilename,\
                      GENERIC_WRITE + GENERIC_READ,\
                      FILE_SHARE_WRITE + 
                        FILE_SHARE_READ,\
                      NULL,\
                      OPEN_EXISTING,\
                      FILE_ATTRIBUTE_NORMAL,\
                      0
    .IF eax == INVALID_HANDLE_VALUE
        jmp OpenFileFailed                
    .ENDIF
    mov hFile, eax 
    invoke GetFileSize, hFile, NULL
    .IF eax == 0
        invoke CloseHandle, hFile  
        jmp GetFileSizeFailed
    .ENDIF  
    
    ;; create memory map
    xor ebx, ebx     
    invoke CreateFileMapping, hFile, ebx, 
      PAGE_READWRITE, ebx, eax, ebx
    .IF eax == 0
        invoke CloseHandle, hFile
        jmp CreateMapFailed                
    .ENDIF
    mov hMap, eax
    ;; map file to memory
    invoke MapViewOfFile, hMap,
                       FILE_MAP_WRITE+FILE_MAP_READ+FIL
                         E_MAP_COPY, 
                       ebx, ebx, ebx
    .IF eax == 0
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp MapFileFailed
    .ENDIF
    mov pMem, eax                               
    ;; check it's PE file or not ?
    xchg eax, esi
    assume esi : ptr IMAGE_DOS_HEADER
    .IF [esi].e_magic != 'ZM'
        invoke UnmapViewOfFile, pMem
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp InvalidPE        
    .ENDIF       
    add esi, [esi].e_lfanew
    assume esi : ptr IMAGE_NT_HEADERS   
    .IF word ptr [esi].Signature != 'EP'
        invoke UnmapViewOfFile, pMem
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp InvalidPE        
    .ENDIF
    mov dwNTHeaderAddr, esi
    
    ;; 寻找引入表
    assume esi : ptr IMAGE_NT_HEADERS
    mov eax, dword ptr [esi].OptionalHeader.DataDirecto
      ry[IMAGE_DIRECTORY_ENTRY_IMPORT * sizeof 
      IMAGE_DATA_DIRECTORY].VirtualAddress
    ;; å°åå­å移转æ¢ä¸ºæä»¶åç§»,åå ä¸æ
      ä»¶å¤´çåå­æ å°å°±ç­äºå¼å¥è¡¨å¨æä»¶
      的地址了
    invoke RVA2Offset, pMem, eax
    xchg ebx, eax
    add ebx, pMem
    assume ebx : ptr IMAGE_IMPORT_DESCRIPTOR
    ;; è¿éç´å°ä¸ä¸ªå¨0çIMAGE_IMPORT_DESCRIPTOR
      结构为结束
    ;; 这里判断Name1是否为NULL
    ListIIDLoop:
        mov eax, dword ptr [ebx].Name1
        test eax, eax
        jz EndListIIDLoop
        ;; æ­¤æ¶eaxæåä¸ä¸ªDLLåå­çRVA,æä»¬å
          RVA转换为文件的偏移
        invoke RVA2Offset, pMem, eax
        ;; æä»¶çåç§»å ä¸æä»¶çèµ·å§æéå°
          为文件中的地址
        add eax, pMem
        ;; 打印DLL名
        invoke PrintLine, offset g_szOutFormat, offset 
          g_szOutLine        
        invoke PrintLine, offset g_szOutFormat, eax
        invoke PrintLine, offset g_szOutFormat, offset 
          g_szOutLine
        ;; æ£æ¥OriginalFirstThunkæ¯å¦ä¸º0ã妿ä
          0则使用FirstThunk        
        mov edx, dword ptr [ebx].OriginalFirstThunk
        test edx, edx
        jnz UseOrignalFirstThunk
        mov edx, dword ptr [ebx].FirstThunk
    UseOrignalFirstThunk:        
        ;; 转换RVA转换文件偏移
        invoke RVA2Offset, pMem, edx
        add eax, pMem
        mov edx, eax
    DisplayApiName:
        ;; æ¥çedxçæé«ä½æ¯å¦æ¯1ç¡®å®æ¯å¦ä
          序数引出
        test dword ptr [edx], IMAGE_ORDINAL_FLAG32
        jnz DisPlayOrd
        ;; è¿éedxæåIMAGE_IMPORT_BY_NAMEç»æç
          RVA,继续将它转换
        mov eax, dword ptr [edx]
        invoke RVA2Offset, pMem, eax
        add eax, pMem
        assume eax : ptr IMAGE_IMPORT_BY_NAME
        ;; æå°APIå­ç¬¦ä¸²,å°Name1çå°å设置ç»
          eax寄存器
        lea eax, [eax].Name1
        invoke PrintLine, offset g_szOutFormat, eax
        jmp NextAPI
        DisPlayOrd:
        ;; 取出序数,低2个字节为序数
        mov eax, dword ptr [edx]
        and eax, 0FFFFh        
        invoke PrintLine, offset g_szOutOrdFormat, eax
    NextAPI:
        ;; 取下一个IMAGE_THUNK_DATA的值
        add edx, 04h
        ;; 直到edx指向一个0
        mov eax, dword ptr [edx]
        test eax, eax
        jnz DisplayApiName
        ;; 指向下一个DLL
        add ebx, sizeof IMAGE_IMPORT_DESCRIPTOR
        jmp ListIIDLoop
    EndListIIDLoop:   
LogicShellExit:
    ;; close handle & write it
    invoke UnmapViewOfFile, pMem
    invoke CloseHandle, hMap
    invoke CloseHandle, hFile
    assume ebx : nothing
    assume esi : nothing
    ret
;; ----- Show error message ----- 
OpenFileFailed:
     lea eax, g_szOpenFileFailed
     jmp ShowErr
GetFileSizeFailed:
     lea eax, g_szGetFileSizeFailed
     jmp ShowErr    
CreateMapFailed:
     lea eax, g_szCreateMapFailed
     jmp ShowErr
MapFileFailed:
     lea eax, g_szMapFileFailed
     jmp ShowErr        
InvalidPE:          
     lea eax, g_szInvalidPE
     jmp ShowErr   
ShowErr:
     invoke MessageBox, NULL, eax, offset g_szErr, 
       MB_ICONERROR
     jmp LogicShellExit
ListIID endp



最后要注意的就是有时候FirstThunk指向的区域是不能写的,所以当我们做完全部工作后要将所有节的属性赋予它可写的权限。
代码:
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 

  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 
  24 
  25 
  26 
  27 
  28 
  29 

  30 
  31 
  32 
  33 
  34 
  35 
  36 
  37 

  38 
  39 
  40 
  41 
  42 
  43 
  44 
  45 
  46 
  47 
  48 
  49 
  50 
  51 
  52 
  53 
  54 
  55 
  56 
  57 
  58 
  59 
  60 
  61 
  62 
  63 
  64 
  65 
  66 


  67 


  68 
  69 
  70 
  71 
  72 

  73 
  74 
  75 
  76 
  77 
  78 

  79 
  80 

  81 
  82 
  83 

  84 
  85 

  86 

  87 
  88 
  89 
  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 

  98 
  99 
 100 

 101 
 102 
 103 
 104 
 105 

 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125 
 126 
 127 
 128 
 129 
 130 
 131 
 132 
 133 
 134 
 135 
 136 
 137 
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 
 146 
 147 
 148 
 149 
 150 

 151 
 152 
ListIID proc pFilename : LPSTR
    ;; map file to memory
    LOCAL hFile : HANDLE
    LOCAL hMap : HANDLE
    LOCAL pMem : LPVOID
    LOCAL dwNTHeaderAddr : DWORD
    LOCAL szTmpBuf[MAX_PATH] : BYTE
    
    ;; open file
    invoke CreateFile, pFilename,\
                      GENERIC_WRITE + GENERIC_READ,\
                      FILE_SHARE_WRITE + 
                        FILE_SHARE_READ,\
                      NULL,\
                      OPEN_EXISTING,\
                      FILE_ATTRIBUTE_NORMAL,\
                      0
    .IF eax == INVALID_HANDLE_VALUE
        jmp OpenFileFailed                
    .ENDIF
    mov hFile, eax 
    invoke GetFileSize, hFile, NULL
    .IF eax == 0
        invoke CloseHandle, hFile  
        jmp GetFileSizeFailed
    .ENDIF  
    
    ;; create memory map
    xor ebx, ebx     
    invoke CreateFileMapping, hFile, ebx, 
      PAGE_READWRITE, ebx, eax, ebx
    .IF eax == 0
        invoke CloseHandle, hFile
        jmp CreateMapFailed