实战 API 函数 -- API绝密档案系列之五

本来没有这个续篇,因为两个原因,催生了这篇不该出现的东西。上面第四篇谈的都是自己打造的函数,我觉得有必要给各位欣赏一下,现在流行猛壳是如何打造这个函数的。

我将选择顶级猛壳 Themida 的 GetFunctionAddress 来展示在实际中是如何实现获取函数调用地址的。

代码很长,大多是垃圾代码,我对代码做了详细的解释,对有用的代码都加了注解,并加了编号,没有编号的就是垃圾代码了,你可以完全不看,如果你觉得垃圾代码干扰你的思维,可以将凡是没有注解的地方都删除,余下的就是真正的程序了。

这段代码是从 Themida 加密的 notepad.exe 壳中挖出来的。我在IDA中进行了整理,你所看到的和你自己在IDA或Debug中看到的可能在感觉上完全不同。在回答我的前几篇帖子中,我看到有朋友希望我能讲讲怎样使用IDA,不是谦虚,我对IDA所知甚少,离著作还差的远,我想大家不仿要求在论坛注册名为 bpx 的高手来阐述,IDA 对于分割线不支持中文操作系统的一个 Plugin 就出自这位大侠之手。或者请看雪老大亲自出手,也比我强的多。

分析这些代码对于新手来说可能是非常困难的一件事,这个你不用自卑,我干这个工作已经超过20年,如果你也干了这么长的时间,非常坦白的说,你肯定比我强。

论坛上有许多朋友对破解彼有心得和建树,但许多朋友并不擅长分析,所谓“知己知彼,百战不殆”,了解一下壳是怎样具体工作的,对你一定有帮助。

这篇文章是以非常严肃的态度来处理的,原因是面对创造 Themida 这样强壳的作者,应该给予尊敬。

Themida 的 GetFunctionAddress 入口参数有三个,其中一个是通过变量 APIFristChar 来传递 API 函数的第一个字母,目的可能是为了加快运行速度。

代码:
010D505D ; int __stdcall GetFunctionAddress(int BaseAddress,int CryptCode)
010D505D GetFunctionAddress proc near
010D505D
010D505D FunctionAddress= dword ptr -4
010D505D BaseAddress= dword ptr  4
010D505D CryptCode= dword ptr  8
010D505D
010D505D         pusha                                     ; 1
010D505E         push    eax
010D505F         push    edx
010D5060         push    ecx
010D5061         push    ecx
010D5062         pusha
010D5063         popa
010D5064         sidt    qword ptr [esp-2]
010D5069         pop     ecx
010D506A         jmp     loc_10D507E
010D506F         db 11h, 0AAh, 58h, 8Eh, 9Bh, 0E4h, 0CFh, 16h, 67h, 0C0h, 0DDh
010D506F         db 0F7h, 0F8h, 13h, 0C5h
010D507E
010D507E loc_10D507E:
010D507E         pop     ecx
010D507F         rdtsc
010D5081         jnb     loc_10D5089
010D5087         pusha
010D5088         popa
010D5089
010D5089 loc_10D5089:
010D5089         pop     edx
010D508A         pop     eax
010D508B         mov     eax, 0                            ; 2
010D5090         pusha
010D5091         movsx   ecx, cx
010D5094         add     [ebp+153E2D91h], edi
010D509A         popa
010D509B         mov     edx, [esp+20h+CryptCode]          ; 3  CryptCode 函数名加密代码
010D509F         stc
010D50A0         mov     ss:(dwFunctionsCount+1531F38Ah - ThemidaSeg)[ebp], eax ; 4
010D50A6         pusha
010D50A7         mov     edi, [ebp+153E23DDh]
010D50AD         mov     edx, [ebp+153E2DB9h]
010D50B3         popa
010D50B4         push    edx
010D50B5         push    edx
010D50B6         mov     [ebp+153E11CDh], edx
010D50BC         sidt    qword ptr [esp-2]
010D50C1         pop     edx
010D50C2         push    eax
010D50C3         push    edx
010D50C4         stc
010D50C5         rdtsc
010D50C7
010D50C7 loc_10D50C7:
010D50C7         sub     [ebp+153E04B5h], edx
010D50CD         pop     edx
010D50CE         pop     eax
010D50CF         pop     edx
010D50D0         mov     esi, IMAGE_DOS_HEADER.e_lfanew    ; 5 PE 文件头的偏移量
010D50D5         push    eax
010D50D6         push    edx
010D50D7         jle     loc_10D50E3
010D50DD         jle     $+6
010D50E3
010D50E3 loc_10D50E3:
010D50E3         rdtsc
010D50E5         jmp     loc_10D50FB
010D50EA         db 6Fh, 95h, 0D0h, 51h, 69h, 31h, 0ECh, 0CEh, 9Fh, 65h, 0FEh, 2Bh
010D50EA         db 0B8h, 0Ch, 0DEh, 40h, 18h
010D50FB
010D50FB loc_10D50FB:
010D50FB         pop     edx
010D50FC         pop     eax
010D50FD         add     esi, [esp+20h+BaseAddress]        ; 6 DosHeader.e_lfanew + BaseAddress
010D5101         mov     [ebp+153E0021h], edi
010D5107         pusha
010D5108         call    loc_10D5119
010D510D         db 2 dup(53h), 0AFh, 7Ah, 0B8h, 0A5h, 8Ah, 20h, 58h, 84h, 8Fh
010D510D         db 91h
010D5119
010D5119 loc_10D5119:
010D5119         jb      $+6
010D511F         pop     eax
010D5120         mov     [ebp+153E029Dh], ebx
010D5126         popa
010D5127         lodsw                                     ; 7 获取PE头的偏移量
010D5129         sub     [ebp+153E271Dh], ebx
010D512F         add     eax, [esp+20h+BaseAddress]        ; 8 加上基地址得到 PE 头的起始位置
010D5133         jmp     loc_10D5142
010D5138         db 3Dh, 0BCh, 69h, 0B6h, 4Bh, 0ADh, 0AEh, 0E2h, 79h, 2Bh
010D5142
010D5142 loc_10D5142:
010D5142         nop
010D5143         jle     loc_10D5157
010D5149         pusha
010D514A         jg      $+6
010D5150         jl      $+6
010D5156         popa
010D5157
010D5157 loc_10D5157:
010D5157         push    eax                               ; 9
010D5158         jnp     loc_10D5164
010D515E         pusha
010D515F         mov     esi, edx
010D5161         mov     ah, dl
010D5163         popa
010D5164
010D5164 loc_10D5164:
010D5164         nop
010D5165         pusha
010D5166         clc
010D5167         call    loc_10D5171
010D516C         db 0D2h, 1, 94h, 0ECh, 0E3h
010D5171
010D5171 loc_10D5171:
010D5171         xor     eax, [ebp+153E2071h]
010D5177         pop     ecx
010D5178         popa
010D5179         mov     eax, [eax+IMAGE_NT_HEADERS.OptionalHeader.DirectoryExport.VirtualAddress] ; 10
010D517C         jnb     loc_10D5190
010D5182         pusha
010D5183         push    eax
010D5184         push    edx
010D5185         rdtsc
010D5187         pop     edx
010D5188         pop     eax
010D5189         or      [ebp+153E1569h], edx
010D518F         popa
010D5190
010D5190 loc_10D5190:
010D5190         stc
010D5191         add     eax, [esp+24h+BaseAddress]        ; 11, Export.VirtualAddress + BaseAddress
010D5195         sub     [ebp+153E1965h], edi
010D519B         mov     eax, [eax+IMAGE_EXPORT_DIRECTORY.NumberOfNames] ; 12
010D519E         jb      loc_10D51AA
010D51A4         mov     [ebp+153E0D35h], ecx
010D51AA
010D51AA loc_10D51AA:
010D51AA         inc     eax                               ; 13
010D51AB         jbe     loc_10D51BE
010D51B1         push    eax
010D51B2         push    edx
010D51B3         cmc
010D51B4         rdtsc
010D51B6         push    eax
010D51B7         push    edx
010D51B8         rdtsc
010D51BA         pop     edx
010D51BB         pop     eax
010D51BC         pop     edx
010D51BD         pop     eax
010D51BE
010D51BE loc_10D51BE:                                      ; 14
010D51BE         mov     ss:(dwNumeberOfName+1531F38Ah - ThemidaSeg)[ebp], eax
010D51C4         sub     [ebp+153E199Dh], esi
010D51CA         pop     eax                               ; 15
010D51CB         pusha
010D51CC         mov     eax, [ebp+153E0181h]
010D51D2         jmp     loc_10D51E2
010D51D7         db 1Fh, 5Ch, 94h, 99h, 29h, 91h, 49h, 0Dh, 0B6h, 97h, 8Eh
010D51E2
010D51E2 loc_10D51E2:
010D51E2         popa
010D51E3         mov     esi, [eax+IMAGE_NT_HEADERS.OptionalHeader.DirectoryExport.VirtualAddress] ; 16
010D51E6         jb      loc_10D51F2
010D51EC         or      [ebp+153E2ED9h], ebx
010D51F2
010D51F2 loc_10D51F2:
010D51F2         add     esi, IMAGE_EXPORT_DIRECTORY.AddressOfFunctions ; 17
010D51F5         jnp     loc_10D5210
010D51FB         push    eax
010D51FC         push    edx
010D51FD         push    esi
010D51FE         push    esi
010D51FF         sidt    qword ptr [esp-2]
010D5204         pop     esi
010D5205         pop     esi
010D5206         rdtsc
010D5208         sub     [ebp+153E2869h], edi
010D520E         pop     edx
010D520F         pop     eax
010D5210
010D5210 loc_10D5210:                                      ; 18, AddressOfFunctions + BaseAddress
010D5210         add     esi, [esp+20h+BaseAddress]
010D5214         jmp     loc_10D521F
010D5219         db 88h, 0C1h, 0A4h, 6Ah, 85h, 17h
010D521F
010D521F loc_10D521F:
010D521F         clc
010D5220         lea     edi, (dwAddressOfFunctions+1531F38Ah - ThemidaSeg)[ebp] ; 19
010D5220                                                   ; 取 AddressOfFunctions 变量地址
010D5226         pusha
010D5227         and     eax, [ebp+153E2411h]
010D522D         mov     esi, [ebp+153E2285h]
010D5233         popa
010D5234         lodsd                                     ; 20 取AddressOfFunctions 的偏移量
010D5235         mov     [ebp+153E15F9h], eax
010D523B         add     eax, [esp+20h+BaseAddress]        ; 21 + BaseAddress
010D523F         jmp     loc_10D524F
010D5244         db 3Ch, 69h, 36h, 4, 0A3h, 0C9h, 88h, 19h, 0CBh, 3Fh, 7Fh
010D524F
010D524F loc_10D524F:
010D524F         stosd                                     ; 22 保存 AddressOfFunctions
010D5250         jmp     loc_10D5267
010D5255         db 0ABh, 37h, 2 dup(50h), 53h, 9Ah, 68h, 0C4h, 36h, 69h, 0B8h
010D5255         db 2Ah, 7Ah, 0F5h, 52h, 0E6h, 33h, 0E7h
010D5267
010D5267 loc_10D5267:
010D5267         jmp     loc_10D5271
010D526C         db 62h, 0CCh, 5Dh, 5Ah, 0BAh
010D5271
010D5271 loc_10D5271:
010D5271         lea     edi, (dwAddressOfNames+1531F38Ah - ThemidaSeg)[ebp] ; 23
010D5271                                                   ; 取保存 AddressOfNames 变量地址
010D5277         mov     [ebp+153E2219h], edi
010D527D         lodsd                                     ; 24 取 AddressOfNames 的偏移量
010D527E         pusha
010D527F         mov     ebx, [ebp+153E044Dh]
010D5285         mov     ecx, [ebp+153E1A51h]
010D528B         popa
010D528C         add     eax, [esp+20h+BaseAddress]        ; 25 + BaseAddress
010D5290         pusha
010D5291         or      [ebp+153E133Dh], ecx
010D5297         cmc
010D5298         popa
010D5299         push    eax                               ; 26
010D529A         push    eax
010D529B         push    edx
010D529C         jmp     loc_10D52B4
010D52A1         db 4Dh, 0AEh, 42h, 0B7h, 0AEh, 0E4h, 0B0h, 3Ah, 0CCh, 15h, 0EEh
010D52A1         db 31h, 6Bh, 0F2h, 4Eh, 96h, 0E6h, 3Fh, 0A0h
010D52B4
010D52B4 loc_10D52B4:
010D52B4         rdtsc
010D52B6         push    eax
010D52B7         push    edx
010D52B8         jmp     loc_10D52C8
010D52BD         db 0B8h, 21h, 0F6h, 3Ah, 0D2h, 0D3h, 0ADh, 66h, 0D6h, 0F6h, 0D1h
010D52C8
010D52C8 loc_10D52C8:
010D52C8         rdtsc
010D52CA         pusha
010D52CB         popa
010D52CC         pop     edx
010D52CD         pop     eax
010D52CE         pop     edx
010D52CF         pop     eax
010D52D0         mov     [esp], eax
010D52D3         mov     [ebp+153E1895h], esi
010D52D9         stosd                                     ; 27 保存 AddressOfNames 地址
010D52DA         jnz     loc_10D52F9
010D52E0         jmp     loc_10D52F9
010D52E5         db 9Eh, 3Ah, 2Ah, 0E4h, 0BCh, 46h, 8Bh, 5, 7Dh, 0C5h, 9, 0C0h
010D52E5         db 0AFh, 61h, 0A4h, 0Ah, 0FAh, 84h, 0A1h, 0D4h
010D52F9
010D52F9 loc_10D52F9:
010D52F9         lea     edi, (dwAddressOfNamesOrdinals+1531F38Ah - ThemidaSeg)[ebp] ; 28
010D52F9                                                   ; 取 AddressOfNameOrdinals 变量地址
010D52FF         stc
010D5300         cld                                       ; 29
010D5301         lodsd                                     ; 30 读 AddressNameOrdinals 的偏移量
010D5302         push    eax
010D5303         push    edx
010D5304         jnz     loc_10D5320
010D530A         jmp     loc_10D5320
010D530F         db 6Ch, 29h, 0AEh, 0E6h, 87h, 64h, 49h, 7Ah, 92h, 0Ch, 38h, 5
010D530F         db 0CCh, 0CEh, 3Fh, 18h, 0F2h
010D5320
010D5320 loc_10D5320:
010D5320         rdtsc
010D5322         cld
010D5323         pop     edx
010D5324         pop     eax
010D5325         pusha
010D5326         and     di, 44F4h
010D532B         mov     al, 7Ch
010D532D         popa
010D532E         add     eax, [esp+24h+BaseAddress]        ; 31, + BaseAddress
010D5332         jnz     loc_10D534D
010D5338         jmp     loc_10D534D
010D533D         db 3Eh, 3Bh, 53h, 57h, 1Ch, 0C4h, 0EAh, 0F4h, 5Ah, 0CBh, 86h, 67h
010D533D         db 70h, 0BCh, 0D2h, 0B5h
010D534D
010D534D loc_10D534D:
010D534D         cld
010D534E         stosd                                     ; 32 保存 AddressOfNameOrdinals 地址
010D534F         clc
010D5350         pop     esi                               ; 33
010D5351         pusha
010D5352         push    edi
010D5353         push    edi
010D5354         mov     edi, [ebp+153E154Dh]
010D535A         sidt    qword ptr [esp-2]
010D535F         pop     edi
010D5360         sub     [ebp+153E17B1h], edi
010D5366         pop     edi
010D5367         popa
010D5368         jmp     LoopNextName
010D536D         db 0E8h, 0DCh, 0B9h, 0F5h, 0DFh, 0FBh, 0D8h, 8, 0Fh, 24h, 79h
010D536D         db 0D5h, 0A3h, 0A8h
010D537B;下面代码搜索第一字母相匹配的函数名
010D537B LoopNextName:
010D537B         dec     ss:(dwNumeberOfName+1531F38Ah - ThemidaSeg)[ebp]    ; 34
010D537B                                                   ; 计数器减 1
010D5381         cmp     ss:(dwNumeberOfName+1531F38Ah - ThemidaSeg)[ebp], 0 ; 35
010D5381                                                   ; 如果为 0 则失败
010D5388         jnz     short IfNumberOfNameNotZero       ; 36
010D538A         mov     [esp+20h+FunctionAddress], 0      ; 37 如果没有找到,eax 中返回 0
010D5392         lea     esi, (NoFind_CC+1531F38Ah - ThemidaSeg)[ebp] ; 38
010D5398         jmp     esi                               ; 39 没有要求的函数跳转
010D539A
010D539A IfNumberOfNameNotZero:                            ; 40
010D539A         push    esi
010D539B         lodsd                                     ; 41, 取函数名的偏移地址
010D539C         add     eax, [esp+24h+BaseAddress]        ; 42, + BaseAddress
010D53A0         xchg    eax, edi                          ; 43
010D53A1         mov     ebx, edi                          ; 44
010D53A3         mov     al, ss:(APIFristChar+1531F38Ah - ThemidaSeg)[ebp] ; 45
010D53A3                                                   ; 查找 API 函数名第一个字母
010D53A9         test    al, al                            ; 46 如果为 0 为无效函数名
010D53AB         jz      short loc_10D53B1                 ; 47
010D53AD         cmp     al, [edi]                         ; 48, 比较函数的第一字母
010D53AF         jnz     short loc_10D53FD                 ; 49
010D53B1
010D53B1 loc_10D53B1:                                      ; 50
010D53B1         push    edi
010D53B2         xor     al, al                            ; 51
010D53B4
010D53B4 loc_10D53B4:
010D53B4         scasb                                     ; 52 字符串以 0 结尾
010D53B5         jnz     short loc_10D53B4                 ; 53
010D53B7         pop     esi                               ; 54
010D53B8         sub     edi, ebx                          ; 55, 得到函数名的长度
010D53BA         push    edx                               ; 56
010D53BB         cld                                       ; 57
010D53BC         xor     ecx, ecx                          ; 58
010D53BE         dec     ecx                               ; 59
010D53BF         mov     edx, ecx                          ; 60
010D53C1 ;下面的代码将API函数名加密,然后比较,如相同则是需要的函数
010D53C1
010D53C1 loc_10D53C1:                                      ; 61
010D53C1         xor     eax, eax
010D53C3         xor     ebx, ebx                          ; 62
010D53C5         lodsb                                     ; 63
010D53C6         xor     al, cl                            ; 64
010D53C8         mov     cl, ch                            ; 65
010D53CA         mov     ch, dl                            ; 66
010D53CC         mov     dl, dh                            ; 67
010D53CE         mov     dh, 8                             ; 68
010D53D0
010D53D0 loc_10D53D0:                                      ; 69
010D53D0         shr     bx, 1
010D53D3         rcr     ax, 1                             ; 70
010D53D6         jnb     short loc_10D53E1                 ; 71
010D53D8         xor     ax, 5041h                         ; 72
010D53DC         xor     bx, 5449h                         ; 73
010D53E1
010D53E1 loc_10D53E1:                                      ; 74
010D53E1         dec     dh
010D53E3         jnz     short loc_10D53D0                 ; 75
010D53E5         xor     ecx, eax                          ; 76
010D53E7         xor     edx, ebx                          ; 77
010D53E9         dec     edi                               ; 78
010D53EA         jnz     short loc_10D53C1                 ; 79
010D53EC         not     edx                               ; 80
010D53EE         not     ecx                               ; 81
010D53F0         mov     eax, edx                          ; 82
010D53F2         rol     eax, 10h                          ; 83
010D53F5         mov     ax, cx                            ; 84
010D53F8         pop     edx                               ; 85
010D53F9         cmp     edx, eax                          ; 86
010D53FB         jz      short IfEqu                       ; 87
010D53FD
010D53FD loc_10D53FD:                                      ; 88
010D53FD         pop     esi
010D53FE         add     esi, 4                            ; 89
010D5401         inc     ss:(dwFunctionsCount+1531F38Ah - ThemidaSeg)[ebp] ; 90
010D5407         jmp     LoopNextName                      ; 91
010D540C
010D540C IfEqu:                                            ; 92
010D540C         pop     esi
010D540D         push    eax
010D540E         push    edx
010D540F         mov     [ebp+153E13DDh], edi
010D5415         rdtsc
010D5417         jmp     loc_10D5424
010D541C         db 0CEh, 34h, 14h, 87h, 0F2h, 52h, 0EDh, 67h
010D5424
010D5424 loc_10D5424:
010D5424         pop     edx
010D5425         pop     eax
010D5426         sub     [ebp+153E1F45h], edi
010D542C         mov     eax, ss:(dwFunctionsCount+1531F38Ah - ThemidaSeg)[ebp] ; 93
010D542C                                                   ; FunctionsCount 中的数即导出函数的编号
010D5432         pusha
010D5433         push    esi
010D5434         sbb     eax, 11C02E28h
010D543A         pop     ecx
010D543B         and     edi, [ebp+153E1B69h]
010D5441         popa
010D5442         shl     eax, 1                            ; 94, 导出函数编号数组的每个编号占两个字节,
010D5442                                                   ; 所以这里将这个 FunctionsCount 乘 2
010D5444         push    eax
010D5445         push    edx
010D5446         sub     [ebp+153E14E5h], edi
010D544C         rdtsc
010D544E         jnp     loc_10D5455
010D5454         stc
010D5455
010D5455 loc_10D5455:
010D5455         pop     edx
010D5456         pop     eax
010D5457         stc
010D5458         add     eax, ss:(dwAddressOfNamesOrdinals+1531F38Ah - ThemidaSeg)[ebp] ;
010D5458                                                   ; 95, 加上导出函数编号数组的基地址
010D545E         jmp     loc_10D546D
010D5463         db 12h, 6Eh, 0C2h, 0C5h, 58h, 0F3h, 5Ch, 28h, 4Fh, 5Ch
010D546D
010D546D loc_10D546D:
010D546D         sub     esi, esi                          ; 96
010D546F         jmp     loc_10D5480
010D5474 byte_10D5474 db 0D8h, 14h, 5Bh, 3Dh, 52h, 7Eh, 24h, 7Eh, 6, 4Eh, 22h, 0E3h
010D5480
010D5480 loc_10D5480:
010D5480         xchg    eax, esi                          ; 97 esi 中为导出函数编号数组的基地址
010D5481         mov     [ebp+153E26EDh], ebx
010D5487         push    eax
010D5488         push    edx
010D5489         mov     [ebp+153E222Dh], eax
010D548F         rdtsc
010D5491         jnp     loc_10D5498
010D5497         stc
010D5498
010D5498 loc_10D5498:
010D5498         pop     edx
010D5499         pop     eax
010D549A         lodsw                                     ; 98, 取出API函数的编号 16位
010D549C         cld
010D549D         shl     eax, 2                            ; 99, 编号乘 4
010D54A0         pusha
010D54A1         mov     ah, 3Fh
010D54A3         adc     eax, 795FFC2Eh
010D54A9         popa
010D54AA         add     eax, ss:(dwAddressOfFunctions+1531F38Ah - ThemidaSeg)[ebp] ;
010D54AA                                                   ; 100, 在AddressOfFunctions 数组中定位到
010D54AA                                                   ; API 函数偏移量的地址
010D54B0         add     [ebp+153E1839h], edi
010D54B6         xchg    eax, esi                          ; 101, 将地址交换到 esi 寄存器中准备 Load
010D54B7         jnz     loc_10D54BE
010D54BD         stc
010D54BE
010D54BE loc_10D54BE:
010D54BE         lodsd                                      ; 102 取API函数的偏移地址
010D54BF         mov     [ebp+153E058Dh], edx
010D54C5         mov     ecx, [esp+24h]
010D54C9         jmp     loc_10D54D3
010D54CE         db 0ADh, 35h, 31h, 0, 0Dh
010D54D3
010D54D3 loc_10D54D3:
010D54D3         add     eax, [esp+20h+BaseAddress]         ; 103 + BaseAddress
010D54D3                                                    ; eax 中既是 API 函数的调用地址
010D54D7         push    edi
010D54D8         push    edi
010D54D9         pusha
010D54DA         and     di, 0AFBBh
010D54DF         call    loc_10D54F7
010D54E4         db 0E0h, 96h, 90h, 81h, 0F4h, 0Bh, 0D2h, 1Ah, 22h, 0F7h, 77h, 65h
010D54E4         db 6, 0BBh, 88h, 4Bh, 8Fh, 7Eh, 5Fh
010D54F7
010D54F7 loc_10D54F7:
010D54F7         pop     edx
010D54F8         popa
010D54F9         sidt    qword ptr [esp-2]
010D54FE         pop     edi
010D54FF         jmp     loc_10D550B
010D5504         db 6Dh, 46h, 1Ah, 3Eh, 0C4h, 0BCh, 0A6h
010D550B
010D550B loc_10D550B:
010D550B         pop     edi
010D550C         mov     [esp+20h+FunctionAddress], eax    ; 104, 保存结果,即函数调用地址
010D550C                                                   ; 这里将结果保存在入口pushad的第
010D550C                                                   ; 一个压入堆栈的寄存器,即eax,程序
010D550C                                                   ; 在正常退出时,popad 将结果弹人
010D550C                                                   ; eax 寄存器。
010D5510         pusha
010D5511         jge     loc_10D552D
010D5517         jmp     loc_10D552D
010D551C         db 7Fh, 0Fh, 35h, 9Bh, 5, 0CDh, 4, 0B0h, 1Ah, 78h, 0EAh, 60h, 0E3h
010D551C         db 0A2h, 75h, 0B9h, 0BCh
010D552D
010D552D loc_10D552D:
010D552D         mov     [ebp+153E2829h], eax
010D5533         popa
010D5534         xchg    eax, esi                          ; 105, 将函数起始地址转入 esi
010D5534                                                   ; 准备检测是否有debug断点
010D5535         or      [ebp+153E1F99h], edx
010D553B ;下面这段代码是用来检测API函数的入口是否有断点,Debug 断点中断是一个非常特殊的代码,0CCh,
010D553B ;它的二进制码为 11001100b 该段代码利用 1100 的特征,循环左移,四个循环为一个组合,检测是
010D553B ;否为 1100b,这段代码的构思非常巧妙。
010D553B         lodsb                                     ; 106, 取入口地址的第一个字节
010D553C         push    eax
010D553D         push    eax
010D553E         cmc
010D553F         sidt    qword ptr [esp-2]
010D5544         pop     eax
010D5545         xor     [ebp+153E1D01h], ecx
010D554B         pop     eax
010D554C         pusha
010D554D         push    esi
010D554E         mov     [ebp+153E162Dh], eax
010D5554         pop     ecx
010D5555         mov     [ebp+153E0331h], edx
010D555B         popa
010D555C         xor     cl, cl                            ; 107, 计数器清零
010D555E         mov     [ebp+153E23FDh], eax
010D5564         inc     cl                                ; 108
010D5566         pusha
010D5567         cld
010D5568         add     ecx, [ebp+153E15C9h]
010D556E         popa
010D556F
010D556F LoopCheckINT3:
010D556F         rcl     al, 1                      ; 109 1001100b 如果第 7 位是 0,表示不是断点
010D5571         jnb     NoFind_CC                  ; 110 没有断点跳转,下同
010D5577         mov     [ebp+153E1ABDh], edx
010D557D         push    eax
010D557E         push    eax
010D557F         clc
010D5580         sidt    qword ptr [esp-2]
010D5585         pop     eax
010D5586         mov     [ebp+153E162Dh], eax
010D558C         pop     eax
010D558D         rcl     al, 1                      ; 111 001100b  如果第 6 位是 0,表示不是断点
010D558F         jnb     NoFind_CC                  ; 112
010D5595         mov     ss:(dword_10D4733+1531F38Ah - ThemidaSeg)[ebp], ebx
010D559B         rcl     al, 1                      ; 113 01100b   如果第 5 位是 1,表示不是断点
010D559D         jb      NoFind_CC                  ; 114
010D55A3         cld
010D55A4         rcl     al, 1                      ; 115 1100b    如果第 4 位是 1,表示不是断点
010D55A6         jb      NoFind_CC                  ; 116
010D55AC ;下面为发现了断点中断的特殊代码 0CCh,程序从这转移,提示发现 Debug
010D55AC         push    esi
010D55AD         push    esi
010D55AE         jmp     loc_10D55BD
010D55B3         db 4, 38h, 0CDh, 79h, 8Ch, 0FBh, 77h, 9, 82h, 9Fh
010D55BD
010D55BD loc_10D55BD:
010D55BD         sidt    qword ptr [esp-2]
010D55C2         pop     esi
010D55C3         push    eax
010D55C4         push    edx
010D55C5         cld
010D55C6         rdtsc
010D55C8         push    eax
010D55C9         push    edx
010D55CA         rdtsc
010D55CC         pop     edx
010D55CD         pop     eax
010D55CE         pop     edx
010D55CF         pop     eax
010D55D0         pop     esi
010D55D1         dec     cl                                ; 117 计数器减一
010D55D3         jns     LoopCheckINT3                     ; 118 循环检测低位
010D55D9         push    esi
010D55DA         push    esi
010D55DB         jmp     loc_10D55EA
010D55E0         db 0ECh, 0E2h, 7Fh, 0E5h, 4Fh, 53h, 6Bh, 64h, 0EDh, 39h
010D55EA
010D55EA loc_10D55EA:
010D55EA         sidt    qword ptr [esp-2]
010D55EF         pop     esi
010D55F0         push    edx
010D55F1         push    edx
010D55F2         push    ecx
010D55F3         push    ecx
010D55F4         sidt    qword ptr [esp-2]
010D55F9         pop     ecx
010D55FA         pop     ecx
010D55FB         sidt    qword ptr [esp-2]
010D5600         pop     edx
010D5601         jp      $+6
010D5607         pop     edx
010D5608         pop     esi
010D5609         add     esp, 20h                          ; 119 堆栈平衡
010D560C         push    eax
010D560D         push    edx
010D560E         or      [ebp+153E2499h], edi
010D5614         rdtsc
010D5616         push    edi
010D5617         push    edi
010D5618         jbe     $+6
010D561E         sidt    qword ptr [esp-2]
010D5623         pop     edi
010D5624         clc
010D5625         pop     edi
010D5626         pop     edx
010D5627         pop     eax
010D5628         mov     [esp+FunctionAddress], 0          ; 120 发现debug将函数调用地址置 0
010D5630         sub     [ebp+153E13C9h], edi
010D5636         mov     eax, 1                            ; 121 发现debug设标志
010D563B         clc
010D563C         lea     ecx, (FindDebug+1531F38Ah - ThemidaSeg)[ebp] ; 122
010D5642         mov     ss:(FindDebugFlag+1531F38Ah - ThemidaSeg)[ebp], eax ; 123
010D5648 ;从这里进入发现 Debug 的处理
010D5648         jmp     ecx                               ; 124 转移到处理发现 debug 的程序
010D564A         db 89h, 95h, 51h, 21h, 3Eh, 15h, 0E9h, 0Dh, 3 dup(0), 12h, 0E3h
010D564A         db 94h, 0F6h, 77h, 9Ah, 4, 17h, 6Fh, 0C6h, 0BBh, 48h, 53h
010D5662
010D5662 NoFind_CC:                                        ; 125, 清除这个API首字母
010D5662         mov     ss:(dwAPIFristChar+1531F38Ah - ThemidaSeg)[ebp], 0
010D5669         pusha
010D566A         mov     edi, [ebp+153E1BC5h]
010D5670         jmp     loc_10D567F
010D5675         db 0ECh, 22h, 9, 87h, 51h, 0D6h, 7Fh, 49h, 0F3h, 0FFh
010D567F
010D567F loc_10D567F:
010D567F         popa
010D5680         popa
010D5681         pusha
010D5682         jmp     loc_10D5695
010D5687         db 1Ch, 36h, 91h, 0B8h, 82h, 0B8h, 1Dh, 75h, 8Ah, 90h, 15h, 4Ah
010D5687         db 19h, 65h
010D5695
010D5695 loc_10D5695:
010D5695         movsx   edx, bx
010D5698         popa
010D5699         pusha
010D569A         sub     [ebp+153E265Dh], esi
010D56A0         mov     bh, dl
010D56A2         popa                                      ; 126
010D56A3         retn    8                                 ; 127 平衡入口参数
010D56A3 GetFunctionAddress endp
这里就不再多做解释,上面的代码中已经加了注解,前部计算函数调用地址的地方我加的注解比较少,原因是比较简单,而且在上一篇中已经详细解释过,如还不明白,请参考我的第4篇文章,Themida 用来查找函数的方法比较简单,是顺序查讯,我第四篇中的方法比较复杂。代码中对debug断点的检测方法比较特别,值得学习。其实这个没有什么用处,你对某一个函数下断点,完全可以下在第二句或更后面的句子,这些方法就没有用了。

我们也顺便看看ACP对应的方法,至于那种方法好,这是各人的喜好问题了:
代码:
0068E9DC     mov eax, ss:(GetModuleHandleA+401000h - start)[ebp]
0068E9E2     movzx eax, byte ptr [eax]  ; eax中是GetModuleHandleA的第一个字节,如果设置了断点
0068E9E2                                ; eax的值为cc,这里检测是否被Debug跟踪。
0068E9E5     sub eax, 33h        ; 这里 33h + 99h = 0CCh
0068E9E8     cmp eax, 99h
0068E9ED     jz  short SetTimeStemp_0   ; 如果在GetModuleHandleA的第一个字节发现int3代码,则转
0068E9ED                                ; 去SetTimeStemp设置时间,开始记时(CPU周期数)
Themida 的重定位写的比较好,例如上面的代码:
代码:
010D569A         sub     [ebp+153E265Dh], esi
对于上面的表示法,你不可能看出这里指向那个变量,而且 ebp 是个巨大的数,在这里 ebp = 0EBCF1C76h
代码:
010D4B60         call    $+5
010D4B65         pop     ebp
010D4B66 ; ebp = EBCF1C76
010D4B66 ; 0 - (EBCF1C76 - 1001000) = 1530F38A
010D4B66         sub     ebp, 153E2EEFh
上面这段代码就是 Themida 的重定位算法,Themida 在写代码和编译时加入了一个特殊的数,就是这个 1530E38Ah (这里上面那个数差了 1000h 是因为我没有在IDA中加载程序时没有加载 PE 文件头,所以只能使用 1001000为起始地址),下面是具体在编程时的实现:
代码:
GetCurrAddr Proc
    call $ + 5
    pop ebp
    sub ebp, Offset GetCurrAddr + 5 + 1530E38Ah
    ret
GetCurrAddr EndP
在ACP中同样部分是这样的:
代码:
0068C4D2 GetCurrAddr proc near
0068C4D2     call $+5
0068C4D7     pop ebp
0068C4D8     sub ebp, 4114D7h
0068C4DE     retn
0068C4DE GetCurrAddr endp
在汇编中具体实现:
代码:
GetCurrAddr Proc
    call $ + 5
    pop ebp
    sub ebp, Offset GetCurrAddr + 5
    ret
GetCurrAddr EndP
其中的差别就是少了那个特殊的数。

这里顺便说几句关于如何在IDA中分析这种经过重定位后的 [ebp + xxxxxxxx] 结构的数据:

在 IDA 中使用 Offset(User-defined)  Ctrl-R,在弹出的窗口中选择和填入:
    OFF32
    BaseAddress   =  0x1001000
    TargetAddress =  0xFFFFFFFF
    Target Delta  =  0x1530F38A
这样,IDA 就能正确的指出变量的地址:

在IDA的脚本IDC中对应的命令是:OpOffEx(FindAddr, 0, 2, 0xFFFFFFFF, 0x1001000, 0x1530F39A);
代码:
处理前:
010D569A         sub     [ebp+153E265Dh], esi
处理后:
010D569A         sub     ss:(dword_10D42D3+1530F38Ah - CodeBase)[ebp], esi
你可以根据自己的理解,定义dword_10D42D3,这样可以大大提高代码的可读性。

我在第四篇文中给出了一个程序,使用自己打造的API函数,并在heXer的指点下,给出了读取外来的函数的方法,但在实际使用中,根据自己程序的使用函数范围,可以灵活的处理,因为我们不是开发大众化的通用函数,而是为某些目的而使用的特殊函数,Themida 的这段实战函数也没有处理这些特殊的情况,因为不需要。

下面我们修改第四篇中的代码,改用Themida的GetFunctionAddress来替换我们自己打造的函数,在使用Themida函数前,希望读者能够先自己尝试一下,能否自己完成改造,必尽,我已经将实际使用的127句代码全部钩出来了,对你而言,困难已经小了很多。我们在使用Themida函数前,还需要将函数名字转变为一个加密的数,加密的方法就是Themida的GetFunctionAddress中的那段加密代码,挖出来即可工作。在阅读代码的时候,特别要注意那个特殊的数--1530E38Ah 为了书写方便,我定义一个PO来代替这个太长的数,P 的意思是 pointer ,O 的意思是 Offset 。

我希望你能在OD中跟踪这段代码,尤其注意 [ebp+xxxxxxxx] 这个表示变量的方法,Themida 的这个方法比国人开发的 ACP 要好的多,而且实现并不复杂。你可以找一个函数来下断点,当然只能下在第一个字节,看看 Themida 检测 debug 的方法是如何运行的。
代码:
; #########################################################################

      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

; #########################################################################

      include Tfiltinpt.inc

; #########################################################################


        ;=================
        ;  My API prototypes
        ;=================

        MyCallWindowProc   PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        MyCreateWindowEx   PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,\
                                 :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        MyDefWindowProc    PROTO :DWORD,:DWORD,:DWORD,:DWORD
        MyDispatchMessage  PROTO :DWORD
        MyGetMessage       PROTO :DWORD,:DWORD,:DWORD,:DWORD
        MyGetSystemMetrics PROTO :DWORD
        MyLoadCursor       PROTO :DWORD,:DWORD
        MyLoadIcon         PROTO :DWORD,:DWORD
        MyLoadMenu         PROTO :DWORD,:DWORD
        MyMessageBox       PROTO :DWORD,:DWORD,:DWORD,:DWORD
        MyPostQuitMessage  PROTO :DWORD
        MyRegisterClassEx  PROTO :DWORD
        MySendMessage      PROTO :DWORD,:DWORD,:DWORD,:DWORD
        MySetMenu          PROTO :DWORD,:DWORD
        MySetWindowLong    PROTO :DWORD,:DWORD,:DWORD
        MyShowWindow       PROTO :DWORD,:DWORD
        MyTranslateMessage PROTO :DWORD
        MyUpdateWindow     PROTO :DWORD
        MyExitProcess      PROTO :DWORD
        MyGetCommandLine   PROTO
        MyGetModuleHandle  PROTO :DWORD
        ;=================
        ; Local prototypes
        ;=================
;        GetFunctionAddress PROTO :DWORD,:DWORD
        GetProcBaseAddress PROTO :DWORD
        GetDllBase         PROTO :DWORD

        WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
        WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
        TopXY   PROTO :DWORD,:DWORD
        EditSl  PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        Static  PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        Ed1Proc PROTO :DWORD,:DWORD,:DWORD,:DWORD
        Ed2Proc PROTO :DWORD,:DWORD,:DWORD,:DWORD
        Ed3Proc PROTO :DWORD,:DWORD,:DWORD,:DWORD
    .data
        szDisplayName               db " Filtered Input DEMO",0
        CommandLine                 dd 0
        hWnd                        dd 0
        hInstance                   dd 0
        hEdit1                      dd 0
        hEdit2                      dd 0
        hEdit3                      dd 0
        lpfnEd1Proc                 dd 0
        lpfnEd2Proc                 dd 0
        lpfnEd3Proc                 dd 0
        MyImageBase                 dd 0

        szKernel32                  db 'kernel32.dll',0
        szNTDLL                     db 'NTDLL.dll',0

        ;API 函数名列表
        szCallWindowProcA           db 'CallWindowProcA',0
        szCreateWindowExA           db 'CreateWindowExA',0
        szDefWindowProcA            db 'DefWindowProcA',0
        szDispatchMessageA          db 'DispatchMessageA',0
        szGetMessageA               db 'GetMessageA',0
        szGetSystemMetrics          db 'GetSystemMetrics',0
        szLoadCursorA               db 'LoadCursorA',0
        szLoadIconA                 db 'LoadIconA',0
        szLoadMenuA                 db 'LoadMenuA',0
        szMessageBoxA               db 'MessageBoxA',0
        szPostQuitMessage           db 'PostQuitMessage',0
        szRegisterClassExA          db 'RegisterClassExA',0
        szSendMessageA              db 'SendMessageA',0
        szSetMenu                   db 'SetMenu',0
        szSetWindowLongA            db 'SetWindowLongA',0
        szShowWindow                db 'ShowWindow',0
        szTranslateMessage          db 'TranslateMessage',0
        szUpdateWindow              db 'UpdateWindow',0
                                    dd  0
        szExitProcess               db 'ExitProcess',0
        szGetCommandLineA           db 'GetCommandLineA',0
        szGetModuleHandleA          db 'GetModuleHandleA',0
                                    dd  0
        EVEN
        ;API 函数名引用表
        OffCallWindowProcA          dd  offset szCallWindowProcA
        OffCreateWindowExA          dd  offset szCreateWindowExA
        OffDefWindowProcA           dd  offset szDefWindowProcA
        OffDispatchMessageA         dd  offset szDispatchMessageA
        OffGetMessageA              dd  offset szGetMessageA
        OffGetSystemMetrics         dd  offset szGetSystemMetrics
        OffLoadCursorA              dd  offset szLoadCursorA
        OffLoadIconA                dd  offset szLoadIconA
        OffLoadMenuA                dd  offset szLoadMenuA
        OffMessageBoxA              dd  offset szMessageBoxA
        OffPostQuitMessage          dd  offset szPostQuitMessage
        OffRegisterClassExA         dd  offset szRegisterClassExA
        OffSendMessageA             dd  offset szSendMessageA
        OffSetMenu                  dd  offset szSetMenu
        OffSetWindowLongA           dd  offset szSetWindowLongA
        OffShowWindow               dd  offset szShowWindow
        OffTranslateMessage         dd  offset szTranslateMessage
        OffUpdateWindow             dd  offset szUpdateWindow
                                    dd  0
        OffExitProcess              dd  offset szExitProcess
        OffGetCommandLineA          dd  offset szGetCommandLineA
        OffGetModuleHandleA         dd  offset szGetModuleHandleA
                                    dd  0

        ;函数名加密表
        DeCodeCallWindowProcA       db  0
                                    dd  0
        DeCodeCreateWindowExA       db  0
                                    dd  0
        DeCodeDefWindowProcA        db  0
                                    dd  0
        DeCodeDispatchMessageA      db  0
                                    dd  0
        DeCodeGetMessageA           db  0
                                    dd  0
        DeCodeGetSystemMetrics      db  0
                                    dd  0
        DeCodeLoadCursorA           db  0
                                    dd  0
        DeCodeLoadIconA             db  0
                                    dd  0
        DeCodeLoadMenuA             db  0
                                    dd  0
        DeCodeMessageBoxA           db  0
                                    dd  0
        DeCodePostQuitMessage       db  0
                                    dd  0
        DeCodeRegisterClassExA      db  0
                                    dd  0
        DeCodeSendMessageA          db  0
                                    dd  0
        DeCodeSetMenu               db  0
                                    dd  0
        DeCodeSetWindowLongA        db  0
                                    dd  0
        DeCodeShowWindow            db  0
                                    dd  0
        DeCodeTranslateMessage      db  0
                                    dd  0
        DeCodeUpdateWindow          db  0
                                    dd  0
                                    dd  0
        EVEN
        DeCodeExitProcess           db  0
                                    dd  0
        DeCodeGetCommandLineA       db  0
                                    dd  0
        DeCodeGetModuleHandleA      db  0
                                    dd  0
                                    dd  0
        ;自建输入表
        ; Imports from user32
        _CallWindowProcA            dd  ?
        _CreateWindowExA            dd  ?
        _DefWindowProcA             dd  ?
        _DispatchMessageA           dd  ?
        _GetMessageA                dd  ?
        _GetSystemMetrics           dd  ?
        _LoadCursorA                dd  ?
        _LoadIconA                  dd  ?
        _LoadMenuA                  dd  ?
        _MessageBoxA                dd  ?
        _PostQuitMessage            dd  ?
        _RegisterClassExA           dd  ?
        _SendMessageA               dd  ?
        _SetMenu                    dd  ?
        _SetWindowLongA             dd  ?
        _ShowWindow                 dd  ?
        _TranslateMessage           dd  ?
        _UpdateWindow               dd  ?
                                    dd  0
        ; Imports from kernel32
        _ExitProcess                dd  ?
        _GetCommandLineA            dd  ?
        _GetModuleHandleA           dd  ?
                                    dd  0


    .code
    option prologue:none
; 自建输入表引用表
; __stdcall CallWindowProcA(x,x,x,x,x)
MyCallWindowProc    proc lpPrevWndFunc:DWORD,hWndl:DWORD,Msg:DWORD,wParam:DWORD,lParam:DWORD
                jmp     _CallWindowProcA    ; CallWindowProcA(x,x,x,x,x)
MyCallWindowProc    endp

; __stdcall CreateWindowExA(x,x,x,x,x,x,x,x,x,x,x,x)
MyCreateWindowEx    proc dwExStyle:DWORD,lpClassName:DWORD,lpWindowName:DWORD,dwStyle:DWORD,\
                         x:DWORD,y:DWORD,nWidth:DWORD,nHeight:DWORD,hWndParent:DWORD,\
                         hMenu:DWORD,hInstance1:DWORD,lpParam:DWORD
                jmp     _CreateWindowExA    ; CreateWindowExA(x,x,x,x,x,x,x,x,x,x,x,x)
MyCreateWindowEx    endp

; __stdcall DefWindowProcA(x,x,x,x)
MyDefWindowProc     proc hWnd1:DWORD,Msg:DWORD,wParam:DWORD,lParam:DWORD
                jmp     _DefWindowProcA    ; DefWindowProcA(x,x,x,x)
MyDefWindowProc     endp

; __stdcall DispatchMessageA(x)
MyDispatchMessage   proc lpmsg:DWORD
                jmp     _DispatchMessageA   ; DispatchMessageA(x)
MyDispatchMessage   endp

; __stdcall GetMessageA(x,x,x,x)
MyGetMessage        proc lpMsg:DWORD,hWnd1:DWORD,wMsgFilterMin:DWORD,wMsgFilterMax:DWORD
                jmp     _GetMessageA    ; GetMessageA(x,x,x,x)
MyGetMessage        endp

; __stdcall GetSystemMetrics(x)
MyGetSystemMetrics  proc nIndex:DWORD
                jmp     _GetSystemMetrics   ; GetSystemMetrics(x)
MyGetSystemMetrics  endp

; __stdcall LoadCursorA(x,x)
MyLoadCursor        proc hInstance1:DWORD,lpCursorName:DWORD
                jmp     _LoadCursorA   ; LoadCursorA(x,x)
MyLoadCursor        endp

; __stdcall LoadIconA(x,x)
MyLoadIcon          proc hInstance1:DWORD,lpIconName:DWORD
                jmp     _LoadIconA   ; LoadIconA(x,x)
MyLoadIcon          endp

; __stdcall LoadMenuA(x,x)
MyLoadMenu          proc hInstance1:DWORD,lpMenuName:DWORD
                jmp     _LoadMenuA   ; Loads the specified menu resource
MyLoadMenu          endp

; __stdcall MessageBoxA(x,x,x,x)
MyMessageBox        proc hWnd1:DWORD,lpText:DWORD,lpCaption:DWORD,uType:DWORD
                jmp     _MessageBoxA    ; MessageBoxA(x,x,x,x)
MyMessageBox        endp

; __stdcall PostQuitMessage(x)
MyPostQuitMessage   proc nExitCode:DWORD
                jmp     _PostQuitMessage   ; PostQuitMessage(x)
MyPostQuitMessage   endp

; __stdcall RegisterClassExA(x)
MyRegisterClassEx   proc lpwcx:DWORD
                jmp     _RegisterClassExA   ; RegisterClassExA(x)
MyRegisterClassEx   endp

; __stdcall SendMessageA(x,x,x,x)
MySendMessage       proc hWnd1:DWORD,Msg:DWORD,wParam:DWORD,lParam:DWORD
                jmp     _SendMessageA    ; SendMessageA(x,x,x,x)
MySendMessage       endp

; __stdcall SetMenu(x,x)
MySetMenu           proc hWnd1:DWORD,hMenu:DWORD
                jmp     _SetMenu   ; Assign a new menu to the specified window
MySetMenu           endp

; __stdcall SetWindowLongA(x,x,x)
MySetWindowLong     proc hWnd1:DWORD,nIndex:DWORD,dwNewLong:DWORD
                jmp     _SetWindowLongA    ; SetWindowLongA(x,x,x)
MySetWindowLong     endp

; __stdcall ShowWindow(x,x)
MyShowWindow        proc hWnd1:DWORD,nCmdShow:DWORD
                jmp     _ShowWindow   ; ShowWindow(x,x)
MyShowWindow        endp

; __stdcall TranslateMessage(x)
MyTranslateMessage   proc lpMsg:DWORD
                jmp     _TranslateMessage   ; TranslateMessage(x)
MyTranslateMessage   endp
; __stdcall UpdateWindow(x)
MyUpdateWindow      proc hWnd1:DWORD
                jmp     _UpdateWindow   ; UpdateWindow(x)
MyUpdateWindow      endp

; __stdcall ExitProcess(x)
MyExitProcess       proc uExitCode:DWORD
                jmp     _ExitProcess   ; ExitProcess(x)
MyExitProcess       endp

; __stdcall GetCommandLineA()
MyGetCommandLine    proc
                jmp     _GetCommandLineA   ; GetCommandLineA()
MyGetCommandLine    endp

; __stdcall GetModuleHandleA(x)
MyGetModuleHandle   proc lpModuleName:DWORD
                jmp     _GetModuleHandleA   ; GetModuleHandleA(x)
MyGetModuleHandle   endp

    option prologue:PrologueDef

ALIGN

GetProcbaseAddress proc uses ebx esi FunAddr:DWORD
    .if !FunAddr
        ;如果 FunAddr = 0 随便 Call 一下,pop eax 就是当前的地址
        call $ + 5
        pop eax
    .else
        ;如果是某个函数的调用地址,函数调用地址一般有下面的格式:
        ; __stdcall MessageBoxA(x,x,x,x)
        ; MessageBoxA proc near
        ;FF 25 08 04 40 00     jmp     ds:__imp__MessageBoxA
        ; MessageBoxA endp
        ;其中 FF 25 是 jmp 的操作码,后面的 00400408 是函数调用地址
        ;这样就容易理解下面的代码了。
        mov eax, FunAddr
        mov eax, [eax+2]
        mov eax, [eax]
    .endif
    and eax, 0FFFF0000h
    .repeat
        .if word ptr [eax] != 'ZM'
            xor esi, esi
            sub eax, 1000h

        .else
            mov esi, [eax+IMAGE_DOS_HEADER.e_lfanew]
        .endif
        add esi, eax
    .until dword ptr [esi] == 'EP'

    ret
GetProcbaseAddress  endp
ALIGN
; 将函数名加密
EnCodeAPIName proc  uses ebx edx esi edi, pName:DWORD
    LOCAL NameLen: DWORD

    mov  edi, pName
    xor  al, al

LoopLen:
    scasb
    jnz  LoopLen
    sub  edi, pName
    mov  NameLen, edi
    mov  esi, pName
    cld
    xor  ecx, ecx
    dec  ecx
    mov  edx, ecx

LoopEnCode:
    xor  eax, eax
    xor  ebx, ebx
    lodsb
    xor  al, cl
    mov  cl, ch
    mov  ch, dl
    mov  dl, dh
    mov  dh, 8

loc_10D53D0:
    shr  bx, 1
    rcr  ax, 1
    jnb  loc_10D53E1
    xor  ax, 'PA'
    xor  bx, 'TI'

loc_10D53E1:
    dec  dh
    jnz  loc_10D53D0
    xor  ecx, eax
    xor  edx, ebx
    dec  edi
    jnz  LoopEnCode
    not  edx
    not  ecx
    mov  eax, edx
    rol  eax, 10h
    mov  ax, cx
    mov  ecx, NameLen
    ret

EnCodeAPIName endp


; #########################################################################
WinMain proc hInst     :DWORD,
             hPrevInst :DWORD,
;.... 下面省略原来的程序代码。


; #########################################################################

; 关闭 MASM PROC 宏产生堆栈框架的功能
option PROLOGUE:NONE
option EPILOGUE:NONE
sPoint equ 24h  ;Stack Pointer
PO     equ 1530E38Ah

GetFunctionAddress proc     ;BaseAddress:DWORD, CryptCode:DWORD

        pusha
        mov     eax, 0
        mov     edx, [esp+sPoint+4]         ;CryptCode
        mov     FunctionsCount[ebp+PO], eax
        mov     esi, IMAGE_DOS_HEADER.e_lfanew
        add     esi, [esp+sPoint]           ; + BaseAddress  DosHeader.e_lfanew
        lodsw                               ; 取 PE 头的偏移量
        add     eax, [esp+sPoint]           ; +BaseAddress EAX 中是 PE 头的起始位置
        push    eax
        mov     eax, [eax].IMAGE_NT_HEADERS1.OptionalHeader.DirectoryExport.VirtualAddress

        add     eax, [esp+sPoint+4]         ; + BaseAddress Kernel32.Export.VirtualAddress

        mov     eax, [eax].IMAGE_EXPORT_DIRECTORY.NumberOfNames
        inc     eax
        mov     EXPORTDIRECTORY_NumeberOfName[ebp+PO],  eax
        pop     eax
        mov     esi, [eax].IMAGE_NT_HEADERS1.OptionalHeader.DirectoryExport.VirtualAddress
        add     esi, IMAGE_EXPORT_DIRECTORY.AddressOfFunctions

        add     esi, [esp+sPoint]           ; + BaseAddress
        lea     edi, EXPORTDIRECTORY_AddressOfFunctions[ebp+PO]
        lodsd
        add     eax, [esp+sPoint]           ; + BaseAddress 加上函数的基地址
        stosd
        lea     edi, EXPORTDIRECTORY_AddressOfNames[ebp+PO]
        lodsd                               ; eax = AddressOfNames 的偏移量
        add     eax, [esp+sPoint]           ; BaseAddress 偏移量加基地址
        push    eax                         ; 压栈保存
        stosd
        lea     edi, EXPORTDIRECTORY_AddressOfNamesOrdinals[ebp+PO]
        cld
        lodsd                               ; eax = AddressOfNamesOrdinals 的偏移量
        add     eax, [esp+sPoint+4]         ; + BaseAddress 加基地址得到 NamesOrdinals 的首地址
        stosd
        pop     esi                         ; 前面存在堆栈的 AddressOfNames 地址弹到Esi中

LoopNextName:
        dec     EXPORTDIRECTORY_NumeberOfName[ebp+PO]
        cmp     EXPORTDIRECTORY_NumeberOfName[ebp+PO],  0
        jnz     IfNumberOfNameNotZero
        mov     dword ptr [esp+sPoint-8], 0 ;FunctionAddress, 0
        lea     esi, NoFindDBGBreakPoint
        jmp     esi

IfNumberOfNameNotZero:
        push    esi
        lodsd                               ; eax = AddressOfNames 的偏移量
        add     eax, [esp+sPoint+4]         ; + BaseAddress 加基地址获得 Names 的首地址
        xchg    eax, edi                    ; 将首地址转到 edi
        mov     ebx, edi
        mov     al, APIFirstChar[ebp+PO]
        test    al, al
        jz      loc_10D53B1
        cmp     al, [edi]                   ; 比较函数的首字母
        jnz     loc_10D53FD

loc_10D53B1:
        push    edi
        xor     al, al

loc_10D53B4:
        scasb
        jnz     loc_10D53B4
        pop     esi
        sub     edi, ebx                    ; 得到函数名的长度
        push    edx                         ; 保存入口 位置参数
        cld
        xor     ecx, ecx
        dec     ecx
        mov     edx, ecx
;这里将获得的函数名加密

LoopEnCode:
        xor     eax, eax
        xor     ebx, ebx
        lodsb
        xor     al, cl
        mov     cl, ch
        mov     ch, dl
        mov     dl, dh
        mov     dh, 8

loc_10D53D0:
        shr     bx, 1
        rcr     ax, 1
        jnb     loc_10D53E1
        xor     ax, 'PA'
        xor     bx, 'TI'

loc_10D53E1:
        dec     dh
        jnz     loc_10D53D0
        xor     ecx, eax
        xor     edx, ebx
        dec     edi
        jnz     LoopEnCode
        not     edx
        not     ecx
        mov     eax, edx
        rol     eax, 10h
        mov     ax, cx
        pop     edx                         ; 和入口参数 CryptCode 比较
        cmp     edx, eax                    ; 和加密运算结果比较
        jz      IfEqu

loc_10D53FD:
        pop     esi
        add     esi, 4
        inc     FunctionsCount[ebp+PO]
        jmp     LoopNextName

IfEqu:
        pop     esi
        mov     eax, FunctionsCount[ebp+PO] ; 取出 VirtualAllocEx 的编号,即第几个函数
        shl     eax, 1                      ; 导出函数编号数组的每个编号占两个字节,所以这里将这个
                                            ; FunctionsCount 乘 2
        add     eax, EXPORTDIRECTORY_AddressOfNamesOrdinals[ebp+PO] ;
                                            ; 加上导出函数编号数组的基地址
        sub     esi, esi
        xchg    eax, esi
        lodsw                               ; 获得函数序号
        shl     eax, 2                      ; 序号乘 4 得到函数调用地址位置偏移量
        add     eax, EXPORTDIRECTORY_AddressOfFunctions[ebp+PO] ;
                                            ; 加 AddressOfFunctions 得到函数调用地址偏移量
        xchg    eax, esi                    ; 将地址交换到 esi 寄存器中准备 Load
        lodsd
        add     eax, [esp+sPoint]           ; BaseAddress
        mov     [esp+sPoint-8], eax         ; FunctionAddress, eax  保存结果
        xchg    eax, esi
        lodsb
        xor     cl, cl
        inc     cl
        ; 循环左移检测中断 3 即debug 的断点中断的特征字 0CCh,二进制表示 11001100B
        ; 第一个循环检测高位 0Ch 即 1100b
        ; 第二个循环检查低位 0Ch 即 1100b
LoopCheckINT3:
        rcl     al, 1                       ; 1001100b 如果第 7 位是 0,表示不是断点
        jnb     NoFindDBGBreakPoint

        rcl     al, 1                       ; 001100b  如果第 6 位是 0,表示不是断点
        jnb     NoFindDBGBreakPoint

        rcl     al, 1                       ; 01100b   如果第 5 位是 1,表示不是断点
        jb      NoFindDBGBreakPoint

        rcl     al, 1                       ; 1100b    如果第 4 位是 1,表示不是断点
        jb      NoFindDBGBreakPoint

        dec     cl
        jns     LoopCheckINT3               ; 循环检测


        add     esp, 20h

        mov     dword ptr [esp-4], 0        ; FunctionAddress, 0

        mov     eax, 1
        clc
;--------------------------------
        lea     ecx, FindDebugExit[ebp+PO]
        jmp     ecx                         ; 发现debug从这里退出

NoFindDBGBreakPoint:
        mov     APIFirstChar[ebp+PO], 0     ; 清除为了查询某API函数的第一个字母
;--------------------------------
        popa
        retn    8                           ; 平衡堆栈
option PROLOGUE:PROLOGUEDEF
option EPILOGUE:EPILOGUEDEF
GetFunctionAddress endp

GetCurrAddrEbp Proc
    call $ + 5
    pop ebp
    sub ebp, Offset GetCurrAddrEbp + 5 + PO
    ret
GetCurrAddrEbp EndP

    FunctionsCount                          dd  ?
    EXPORTDIRECTORY_NumeberOfName           dd  ?
    EXPORTDIRECTORY_AddressOfNames          dd  ?
    EXPORTDIRECTORY_AddressOfFunctions      dd  ?
    EXPORTDIRECTORY_AddressOfNamesOrdinals  dd  ?
    APIFirstChar                            db  ?
    EVEN
                                            db 10h dup (90h)

start:
    jmp S1
    ; 这里加了一句永远也不会执行的 API 调用,目的是为了获得 user32.dll 的基地址
    invoke  MessageBox,0,ADDR TheText,ADDR szDisplayName,MB_OK
S1:
    mov  esi, offset szCallWindowProcA
    mov  edi, offset DeCodeCallWindowProcA

    ; 下面两个循环将函数名转成对应的密码,并取函数的第一个字符保存
    .while (byte ptr [esi] != 0)
        mov  al, [esi]
        stosb
        invoke EnCodeAPIName, esi
        add  esi, ecx
        stosd
    .endw

    mov  esi, offset szExitProcess
    mov  edi, offset DeCodeExitProcess
    .while (byte ptr [esi] != 0)
        mov  al, [esi]
        stosb
        invoke EnCodeAPIName, esi
        add  esi, ecx
        stosd
    .endw

    call GetCurrAddrEbp
    mov  eax, offset ExitProcess            ; 取ExitProcess函数地址
    invoke GetProcbaseAddress, eax          ; 获取kernel32.dll基地址
    mov  MyImageBase, eax                   ; 保存

    mov  edx, offset DeCodeExitProcess
    mov  esi, offset OffExitProcess         ; 取kernel32.dll API 函数名引用表的首地址
    mov  edi, offset _ExitProcess           ; 取kernel32.dll 自建输入表首地址
    .while (dword ptr [esi] != 0)
        xchg edx, esi
        lodsb                               ; 取函数名首字母
        mov  APIFirstChar, al               ; 传递隐含参数,函数名首字母
        lodsd                               ; 取函数名密码
        xchg edx, esi
        push eax                            ; 传递函数名密码参数
        mov  eax, MyImageBase
        push eax                            ; 传递基地址参数
        call GetFunctionAddress             ; 调用 Themida 的 GetFunctionAddress
        stosd                               ; 建立kernel32.dll的输入表
        lodsd
    .endw

    mov  eax, offset MessageBox             ; 取MessageBox函数的地址
    invoke GetProcbaseAddress, eax          ; 获取user32.dll基地址
    mov  MyImageBase, eax                   ; 保存

    mov  edx, offset DeCodeCallWindowProcA
    mov  esi, offset OffCallWindowProcA     ; 取user32.dll API 函数名引用表首地址
    mov  edi, offset _CallWindowProcA       ; 取user32.dll 自建输入表首地址
    .while (dword ptr [esi] != 0)
        xchg  edx, esi
        lodsb                               ; 取函数名首字母
        mov  APIFirstChar, al               ; 传递隐含参数,函数名首字母
        lodsd                               ; 取函数名密码
        xchg edx, esi
        push eax                            ; 传递函数名密码参数
        mov  eax, MyImageBase
        push eax                            ; 传递基地址参数
        call GetFunctionAddress             ; 调用 Themida 的 GetFunctionAddress
        stosd                               ; 建立user32.dll的输入表
        lodsd
    .endw

    ;下面是原程序的代码,所有 API 函数都家里 My 前缀
    invoke  MyGetModuleHandle, NULL
    mov  hInstance, eax

    invoke  MyGetCommandLine
    mov  CommandLine, eax
    invoke  WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
    ;这里保留是为了取 Kernel32.dll 的基地址

;    invoke GetModuleHandle, 0
;    mov myhandle, eax
    jmp  ProcExit

FindDebugExit:
    szText  FindTitle, 'Find'
    szText  FindCp, 'Find Debug The Program Exit!!!'
    invoke MessageBox,0, addr FindCp, addr FindTitle, MB_OK
ProcExit:
    invoke ExitProcess,eax

    ret

end start
程序编译时请将地址设在1000000h,即增加 Link 的开关 /BASE:0x1000000,我上传的附件中的exe文件是带有调试符号的,在OD中可以比较容易理解,不过OD对调试符号的支持似乎有些问题。这一点不如SOFTICE。编译后的程序是不能直接运行的,需要修改代码段的读写属性,代码段的读写属性缺省是不可以写入的,Themida 使用的变量是放在代码段的,这样做是为了还原 Themida 的做法。至于怎样修改,你可以随便找一个PE编辑器来完成,也可以在OD中直接修改。

这里简单说明在OD中如何修改:
1、OD 加载 Tfiltinpt.exe。
2、在数据窗口 Ctrl+G ,在打开的小窗口中填入 1000000 (即 ImageBase)。
3、单击鼠标右键,在弹出的菜单中选择 【指定】 -> PE 文件头。
4、Ctrl+B 打开搜索窗口,在ASCII小窗中输入 .text (我们的程序使用.code 宏产生的代码段名为 .text)。
5、现在在数据窗口中看到的是 .text 的 IMAGE_SECTION_HEADER 结构,该结构的最后一个字段为:
   Characteristics 属性,将光标放在 Characteristics 属性上按 Ctrl+E 打开编辑数据窗口,将其中的 60 改成 E0 即在属性 (CODE|EXECUTE|READ|WRITE) 增加了 write 属性。
6、按鼠标右键,在弹出的菜单中选择【复制到可执行文件】,然后在打开的窗口中按鼠标右键,在弹出的菜单中选择【保存文件】,可以选择同名文件覆盖,也可以另外起名保存。

一般的PE编辑器会在PE文件头的后面的空间写入一些垃圾,用OD操作,文件比较干净。

随笔:

我一直有一种责任感,来自于一句话,这句话我曾经在这个论坛的某个跟贴中提过(懒得查),“一个人的责任是多方面的,包括社会责任”。

80年IBM PC机进入中国,就是那种显示绿颜色字符的8086和8088cpu的计算机,在随后的几年中,中国的政府启动了研制自己计算机硬件的计划,具体落实到长城计算机集团,当时公布的数据,长城计算机集团平均每天投资150万元人民币(那时的人民币和现在的人民币的价值是不同的),当时美国的计算机专家就提出,中国应该发展软件,可惜当时的国家的领导人没有战略眼光,将有限的资源全部投入到一个不现实的硬件发展计划中。如果当时以每天投入150万元的力量来培养中国的软件事业,现在世界上可能就没有微软了,最伟大的操作系统完全可能就诞生在中国。这样将形成东软西硬的局面(相当于金庸大侠的东邪西毒),谁也离不了谁(印度政府在这方面的做法就比中国政府科学的多,大家可以到网上去了解印度政府的许多政策,所以今天印度的软件开发在世界所占的比重远超过中国)。

机不可失,时不再来。既然失去了这样一个大好的机会,现在怎么办?这就是我们每个从事 IT 行业的人必须思考的问题。记得20多年前看过的香港电视剧【霍元甲】当中主题歌这样写到:“昏睡百年,国人渐已醒。睁开眼吧,小心看吧,哪个愿臣虏自认...",这是出自以故香港四大才子之一黄沾之手,虽然已过多年,但每当回想起这段歌词就热血沸腾。国人当自强,放开中国人自古传下来的恶习。自古师父教徒弟总是要留一手,这样若干代下来,不是留下许多手失传?如果这样国人如何自强。看雪论坛有许多高手,应该多想想这个问题。

众人拾柴火焰高,只要每个高手有这样一棵心,何怕国人不强?

许多人都知道中国人不能抱团,最喜欢窝里斗,一个人是龙,一群人却成虫,每个中国人都应该反思。

注:修改了一些错笔字,修改和增加了某些注解,增加了随笔,去掉了原来上传附件中的垃圾代码重新上传了附件。
上传的附件 tfiltinpt.rar