查看原文
其他

Themida 2.3.5.5分析

ilyzqe 看雪学院 2019-05-25

这几天在看关于壳子的文章,在这里得感谢qqmcc大佬的两篇文章,链接我放出来。


1、脱壳手记---Themida(1.8.5.5) 

https://bbs.pediy.com/thread-172921.htm


2、脱壳手记---Themida(2.1.2.0)

https://bbs.pediy.com/thread-173013.htm


3、Sure大的链接 ---Themida v2.3.5.10 脱壳教程 by Sure

https://www.xuepojie.com/thread-13373-1-1.html



看了他的这两篇文章,大概就明白了themida保护函数,IAT表的一些手段,最近又逛学破解论坛看到Sure大佬发的一个这个版本的壳子的脚本,感觉强壳的脚本很多但是分析有点少,于是我就手动的分析了一下,在这里发出来算是记录一下吧,要不然还是会丢~


【使用工具】:冷小黑OD


【操作平台】:win xp 虚拟机


关于Themida研究了这三个版本,感觉核心技术就是[esi]表,这个表的大概结构我贴出来:


006F912B 7B 8E 39 F8 2B 04 92 8F          ?拸
006F913B 78 7B 00 20 FF FF FF FF DD DD DD DD 4D 1C DB 55 x{. 葺葺M踀
006F914B 2B 04 92 AF 79 7B 00 C0 FF FF FF FF DD DD DD DD  +挴y{.?葺葺
006F915B 00 78 7A 92 2B 04 92 CF 89 7B 00 20 FF FF FF FF  .xz?捪墈. 
006F916B DD DD DD DD D2 6E 05 7B 2B 04 92 EF 8A 7B 00 60 葺葺襫{+掞妠.`
006F917B  FF FF FF FF DD DD DD DD 4F 1B 2E D7 2C 04 92 0F  葺葺O.??
006F918B  BA 7A 00 A0 FF FF FF FF DD DD DD DD 98 A8 22 73  簔.?葺葺槰"s
006F919B  2C 04 92 2F B2 7A 00 00 FF FF FF FF DD DD DD DD  ,?瞶..葺葺
006F91AB  7E 4F E6 12 2C 04 92 4F B7 7A 00 20 FF FF FF FF  ~O?,扥穤. 
006F91BB DD DD DD DD EE 72 DF 46 2C 04 92 6F 0E 52 00 A0 葺葺顁逨,抩R.
006F91CB FF FF FF FF DD DD DD DD AC 85 52 4B 2C 04 92 8F 葺葺瑓RK,拸
006F91DB 12 52 00 60 FF FF FF FF DD DD DD DD D7 11 33 E0 R.`
葺葺?3
006F91EB 2C 04 92 AF 1E 52 00 60 24 52 00 E0 FF FF FF FF ,挴R.`$R.?
006F91FB DD DD DD DD 38 C8 E1 23 2C 04 92 CF 02 52 00 20 葺葺8柔#,捪R.
006F920B 2E 52 00 00 FF FF FF FF DD DD DD DD 4A 62 49 E1 .R..葺葺JbI
006F921B 2C 04 92 EF 39 53 00 60 FF FF FF FF DD DD DD DD ,掞9S.`
葺葺
006F922B 28 57 A9 3A 2D 04 92 0F FF FF FF FF DD DD DD DD  (W?-?葺葺
006F923B EE EE EE EE 铑铑葺葺


一个DWORD看成是一个元素  这个表中的FF FF FF FF DD DD DD DD看成是一组元素的结尾 而 FF FF FF FF DD DD DD DD EE EE EE EE 是一个表的结束。


一般来说一个[esi]表是有三个可用元素,但是当按照这个步骤进去以后会发现这个[esi]表是有四个元素,程序放到OD里边 在代码段下写入断点到这个循环,然后F7 F8 shift+F9跑起来。


006F72C8    F3:A4           rep movs byte ptr es:[edi],byte ptr ds:[esi]


到这个地方


007095C8 890A mov dword ptr ds:[edx],ecx ; 写入IAT
007095CA 5A pop edx ; 第二个元素还是去IAT填充API这里的IAT 的基址是487600
007095CB F5 cmc
007095CC AD lods dword ptr ds:[esi] ; 取第三个元素
007095CD F9 stc
007095CE 50 push eax ; v2_3_5_1.00487600
007095CF B8 00000000 mov eax,0x0
007095D4 50 push eax ; v2_3_5_1.00487600
007095D5 810424 6B65E465 add dword ptr ss:[esp],0x65E4656B
007095DC 8F46 FC pop dword ptr ds:[esi-0x4]
007095DF 816E FC 6B65E465 sub dword ptr ds:[esi-0x4],0x65E4656B ; 元素3置零
007095E6 58 pop eax ; v2_3_5_1.00487600
007095E7 F8 clc
007095E8 89B5 8C35AD0A mov dword ptr ss:[ebp+0xAAD358C],esi ; 保存当前esi指针位置
007095EE 60 pushad ; 无用代码
007095EF 81C8 4979F419 or eax,0x19F47949
007095F5 66:81E2 EF0D and dx,0xDEF
007095FA 61 popad
007095FB 83F8 FF cmp eax,-0x1 ; 跟FFFFFFFF比较 看看是不是到末尾
007095FE 0F85 B5000000 jnz v2_3_5_1.007096B9


接着按F7单步慢慢走慢慢分析:


007096B9 C1C0 03 rol eax,0x3
007096BC F9 stc
007096BD 0385 550EAD0A add eax,dword ptr ss:[ebp+0xAAD0E55] ; v2_3_5_1.00400000
007096C3 60 pushad ; 拿到偏移+基址
007096C4 60 pushad ; 处理NOP指令


这个地方拿到的基址+偏移是00429C2D,我们到这个位置看一下:


00429C2D 90 nop
00429C2E 90 nop
00429C2F 90 nop
00429C30 90 nop
00429C31 90 nop
00429C32 90 nop


070971C    813E AAAAAAAA   cmp dword ptr ds:[esi],0xAAAAAAAA ; 判断是不是需要处理的FF15数据


这里比较AAAAAAAA需要说明一下,它实际上是TMD判断原来代码中的FF15(“CALL 地址”)和FF25(”JMP 地址”),我们在修复的时候也需要判断,至于判断的方法也不一定要从这里,知道了流程,脚本的方法是很多的。


00709875 B9 057F447D mov ecx,0x7D447F05
0070987A D1E1 shl ecx,1
0070987C 81E1 976FEE6F and ecx,0x6FEE6F97
00709882 F7D1 not ecx ; winmm.midiStreamOut
00709884 F7D1 not ecx ; winmm.midiStreamOut
00709886 81F1 5A19005C xor ecx,0x5C00195A
0070988C 89CB mov ebx,ecx ; winmm.midiStreamOut
0070988E 59 pop ecx ; v2_3_5_1.00709E74
0070988F 81F3 0C5E8200 xor ebx,0x825E0C
00709895 81C3 7B76182A add ebx,0x2A18767B
0070989B C1EB 05 shr ebx,0x5
0070989E 81EB FE140103 sub ebx,0x30114FE
007098A4 89D8 mov eax,ebx
007098A6 5B pop ebx ; 这一段的目的是EAX清零操作



00709997 803F 90 cmp byte ptr ds:[edi],0x90 ; 比较是不是需要处理的NOP指令




00709A75 57 push edi
00709A76 BF B2606917 mov edi,0x176960B2
00709A7B BB B2606917 mov ebx,0x176960B2 ; mov ebx,0
00709A80 29FB sub ebx,edi
00709A82 8B3C24 mov edi,dword ptr ss:[esp] ; pop edi
00709A85 83C4 04 add esp,0x4


00709AB5 83F8 50 cmp eax,0x50 ; 判断是否是需要处理的push指令


00709B0A AA stos byte ptr es:[edi] ; 填充一个E8


00709C26 51 push ecx ; winmm.midiStreamOut
00709C27 B9 04000000 mov ecx,0x4
00709C2C 2D 5614BE78 sub eax,0x78BE1456
00709C31 29C8 sub eax,ecx ; winmm.midiStreamOut
00709C33 05 5614BE78 add eax,0x78BE1456 ; 当前API往后移动四个字节
00709C38 59 pop ecx ; winmm.midiStreamOut


00709C55 AB stos dword ptr es:[edi] ; 将移动后的API地址填充进E8后边 也就是429C2D


00709C91 AD lods dword ptr ds:[esi] ; 取[esi]表中元素 这时候取得是第四个元素
00709C92 0F85 09000000 jnz v2_3_5_1.00709CA1
00709C98 60 pushad
00709C99 F8 clc
00709C9A 0F82 00000000 jb v2_3_5_1.00709CA0
00709CA0 61 popad
00709CA1 52 push edx ; v2_3_5_1.006F90E3
00709CA2 BA FCFFFFFF mov edx,-0x4
00709CA7 01F2 add edx,esi
00709CA9 56 push esi
00709CAA BE 3A0A8D4B mov esi,0x4B8D0A3A
00709CAF C702 3A0A8D4B mov dword ptr ds:[edx],0x4B8D0A3A ; 当前[esi]表清零
00709CB5 3132 xor dword ptr ds:[edx],esi
00709CB7 5E pop esi ; v2_3_5_1.006F90E7


007095E8 89B5 8C35AD0A mov dword ptr ss:[ebp+0xAAD358C],e>; 保存当前esi指针位置
007095EE 60 pushad ; 无用代码
007095EF 81C8 4979F419 or eax,0x19F47949
007095F5 66:81E2 EF0D and dx,0xDEF
007095FA 61 popad
007095FB 83F8 FF cmp eax,-0x1 ; 跟FFFFFFFF比较 看看是不是到末尾
007095FE 0F85 B5000000 jnz v2_3_5_1.007096B9


007096B9 C1C0 03 rol eax,0x3 ; 第三个元素左移拿到的是一个偏移值
007096BC F9 stc
007096BD 0385 550EAD0A add eax,dword ptr ss:[ebp+0xAAD0E55] ; v2_3_5_1.00400000
007096C3 60 pushad ; 拿到偏移+基址
007096C4 60 pushad ; 处理NOP指令


第四个元素发现又把之前填充NOP的代码重新执行了一遍,看一下现在的偏移是00429EB5 而之前的是00429C2D。我们到这个429EB5看一下,发现是:


00429EB0 8B56 1C mov edx,dword ptr ds:[esi+0x1C]
00429EB3 51 push ecx ; winmm.midiStreamOut
00429EB4 52 push edx
00429EB5 90 nop
00429EB6 90 nop
00429EB7 90 nop
00429EB8 90 nop
00429EB9 90 nop
00429EBA 90 nop
00429EBB 3BC7 cmp eax,edi ; v2_3_5_1.00429C32


而且当前寄存器存放的API地址还是这个winmm.midiStreamOut既然他是同样的处理流程就可以猜测这个参数的作用就是把相同API的调用的地方再去处理一下。


我们去之前的4429C2D看一下是不是调用的相同的API:


00429C2D E8 BC087076 call winmm.midiStreamOut


结果发现是这个API 那我们就放着这个流程执行,看一下填充完成以后是不是同样的API:


00429EB3 51 push ecx ; winmm.76B20633
00429EB4 52 push edx
00429EB5 90 nop
00429EB6 E8 33067076 call winmm.midiStreamOut
00429EBB 3BC7 cmp eax,edi ; v2_3_5_1.00429EBB


结果发现是填充了相同的API,如果把一组[ESI]表的规定为三个有效参数那么多出来的就是去相同的调用的地方填充同一个参数。


接着就是到了这一组[esi]的结尾了


00709C91 AD lods dword ptr ds:[esi] ; 取[esi]表中元素 这时候取得是第四个元素
00709C92 0F85 09000000 jnz v2_3_5_1.00709CA1

同样取完以后清零
00709CAA BE 3A0A8D4B mov esi,0x4B8D0A3A
00709CAF C702 3A0A8D4B mov dword ptr ds:[edx],0x4B8D0A3A ; 当前[esi]表清零
00709CB5 3132 xor dword ptr ds:[edx],esi ; v2_3_5_1.006F90EB


当前取出来的是FFFFFFFF 意味着到了结束了

007095E8 89B5 8C35AD0A mov dword ptr ss:[ebp+0xAAD358C],esi ; 保存当前esi指针位置
007095EE 60 pushad ; 无用代码
007095EF 81C8 4979F419 or eax,0x19F47949
007095F5 66:81E2 EF0D and dx,0xDEF
007095FA 61 popad
007095FB 83F8 FF cmp eax,-0x1 ; 跟FFFFFFFF比较 看看是不是到末尾
007095FE 0F85 B5000000 jnz v2_3_5_1.007096B9
00709604 60 pushad
00709605 60 pushad
00709606 66:81EB 8AAD sub bx,0xAD8A
0070960B B8 07DC1C28 mov eax,0x281CDC07
00709610 61 popad
00709611 0F88 05000000 js v2_3_5_1.0070961C
00709617 66:81DB C08D sbb bx,0x8DC0
0070961C 61 popad
0070961D 813E DDDDDDDD cmp dword ptr ds:[esi],0xDDDDDDDD ; 比较当前的值是否是DDDD

如果取出来的数据是FFFFFFF 而且他的下一个是DDDDDDDD 那么就是当前这一组[esi]数据处理完成,单步走就会来到循环尾了

0070969C 89B5 8C35AD0A mov dword ptr ss:[ebp+0xAAD358C],esi ; 保存当前ESI地址
007096A2 FC cld
007096A3 ^ E9 7ACAFFFF jmp v2_3_5_1.00706122 ; 循环尾



00706122 C785 0439AD0A 000000>mov dword ptr ss:[ebp+0xAAD3904],0x0 ; 循环头
0070612C C785 C438AD0A 000000>mov dword ptr ss:[ebp+0xAAD38C4],0x0
00706136 83BD BE1ACD0A 00 cmp dword ptr ss:[ebp+0xACD1ABE],0x0
0070613D 74 06 je short v2_3_5_1.00706145
0070613F FF95 371EAD0A call dword ptr ss:[ebp+0xAAD1E37] ; v2_3_5_1.006D2F98
00706145 FF85 F116AD0A inc dword ptr ss:[ebp+0xAAD16F1]
0070614B 0F8F 08000000 jg v2_3_5_1.00706159
00706151 0F85 02000000 jnz v2_3_5_1.00706159
00706157 60 pushad
00706158 61 popad
00706159 0F88 01000000 js v2_3_5_1.00706160
0070615F F9 stc
00706160 83BD F116AD0A 64 cmp dword ptr ss:[ebp+0xAAD16F1],0x64 ; 比较导入模块是否是大于0x64大于64进行校验



关于这个校验qqmcc大佬讲的很明白了,就不过多赘述了,接着往下看

0070634B AD lods dword ptr ds:[esi] ; 取第一个元素当前的这一组数据中可用参数是三个
0070634C F5 cmc
0070634D 89B5 8C35AD0A mov dword ptr ss:[ebp+0xAAD358C],esi ; 保存当前[esi]指针位置
00706353 60 pushad
00706354 8BF0 mov esi,eax
00706356 66:81F2 0895 xor dx,0x9508
0070635B 61 popad
0070635C E9 08000000 jmp v2_3_5_1.00706369
00706361 26:9c pushfd
00706363 9F lahf
00706364 DBBD B376F953 fstp tbyte ptr ss:[ebp+0x53F976B3]
0070636A 68 C1049676 push 0x769604C1
0070636F 5B pop ebx ; mov ebx,0x769604C1
00706370 81F3 25489B1D xor ebx,0x1D9B4825
00706376 81C3 3775006E add ebx,0x6E007537
0070637C 81F3 1BC20DD9 xor ebx,0xD90DC21B ; 第一个元素置零
00706382 895E FC mov dword ptr ds:[esi-0x4],ebx ; mov dword ptr ds:[esi-0x4],0

00706398 3D EEEEEEEE cmp eax,0xEEEEEEEE ; 判断是否是处理完一个[esi]
0070639D 0F85 92000000 jnz v2_3_5_1.00706435


007064CD 899D EB76C30A mov dword ptr ss:[ebp+0xAC376EB],ebx ; 保存元素


接下来就是[esi]表中的第一个元素的使用,他的作用是在一个数组中找到对应的数据,然后记录下表用来作为偏移莱寻找制定的API。


007067CA 8B95 CC38AD0A mov edx,dword ptr ss:[ebp+0xAAD38CC] ; 2A40000出现一个基址
007067D0 60 pushad
007067D1 80D7 00 adc bh,0x0
007067D4 80C5 80 add ch,0x80
007067D7 61 popad
007067D8 3B02 cmp eax,dword ptr ds:[edx] ; 出现一个数组了 用莱寻找偏移
007067DA 0F84 BA000000 je v2_3_5_1.0070689A
007067E0 |0F8B 01000000 jpo v2_3_5_1.007067E7
007067E6 |
F9 stc
007067E7 |55 push ebp
007067E8 |
57 push edi ; v2_3_5_1.00429EBB
007067E9 |BF 3E354E70 mov edi,0x704E353E
007067EE |
81C7 011C8544 add edi,0x44851C01
007067F4 |81CF B4070A6B or edi,0x6B0A07B4
007067FA |
81EF BB57DBFF sub edi,0xFFDB57BB
00706800 |89FD mov ebp,edi ; v2_3_5_1.00429EBB
00706802 |
5F pop edi ; 数组指针位置+4
00706803 |01EA add edx,ebp
00706805 |
5D pop ebp

00706818 F8 clc
00706819 52 push edx
0070681A BA 32643D47 mov edx,0x473D6432
0070681F 81C2 037DDE45 add edx,0x45DE7D03
00706825 81E2 1840E15C and edx,0x5CE14018
0070682B 81F2 1140010C xor edx,0xC014011
00706831 01D1 add ecx,edx ; inc ecx
00706833 5A pop edx ; 02A40004


0070683B 3B8D A87FC10A cmp ecx,dword ptr ss:[ebp+0xAC17FA8] ; 数组长度是D1

其实这一段真正有用的代码我提取一下就是下边这几句

mov edx,dword ptr ss:[ebp+0xAAD38CC]
add edx,0x4
inc ecx
cmp ecx,dword ptr ss:[ebp+0xAC17FA8]


我们跑出这个循环在这下个断点
0070689A     898D 5037AD0A        mov dword ptr ss:[ebp+0xAAD3750],ecx ; 将寻找到的偏移存放在一个局部变量


007068CE C785 B31BCD0A 000000>mov dword ptr ss:[ebp+0xACD1BB3],0x0
007068D8 B9 01000000 mov ecx,0x1
007068DD 85C9 test ecx,ecx
007068DF 74 51 je short v2_3_5_1.00706932 ; 这个地方有个3F0000 数据窗口跟一下会发现是所有导入模块的基址
007068E1 8B8D 7527AD0A mov ecx,dword ptr ss:[ebp+0xAAD2775]
007068E7 8B09 mov ecx,dword ptr ds:[ecx] ; 开始比较基址
007068E9 3B8D 471AAD0A cmp ecx,dword ptr ss:[ebp+0xAAD1A47] ; user32.77D10000
007068EF 74 10 je short v2_3_5_1.00706901
007068F1 3B8D AB26AD0A cmp ecx,dword ptr ss:[ebp+0xAAD26AB] ; advapi32.77DA0000
007068F7 74 08 je short v2_3_5_1.00706901
007068F9 3B8D 2D17AD0A cmp ecx,dword ptr ss:[ebp+0xAAD172D] ; kernel32.7C800000


数据窗口内容:


003F0000 00 00 B1 76 00 00 A2 71 00 00 80 7C 00 00 D1 77 ..眝....€|..褀
003F0010 00 00 EF 77 00 00 F7 72 00 00 DA 77 00 00 59 7D ..飛..鱮..趙..Y}
003F0020 00 00 99 76 00 00 0F 77 00 00 17 5D 00 00 32 76 ..檝..w..]..2v


007069D6 8B85 5037AD0A mov eax,dword ptr ss:[ebp+0xAAD3750] ; 用到了ecx存放的偏移
007069DC FC cld
007069DD D1E0 shl eax,1
007069DF 0F89 09000000 jns v2_3_5_1.007069EE
007069E5 60 pushad
007069E6 66:8BD8 mov bx,ax
007069E9 66:BF 6693 mov di,0x9366
007069ED 61 popad
007069EE 0385 FC57C70A add eax,dword ptr ss:[ebp+0xAC757FC] ; 这里边存放的是对应模块的基址这个地方应该是定位的对应模块的导出表
007069F4 0F8B 1B000000 jpo v2_3_5_1.00706A15



00706A4A 31F0 xor eax,esi ; winmm.76B11AD6
00706A4C 31C6 xor esi,eax
00706A4E 31F0 xor eax,esi ; xchg eax,esi
00706A50 E9 0A000000 jmp v2_3_5_1.00706A5F
00706A55 4A dec edx
00706A56 AC lods byte ptr ds:[esi]
00706A57 71 07 jno short v2_3_5_1.00706A60
00706A59 93 xchg eax,ebx
00706A5A ^ 75 CA jnz short v2_3_5_1.00706A26
00706A5C 86D3 xchg bl,dl
00706A5E 630F arpl word ptr ds:[edi],cx
00706A60 8311 00 adc dword ptr ds:[ecx],0x0
00706A63 0000 add byte ptr ds:[eax],al
00706A65 0F8F 0B000000 jg v2_3_5_1.00706A76
00706A6B E9 06000000 jmp v2_3_5_1.00706A76
00706A70 3e:be cca462de mov esi,0xde62a4cc
00706A76 66:AD lods word ptr ds:[esi] ; 拿到导出函数序号


00706A90 C1E0 02 shl eax,0x2
00706A93 60 pushad
00706A94 80D2 C7 adc dl,0xC7
00706A97 B4 FE mov ah,0xFE
00706A99 61 popad
00706A9A 0385 49F7C70A add eax,dword ptr ss:[ebp+0xAC7F749] ; winmm.76B11384
00706AA0 FC cld
00706AA1 50 push eax
00706AA2 81EC 04000000 sub esp,0x4
00706AA8 893424 mov dword ptr ss:[esp],esi ; winmm.76B11518
00706AAB 58 pop eax ; v2_3_5_1.006F90F3
00706AAC 5E pop esi ; v2_3_5_1.006F90F3
00706AAD 0F8F 14000000 jg v2_3_5_1.00706AC7 ; xchg eax,esi


00706AC7 AD lods dword ptr ds:[esi] ; 根据导出序号拿到了API得偏移


00706AED 81C1 8FD0C722 add ecx,0x22C7D08F
00706AF3 5F pop edi ; v2_3_5_1.006F90F3
00706AF4 61 popad
00706AF5 0F88 01000000 js v2_3_5_1.00706AFC
00706AFB F8 clc
00706AFC 05 C2237533 add eax,0x337523C2
00706B01 2D CB28C32D sub eax,0x2DC328CB
00706B06 01C8 add eax,ecx ; winmm.76B10000
00706B08 05 CB28C32D add eax,0x2DC328CB
00706B0D 2D C2237533 sub eax,0x337523C2
00706B12 F9 stc ; 这一大段的意思是去现在装载的模块中根据导出序号找到偏移然后找到函数
00706B13 8138 E8000000 cmp dword ptr ds:[eax],0xE8 ; 比较开头是不是E8


00706C77 8138 6970686C cmp dword ptr ds:[eax],0x6C687069 ; 比较的硬编码

往下会有这么几句指令我把它摘出来
00706DBC 8B9D 2D17AD0A mov ebx,dword ptr ss:[ebp+0xAAD172D] ; kernel32.7C800000
00706DDD 29CB sub ebx,ecx ; winmm.76B10000

这样的指令应该是检测模块位置是否填充正确 我对这个也不确定因为这些都不影响脱壳


00706E6C 8D9D 608ECD0A lea ebx,dword ptr ss:[ebp+0xACD8E60]
00706E72 E9 08000000 jmp v2_3_5_1.00706E7F
00706E77 76 51 jbe short v2_3_5_1.00706ECA
00706E79 006F 00 add byte ptr ds:[edi],ch
00706E7C D216 rcl byte ptr ds:[esi],cl
00706E7E 46 inc esi ; v2_3_5_1.006F90F3
00706E7F FFD3 call ebx ; 通过这个call 确定API的正确性
00706E81 60 pushad ; 它是通过判断函数头几个字节

00706EA1 61 popad ; 覆盖存放的上一个API地址
00706EA2 8985 C426AD0A mov dword ptr ss:[ebp+0xAAD26C4],eax ; winmm.midiOutPrepareHeader


这时候取得是第二个元素:


00709452 AD lods dword ptr ds:[esi] ; 取第二个元素

0070946C 893C24 mov dword ptr ss:[esp],edi
0070946F BF 6D4D6663 mov edi,0x63664D6D
00709474 81E7 EE30706C and edi,0x6C7030EE
0070947A C1E7 08 shl edi,0x8
0070947D 81C7 C779DE03 add edi,0x3DE79C7
00709483 81F7 C7E5DE63 xor edi,0x63DEE5C7
00709489 897E FC mov dword ptr ds:[esi-0x4],edi ; 第二个元素清零
0070948C 5F pop edi ; winmm.midiOutPrepareHeader



0070956B 51 push ecx ; winmm.midiOutPrepareHeader
0070956C B9 9FF0C70D mov ecx,0xDC7F09F
00709571 05 E22F547B add eax,0x7B542FE2
00709576 01C8 add eax,ecx ; winmm.midiOutPrepareHeader
00709578 2D E22F547B sub eax,0x7B542FE2
0070957D 59 pop ecx ; 这一段执行完成就会拿到IAT的偏移
0070957E 0F8A 05000000 jpe v2_3_5_1.00709589
00709584 60 pushad
00709585 F9 stc
00709586 60 pushad
00709587 61 popad
00709588 61 popad
00709589 0385 550EAD0A add eax,dword ptr ss:[ebp+0xAAD0E55] ; 拿到代码段中IAT的基址+偏移
0070958F FC cld
00709590 8B8D C426AD0A mov ecx,dword ptr ss:[ebp+0xAAD26C4] ; winmm.midiOutPrepareHeader

007095BB 83EC 04 sub esp,0x4
007095BE 891424 mov dword ptr ss:[esp],edx ; v2_3_5_1.00487604
007095C1 BA 00000000 mov edx,0x0
007095C6 01C2 add edx,eax ; v2_3_5_1.00487604
007095C8 890A mov dword ptr ds:[edx],ecx ; winmm.midiOutPrepareHeader
007095CA 5A pop edx ; 第二个元素还是去IAT填充API这里的IAT 的基址是487600


到这里就跟文章头部呼应起来了,大体流程就是这些,这样一份Themida加密IAT得流程就分析完成了。


当我们拉到下边的找到最后一个EEEEEEEEDDDDDDD结构下一个内存访问断点一直单步到这个地方:


00706420 /E9 BF380000 jmp v2_3_5_1.00709CE4 ; 运行到这可以直接代码段下内存访问断点到达OEP


代码段内存访问断点以后shift+F9,到这:


直接就到了OEP
00466C98 55 push ebp
00466C99 8BEC mov ebp,esp
00466C9B 6A FF push -0x1
00466C9D 68 283A4B00 push v2_3_5_1.004B3A28
00466CA2 68 749A4600 push v2_3_5_1.00469A74
00466CA7 64:A1 00000000 mov eax,dword ptr fs:[0]
00466CAD 50 push eax
00466CAE 64:8925 00000000 mov dword ptr fs:[0],esp
00466CB5 83EC 58 sub esp,0x58
00466CB8 53 push ebx ; v2_3_5_1.00708D28
00466CB9 56 push esi ; v2_3_5_1.0070ACF3
00466CBA 57 push edi


但是这个Sure大可能是没有加虚拟机选项之类的保护,也没有StolenCode所以脱壳还是比较轻松的,到这里手动拖也可以,脚本脱也行了就不赘述了。


最后我只放上Sure大的加壳程序,毕竟是从人家那里学的还是要尊重原创嘛。


因为还不是很会写脚本写出来烂乎乎的,还是用的Sure大的。


最后就是最近看这三个版本的THemida感觉对于TMD来说不去看Anti之类的东西核心技术好像就是这个[esi]表了,掌握了这个表,对于脱壳来说是事半功倍的。


如有不对,望指正。




- End -



看雪ID:ilyzqe           

https://bbs.pediy.com/user-774936.htm



本文由看雪论坛 ilyzqe 原创

转载请注明来自看雪社区




热门图书推荐

立即购买!




热门文章阅读

1、smali学习笔记之四种方法破解一个简单creakme

2、IIS6.0缓冲区溢出漏洞深度分析(CVE-2017-7269)

3、Linux内核fuzz技术——trinity




公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com



↙点击下方“阅读原文”

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存