ASPR学习笔记(二):还原跳转表 作者:lelfei
这一个星期都在弄这个ASPR壳,弄的头晕眼花的。。。还是上次的目标AllNetic Working Time Tracker - Version 2.2,好像是一个工作时间安排的工具吧?
下载地址是http://www.allnetic.com/
壳的版本:
PEiD扫描:ASProtect 2.1x SKE -> Alexey Solodovnikov
使用verA0.13插件扫描:Version: ASProtect 1.35 build 04.25 Release
请关注上一节:ASPR学习笔记(一):修复IAThttp://bbs.unpack.cn/viewthread.php?tid=31713
在上一节修复完IAT后可以看出OEP=289BE0,RVA=29CB98,SIZE=9B8
修复IAT还不能算完成,因为ASPR把程序中的跳转表JMP 替换成了CALL 01410000这类壳中调用了,还需要把CALL还原成JMP。
先看看壳是怎么把JMP替换成CALL的?
替换CALL XXXXXXXX时同样也会修改程序代码数据,到达OEP在程序开始部分的跳转表中随便找个CALL 01410000,比如:
00401288 call 01410000
在地址部分下硬件断点hw 00401289,重新载入程序后运行,经过几次初始化中断之后就来到了替换CALL的循环。
这一段是生成基本数据并修正所有CALL地址:
01262440 55 push ebp
01262441 8BEC mov ebp, esp
01262443 53 push ebx
01262444 8B5D 08 mov ebx, dword ptr
01262447 EB 01 jmp short 0126244A
0126244A 85DB test ebx, ebx
0126244C 74 2A je short 01262478
0126244E 8BC3 mov eax, ebx
01262450 E8 4FE5FFFF call 012609A4 ; 生成基本数据
01262455 84C0 test al, al
01262457 75 0A jnz short 01262463 ; 生成出错?
01262459 68 88242601 push 1262488 ; ASCII "107",CR,LF
0126245E E8 812DFFFF call 012551E4 ; 错误提示并退出
01262463 8BC3 mov eax, ebx
01262465 E8 36000000 call 012624A0 ; 修正所有跳转表地址
0126246A 84C0 test al, al
0126246C 75 0A jnz short 01262478 ; 修正出错?
0126246E 68 98242601 push 1262498 ; ASCII "108",CR,LF
01262473 E8 6C2DFFFF call 012551E4 ; 错误提示并退出
01262478 5B pop ebx
01262479 5D pop ebp
0126247A C2 0400 retn 4
其中call 012609A4生成修正跳转表所用的基本数据:
012609A4 53 push ebx
012609A5 56 push esi
012609A6 57 push edi
012609A7 55 push ebp
012609A8 83C4 F8 add esp, -8
012609AB 8BF8 mov edi, eax
012609AD C60424 00 mov byte ptr , 0
012609B1 837F 08 00 cmp dword ptr , 0
012609B5 0F84 DF010000 je 01260B9A
012609BB 8D47 5C lea eax, dword ptr
012609BE BA 0A000000 mov edx, 0A
012609C3 E8 FC27FFFF call 012531C4 ; 初始化随机序列数据
012609C8 8D47 5C lea eax, dword ptr
012609CB BA 0A000000 mov edx, 0A
012609D0 E8 A327FFFF call 01253178 ; 生成随机序列保存到
012609D5 33ED xor ebp, ebp
012609D7 8A442F 5C mov al, byte ptr
012609DB 8D57 40 lea edx, dword ptr
012609DE 03D5 add edx, ebp
012609E0 8802 mov byte ptr , al
012609E2 45 inc ebp
012609E3 83FD 0A cmp ebp, 0A
012609E6^ 75 EF jnz short 012609D7 ; copy随机序列到
012609E8 8B77 08 mov esi, dword ptr
012609EB BD 0A000000 mov ebp, 0A
012609F0 8A1E mov bl, byte ptr ; 取index
012609F2 46 inc esi
012609F3 80FB 09 cmp bl, 9
012609F6 0F87 9E010000 ja 01260B9A
012609FC 8BD3 mov edx, ebx
012609FE 8BC7 mov eax, edi
01260A00 E8 77FFFFFF call 0126097C ; 取index所在的随机值
01260A05 8BD8 mov ebx, eax
01260A07 80FB FF cmp bl, 0FF ; 随机序列是否正确?
01260A0A 75 0A jnz short 01260A16
01260A0C 68 AC0B2601 push 1260BAC ; ASCII "107",CR,LF
01260A11 E8 CE47FFFF call 012551E4
01260A16 8B06 mov eax, dword ptr
01260A18 83C6 04 add esi, 4
01260A1B 33D2 xor edx, edx
01260A1D 8AD3 mov dl, bl
01260A1F 8D1452 lea edx, dword ptr
01260A22 894497 6C mov dword ptr , eax
01260A26 3D 00200000 cmp eax, 2000
01260A2B 0F87 69010000 ja 01260B9A
01260A31 8B16 mov edx, dword ptr
01260A33 895424 04 mov dword ptr , edx
01260A37 83C6 04 add esi, 4
01260A3A 33D2 xor edx, edx
01260A3C 8AD3 mov dl, bl
01260A3E 8D1452 lea edx, dword ptr
01260A41 8B4C24 04 mov ecx, dword ptr
01260A45 894C97 70 mov dword ptr , ecx
01260A49 33D2 xor edx, edx
01260A4B 8AD3 mov dl, bl
01260A4D 8D1452 lea edx, dword ptr
01260A50 897497 68 mov dword ptr , esi
01260A54 03F0 add esi, eax
01260A56 4D dec ebp
01260A57^ 75 97 jnz short 012609F0 ; 循环生成函数随机入口
01260A59 8D47 4A lea eax, dword ptr
01260A5C B9 09000000 mov ecx, 9
01260A61 8BD6 mov edx, esi
01260A63 E8 2C4FFEFF call <CopyStringByByte> ; 复制9字节到
01260A68 83C6 09 add esi, 9
01260A6B 8A06 mov al, byte ptr
01260A6D 8847 20 mov byte ptr , al ; 标志位
01260A70 46 inc esi
01260A71 8B06 mov eax, dword ptr
01260A73 8987 E4000000 mov dword ptr , eax ; 数据组的长度
01260A79 83C6 04 add esi, 4
01260A7C 8B06 mov eax, dword ptr
01260A7E 8987 E0000000 mov dword ptr , eax ; 待修正代码的偏移
01260A84 83C6 04 add esi, 4
01260A87 BA 54652601 mov edx, 1266554
01260A8C 8902 mov dword ptr , eax
01260A8E 8B06 mov eax, dword ptr
01260A90 8947 18 mov dword ptr , eax ; 待修正代码的数据组的数量
01260A93 83C6 04 add esi, 4
01260A96 837F 2C 00 cmp dword ptr , 0 ; 生成4个新段并把入口放入开始的位置中
01260A9A 74 0D je short 01260AA9 ; 段1是否已经生成?
01260A9C 8D47 2C lea eax, dword ptr
01260A9F E8 0C99FFFF call 0125A3B0
01260AA4 33C0 xor eax, eax
01260AA6 8947 2C mov dword ptr , eax
01260AA9 8BC7 mov eax, edi
01260AAB E8 74F6FFFF call 01260124 ; 生成段1并生成代码数据
01260AB0 8947 2C mov dword ptr , eax ; 保存段1入口地址
01260AB3 837F 30 00 cmp dword ptr , 0
01260AB7 74 0D je short 01260AC6 ; 段2是否已经生成?
01260AB9 8D47 30 lea eax, dword ptr
01260ABC E8 EF98FFFF call 0125A3B0
01260AC1 33C0 xor eax, eax
01260AC3 8947 30 mov dword ptr , eax
01260AC6 8BC7 mov eax, edi
01260AC8 E8 87F9FFFF call 01260454 ; 生成段2并生成代码数据
01260ACD 8947 30 mov dword ptr , eax ; 保存段2入口地址
01260AD0 837F 34 00 cmp dword ptr , 0
01260AD4 74 0D je short 01260AE3 ; 段3是否已经生成?
01260AD6 8D47 34 lea eax, dword ptr
01260AD9 E8 D298FFFF call 0125A3B0
01260ADE 33C0 xor eax, eax
01260AE0 8947 34 mov dword ptr , eax
01260AE3 8BC7 mov eax, edi
01260AE5 E8 4AFBFFFF call 01260634 ; 生成段3并生成代码数据
01260AEA 8947 34 mov dword ptr , eax ; 保存段3入口地址
01260AED 837F 38 00 cmp dword ptr , 0
01260AF1 74 0D je short 01260B00 ; 段4是否已经生成?
01260AF3 8D47 38 lea eax, dword ptr
01260AF6 E8 B598FFFF call 0125A3B0
01260AFB 33C0 xor eax, eax
01260AFD 8947 38 mov dword ptr , eax
01260B00 8BC7 mov eax, edi
01260B02 E8 D1FCFFFF call 012607D8 ; 生成段4并生成代码数据
01260B07 8947 38 mov dword ptr , eax ; 保存段4入口地址
01260B0A 8977 54 mov dword ptr , esi ; 修正跳转表所用的数据组基址
01260B0D 8B47 18 mov eax, dword ptr
01260B10 F7AF E4000000 imul dword ptr ; 数据组数量*数据组长度
01260B16 03F0 add esi, eax
01260B18 8B06 mov eax, dword ptr
01260B1A 8947 1C mov dword ptr , eax ; 0
01260B1D 83C6 04 add esi, 4
01260B20 8977 58 mov dword ptr , esi ; 数据组的结束地址
01260B23 8B87 E0000000 mov eax, dword ptr
01260B29 50 push eax
01260B2A 68 00030000 push 300
01260B2F 8B47 2C mov eax, dword ptr
01260B32 50 push eax
01260B33 57 push edi
01260B34 E8 DF010000 call 01260D18 ; hash(段1,300)
01260B39 8BD8 mov ebx, eax
01260B3B 899F F0000000 mov dword ptr , ebx ; save hash
01260B41 53 push ebx
01260B42 68 00030000 push 300
01260B47 8B47 30 mov eax, dword ptr
01260B4A 50 push eax
01260B4B 57 push edi
01260B4C E8 C7010000 call 01260D18 ; hash(段2,300)
01260B51 8BF0 mov esi, eax
01260B53 89B7 F4000000 mov dword ptr , esi ; save hash
01260B59 56 push esi
01260B5A 68 00030000 push 300
01260B5F 8B47 34 mov eax, dword ptr
01260B62 50 push eax
01260B63 57 push edi
01260B64 E8 AF010000 call 01260D18 ; hash(段3,300)
01260B69 8BD8 mov ebx, eax
01260B6B 899F F8000000 mov dword ptr , ebx ; save hash
01260B71 53 push ebx
01260B72 68 00030000 push 300
01260B77 8B47 38 mov eax, dword ptr
01260B7A 50 push eax
01260B7B 57 push edi
01260B7C E8 97010000 call 01260D18 ; hash(段4,300)
01260B81 8BF0 mov esi, eax
01260B83 89B7 FC000000 mov dword ptr , esi ; save hash
01260B89 56 push esi
01260B8A 57 push edi
01260B8B E8 3C1B0000 call 012626CC ; hash(数据组)
01260B90 8987 04010000 mov dword ptr , eax ; save hash
01260B96 C60424 01 mov byte ptr , 1
01260B9A 8A0424 mov al, byte ptr
01260B9D 59 pop ecx
01260B9E 5A pop edx
01260B9F 5D pop ebp
01260BA0 5F pop edi
01260BA1 5E pop esi
01260BA2 5B pop ebx
01260BA3 C3 retn
call 012624A0修正所有跳转表地址:
012624A0 53 push ebx
012624A1 56 push esi
012624A2 57 push edi
012624A3 55 push ebp
012624A4 83C4 DC add esp, -24
012624A7 8BD8 mov ebx, eax
012624A9 33C0 xor eax, eax
012624AB 8B53 54 mov edx, dword ptr
012624AE 85D2 test edx, edx
012624B0 0F84 0D020000 je 012626C3
012624B6 83BB E4000000 0>cmp dword ptr , 0
012624BD 0F84 00020000 je 012626C3
012624C3 837B 18 00 cmp dword ptr , 0
012624C7 0F84 F6010000 je 012626C3
012624CD 837B 24 00 cmp dword ptr , 0
012624D1 0F84 EC010000 je 012626C3
012624D7 837B 2C 00 cmp dword ptr , 0
012624DB 0F84 E2010000 je 012626C3
012624E1 837B 30 00 cmp dword ptr , 0
012624E5 0F84 D8010000 je 012626C3
012624EB 8BF2 mov esi, edx
012624ED 8B43 18 mov eax, dword ptr
012624F0 890424 mov dword ptr , eax
012624F3 8B83 E0000000 mov eax, dword ptr
012624F9 894424 14 mov dword ptr , eax
012624FD 8D7B 40 lea edi, dword ptr
01262500 833C24 00 cmp dword ptr , 0
01262504 0F86 AB010000 jbe 012626B5
0126250A 33C0 xor eax, eax
0126250C 8A07 mov al, byte ptr ; 取函数入口index0
0126250E 8D0440 lea eax, dword ptr
01262511 8B6C83 68 mov ebp, dword ptr ; 函数入口:
01262515 8BC6 mov eax, esi
01262517 FFD5 call ebp ; 函数功能:mov eax,
01262519 8BE8 mov ebp, eax ; 取出的值为待修正地址的偏移
0126251B 036B 24 add ebp, dword ptr ; +基址
0126251E 03AB E0000000 add ebp, dword ptr ; +段偏移
01262524 EB 01 jmp short 01262527 ; 结果为待修正的地址入口
01262526 90 nop
01262527 33C0 xor eax, eax
01262529 8A47 09 mov al, byte ptr ; 取函数入口index9
0126252C 8D0440 lea eax, dword ptr
0126252F 8B5483 68 mov edx, dword ptr ; 函数入口:
01262533 8BC6 mov eax, esi
01262535 FFD2 call edx ; 函数功能:mov eax,byte ptr
01262537 807B 20 00 cmp byte ptr , 0 ; 取出的值为待修正地址的标志位
0126253B 0F85 3D010000 jnz 0126267E ; 总标志位:是否进行修正?
01262541 3C 01 cmp al, 1
01262543 0F85 35010000 jnz 0126267E ; 数据组标志位:是否进行修正?
0126267E 8B43 2C mov eax, dword ptr ; 取call XXXXXXXX的地址值=01410000
01262681 2BC5 sub eax, ebp ; WorkingT.00401268
01262683 83E8 05 sub eax, 5 ; 计算相对偏移
01262686 45 inc ebp
01262687 8945 00 mov dword ptr , eax ; !!!!!!!!!!!将jmp XXXXXXXX改为call 01410000写到程序中
0126268A 6A 0A push 0A
0126268C E8 1309FFFF call 01252FA4 ; rnd 计算出一个随机值
01262691 8BC8 mov ecx, eax
01262693 038B E4000000 add ecx, dword ptr ; 复制的长度:随机多复制几个字节
01262699 8BD6 mov edx, esi
0126269B 8BC3 mov eax, ebx
0126269D E8 BAE6FFFF call 01260D5C ; copy一段代码到一个段中
012626A2 FF0C24 dec dword ptr ; 循环次数-1
012626A5 03B3 E4000000 add esi, dword ptr ; +数据组长度:取下一个数据组
012626AB 833C24 00 cmp dword ptr , 0
012626AF^ 0F87 55FEFFFF ja 0126250A ; 继续循环
012626B5 53 push ebx
012626B6 E8 5D000000 call 01262718 ; 校验hash:是否修改代码?
012626BB 0183 EC000000 add dword ptr , eax ; 校验的段:
012626C1 B0 01 mov al, 1 ; 段1
012626C3 83C4 24 add esp, 24 ; 段2
012626C6 5D pop ebp ; 段3
012626C7 5F pop edi ; 段4
012626C8 5E pop esi ; 数据组
012626C9 5B pop ebx ; 2E34-3F10的代码
012626CA C3 retn
对流程进行分析可以知道ASPR壳先生成了一段修正跳转表用到的基本数据,然后根据基本数据分别取数据组中的相关数据进行解密计算,并把计算出的地址替换到跳转表的代码中,让程序在调用跳转表时直接调用CALL 1410000。
对基本数据进行分析:
基本含义:
+0:dword not used
+4:dword not used
+8:dword 基本数据组初始化时所用的随机函数地址所需的数据
+C:dword not used
+10: dword 计算代码段HASH时使用的代码起止位置数据表
+14: dword 程序基址
+18: dword 待修正跳转表地址数量
+1C: dword not used
+20: dword 是否修正跳转表的总标志位
+24: dword 程序代码段起始地址
+28: dword 待修正跳转表地址的范围
+2C: dword 跳转表调用的地址,代码区段1
+30: dword 代码区段2
+34: dword 代码区段3
+38: dword 代码区段4
+3C: dword IAT数据组基址
+40: byte*0A函数序列的随机地址index,从+5C复制过来
+4A: byte*0Anot used--copy from [+8]
+54: dword 修正跳转表所用的数据组的基址
+58: dword 修正跳转表所用的数据组的结束地址
+5C: byte*0A函数序列的随机地址index
+66: byte*2 not used
+68: dword
{
+0:dword函数序列index指向的函数地址
+4:dword标志位? not used, copy from [+8]
+8:dwordhash? not used, copy from [+8]
}*8
+E0: dword 对地址加密的offset
+E4: dword 待修正跳转表所用的数据组的每组数据长度
+E8: dword
+EC: dword 修正完毕后的计数
+F0: dword 代码区段1的HASH值
+F4: dword 代码区段2的HASH值
+F8: dword 代码区段3的HASH值
+FC: dword 代码区段4的HASH值
+100:dword not used
+104:dword 修正跳转表所用数据组的HASH值
本程序待修正的数据组有0x79个,每个数据组长度为0x31。
修正跳转表时使用了2个随机函数的index:
index=0时:
0126250A 33C0 xor eax, eax
0126250C 8A07 mov al, byte ptr ; 取函数入口index0
0126250E 8D0440 lea eax, dword ptr
01262511 8B6C83 68 mov ebp, dword ptr ; 函数入口:
01262515 8BC6 mov eax, esi
01262517 FFD5 call ebp ; 函数功能:mov eax,
功能只相当于一句:mov eax,
index=9时:
01262527 33C0 xor eax, eax
01262529 8A47 09 mov al, byte ptr ; 取函数入口index9
0126252C 8D0440 lea eax, dword ptr
0126252F 8B5483 68 mov edx, dword ptr ; 函数入口:
01262533 8BC6 mov eax, esi
01262535 FFD2 call edx ; 函数功能:mov eax,byte ptr
功能只相当于一句:mov eax,byte ptr
再来看看程序调用修复后的CALL 1410000是怎么变成API地址执行的?
在call 1410000有大量的花指令和无效指令(我所说的无效意思是指令的计算结果完全没有使用),删除掉这些垃圾指令后代码就是这样子:
push edx
pushfd
sub esp, 20
lea edx, dword ptr
push edi
pop dword ptr
push eax
pop dword ptr
or eax, edi
mov dword ptr , ebp
push esi
pop dword ptr
mov esi, 42AFF2
push ebx
pop dword ptr
mov ebx, 4716BE
push ecx
pop dword ptr
mov edi, esp
lea edi, dword ptr
push edi
push edx
push edx
pop edi
add edi, 20
mov edi, dword ptr
push edi
push edx
pop edi
lea edi, dword ptr
push dword ptr
pop edi
lea edi, dword ptr
lea edi, dword ptr
push edi
push 0
pop edi
push dword ptr fs:
push 1210620
lea edx, dword ptr
call edx
分析一下这段代码的功能可以知道,其实功能非常简单,只是保存现场后call 1261F80,并把6个参数放入了堆栈:
[-2C]=esp+4 返回地址指针
[-30]=esp-28 返回时的堆栈-28
[-34]=[-8] 返回时的寄存器状态
[-38]=[-0]-5+EBC [-0]-5=CALL XXXXXXXX的后一个字节,EBC=CurrentProcessID
[-3C]=fs: 异常入口
[-40]=1210620 基本数据基址
其中加方括号的是相对于入口的堆栈偏移。
再来看看1261F80处的代码:
01261F80 55 push ebp
01261F81 8BEC mov ebp, esp
01261F83 83C4 D8 add esp, -28
01261F86 53 push ebx
01261F87 56 push esi
01261F88 57 push edi
01261F89 33C0 xor eax, eax
01261F8B 8945 DC mov dword ptr , eax
01261F8E 8945 D8 mov dword ptr , eax
01261F91 8945 E0 mov dword ptr , eax
01261F94 8B5D 08 mov ebx, dword ptr ; ebx=1210620,基本数据基址
01261F97 33C0 xor eax, eax
01261F99 55 push ebp
01261F9A 68 54222601 push 1262254
01261F9F 64:FF30 push dword ptr fs:
01261FA2 64:8920 mov dword ptr fs:, esp
01261FA5 EB 01 jmp short 01261FA8
01261FA7 90 nop
01261FA8 A1 54672601 mov eax, dword ptr
01261FAD C600 C9 mov byte ptr , 0C9
01261FB0 A1 A8672601 mov eax, dword ptr
01261FB5 C600 72 mov byte ptr , 72
01261FB8 33D2 xor edx, edx
01261FBA 55 push ebp
01261FBB 68 FB212601 push 12621FB
01261FC0 64:FF32 push dword ptr fs:
01261FC3 64:8922 mov dword ptr fs:, esp
01261FC6 A1 20672601 mov eax, dword ptr ; =012679D4
01261FCB 8B00 mov eax, dword ptr ; =01210000
01261FCD FFD0 call eax ; 功能:mov eax,+34]
01261FCF 8945 F0 mov dword ptr , eax
01261FD2 EB 01 jmp short 01261FD5
01261FD4 90 nop
01261FD5 8B45 1C mov eax, dword ptr
01261FD8 83E8 08 sub eax, 8
01261FDB 8B00 mov eax, dword ptr ; 入口时的EDX值
01261FDD 50 push eax
01261FDE 8A8B 16010000 mov cl, byte ptr
01261FE4 8B55 18 mov edx, dword ptr
01261FE7 8BC3 mov eax, ebx
01261FE9 E8 7EFAFFFF call 01261A6C ; 保存EDX值
01261FEE 8B45 1C mov eax, dword ptr
01261FF1 50 push eax ; 返回时的地址指针
01261FF2 B1 04 mov cl, 4
01261FF4 8B55 18 mov edx, dword ptr
01261FF7 8BC3 mov eax, ebx
01261FF9 E8 6EFAFFFF call 01261A6C ; 保存返回时的地址指针
01261FFE A1 DC672601 mov eax, dword ptr
01262003 8B40 34 mov eax, dword ptr ; GetCurrentProcessId
01262006 FFD0 call eax ; 获取线程ID
01262008 2945 10 sub dword ptr , eax ; 计算出调用CALL 1410000时的指令地址codeaddr
0126200B 8B45 10 mov eax, dword ptr
0126200E 2B43 14 sub eax, dword ptr ; codeaddr-baseaddr,相对地址
01262011 8B55 10 mov edx, dword ptr
01262014 2B53 24 sub edx, dword ptr ; codeaddr-codebase,代码段地址偏移
01262017 2B93 E0000000 sub edx, dword ptr ; codeaddr-codebase-offset
0126201D 8955 F8 mov dword ptr , edx
01262020 3B43 28 cmp eax, dword ptr
01262023 0F83 B2010000 jnb 012621DB ; 代码超出范围?
01262029 8D53 40 lea edx, dword ptr
0126202C 8955 E8 mov dword ptr , edx
0126202F EB 01 jmp short 01262032
01262031 90 nop
01262032 8B53 18 mov edx, dword ptr ; 取出待修正跳转表的总数
01262035 8955 F4 mov dword ptr , edx
01262038 8B55 10 mov edx, dword ptr
0126203B 83C2 05 add edx, 5
0126203E 8A12 mov dl, byte ptr ; 取出调用的跳转表代码的后一字节数据codeextra
01262040 3293 E0000000 xor dl, byte ptr ; codeextra xor offset
01262046 8BFA mov edi, edx
01262048 81E7 FF000000 and edi, 0FF
0126204E 25 FF000000 and eax, 0FF
01262053 33F8 xor edi, eax ; codeextra xor offset xor (codeaddr-baseaddr)
01262055 3B7D F4 cmp edi, dword ptr ; 异或出的结果为当前API所用数据组的组号
01262058 0F87 9F000000 ja 012620FD
0126205E 8B83 E4000000 mov eax, dword ptr ; 取出每组数据的长度
01262064 F7EF imul edi
01262066 0343 54 add eax, dword ptr ; 获取当前API所用数据组地址
01262069 8945 FC mov dword ptr , eax
0126206C EB 01 jmp short 0126206F
0126206E 90 nop
0126206F 8B45 E8 mov eax, dword ptr
01262072 0FB600 movzx eax, byte ptr
01262075 8D0440 lea eax, dword ptr
01262078 8B7483 68 mov esi, dword ptr
0126207C 8B45 FC mov eax, dword ptr
0126207F FFD6 call esi ; mov eax,
01262081 8BF0 mov esi, eax
01262083 3B75 F8 cmp esi, dword ptr ; 校验codeaddr-codebase-offset
01262086 75 63 jnz short 012620EB
01262088 807B 20 00 cmp byte ptr , 0 ; 检测总标志位
0126208C 74 3C je short 012620CA
0126208E 8B45 E8 mov eax, dword ptr
01262091 0FB640 09 movzx eax, byte ptr
01262095 8D0440 lea eax, dword ptr
01262098 8B5483 68 mov edx, dword ptr
0126209C 8B45 FC mov eax, dword ptr
0126209F FFD2 call edx ; mov eax,byte ptr
012620A1 3C 01 cmp al, 1 ; 检测数据组标志位
012620A3 75 25 jnz short 012620CA
012620A5 56 push esi ; codeaddr-codebase-offset
012620A6 8D45 FC lea eax, dword ptr
012620A9 50 push eax ; 数据组地址指针
012620AA 8B45 14 mov eax, dword ptr
012620AD 50 push eax ; 状态寄存器
012620AE 8B45 18 mov eax, dword ptr
012620B1 50 push eax ; 返回程序时的堆栈指针
012620B2 8B45 0C mov eax, dword ptr
012620B5 50 push eax ; fs:
012620B6 8B45 F0 mov eax, dword ptr
012620B9 50 push eax ; +34]
012620BA 8B4D 1C mov ecx, dword ptr ; 返回程序的地址指针
012620BD 8B55 10 mov edx, dword ptr ; 调用的跳转表地址指针
012620C0 8BC3 mov eax, ebx ; 修正跳转表所用的基本数据
012620C2 E8 BD010000 call 01262284 ; 计算出API地址并调用
这一段代码的功能是计算出CALL 1410000入口的地址并根据入口地址+5的BYTE数据计算出所用数据组的组号;校验入口地址;校验标志位;把相关参数放入堆栈和寄存器并call 01262284。总的来说这一段代码没有进行太实际的计算。
继续看call 01262284处的代码:
01262284 55 push ebp
01262285 8BEC mov ebp, esp
01262287 83C4 D8 add esp, -28
0126228A 53 push ebx
0126228B 56 push esi
0126228C 57 push edi
0126228D 894D F8 mov dword ptr , ecx
01262290 8955 F0 mov dword ptr , edx
01262293 8945 F4 mov dword ptr , eax
01262296 8B75 18 mov esi, dword ptr ; 当前API所用的数据组指针
01262299 8B45 F4 mov eax, dword ptr ; 基本数据
0126229C 8D58 40 lea ebx, dword ptr ; 基本数据+40,随机函数index
0126229F EB 01 jmp short 012622A2
012622A1 90 nop
012622A2 A1 54672601 mov eax, dword ptr
012622A7 C600 DD mov byte ptr , 0DD
012622AA 33C0 xor eax, eax
012622AC 8A43 07 mov al, byte ptr ; index7
012622AF 8D0440 lea eax, dword ptr
012622B2 8B55 F4 mov edx, dword ptr
012622B5 8B7C82 68 mov edi, dword ptr
012622B9 8B06 mov eax, dword ptr
012622BB FFD7 call edi ; mov eax,
012622BD 8945 EC mov dword ptr , eax ; 保存
012622C0 33C0 xor eax, eax
012622C2 8A43 08 mov al, byte ptr ; index8
012622C5 8D0440 lea eax, dword ptr
012622C8 8B55 F4 mov edx, dword ptr
012622CB 8B7C82 68 mov edi, dword ptr
012622CF 8B06 mov eax, dword ptr
012622D1 FFD7 call edi ; mov eax,
012622D3 8945 E8 mov dword ptr , eax ; 保存
012622D6 EB 01 jmp short 012622D9
012622D8 90 nop
012622D9 33C0 xor eax, eax
012622DB 8A43 02 mov al, byte ptr ; index2
012622DE 8D0440 lea eax, dword ptr
012622E1 8B55 F4 mov edx, dword ptr
012622E4 8B7C82 68 mov edi, dword ptr
012622E8 8B06 mov eax, dword ptr
012622EA FFD7 call edi ; mov eax,
012622EC 8BF8 mov edi, eax
012622EE 33C0 xor eax, eax
012622F0 8A43 06 mov al, byte ptr ; index6
012622F3 8D0440 lea eax, dword ptr
012622F6 8B55 F4 mov edx, dword ptr
012622F9 8B5482 68 mov edx, dword ptr
012622FD 8B06 mov eax, dword ptr
012622FF FFD2 call edx ; mov eax,byte ptr
01262301 8845 DF mov byte ptr , al ; 保存
01262304 8B45 F4 mov eax, dword ptr ; 基本数据
01262307 03B8 E0000000 add edi, dword ptr ; 计算代码hash的偏移:[+2C]+offset
0126230D EB 01 jmp short 01262310
0126230F 90 nop
01262310 57 push edi
01262311 33C0 xor eax, eax
01262313 8A45 DF mov al, byte ptr ; [+0]
01262316 05 FF000000 add eax, 0FF ; [+0]+0xFF
0126231B 50 push eax ; 计算代码hash的长度:[+0]+0xFF
0126231C 8B45 F4 mov eax, dword ptr
0126231F E8 4CFCFFFF call 01261F70 ; 计算代码hash的基址:fixaddr=126099C
01262324 8BC8 mov ecx, eax
01262326 8B45 F4 mov eax, dword ptr
01262329 8B50 10 mov edx, dword ptr ; 计算代码段HASH时使用的代码起止位置数据表指针
0126232C 8B45 F4 mov eax, dword ptr ; 基本数据
0126232F E8 70EAFFFF call 01260DA4 ; hash(fixaddr+[+2C]+offset,[+0]+0xFF)
01262334 8945 D8 mov dword ptr , eax ; 保存hash结果
01262337 EB 01 jmp short 0126233A
01262339 90 nop
0126233A 33C0 xor eax, eax
0126233C 8A43 03 mov al, byte ptr ; index3
0126233F 8D0440 lea eax, dword ptr
01262342 8B55 F4 mov edx, dword ptr
01262345 8B7C82 68 mov edi, dword ptr
01262349 8B06 mov eax, dword ptr
0126234B FFD7 call edi ; mov eax,
0126234D 8BF8 mov edi, eax
0126234F 8B45 F4 mov eax, dword ptr
01262352 03B8 E0000000 add edi, dword ptr ; [+20]+offset
01262358 8B45 EC mov eax, dword ptr ; [+9]
0126235B 03C7 add eax, edi ; [+20]+offset+[+9]
0126235D 0345 D8 add eax, dword ptr ; [+20]+offset+[+9]+hash
01262360 8945 EC mov dword ptr , eax ; API所用的DLL名称HASH:[+20]+offset+[+9]+hash
01262363 8B45 E8 mov eax, dword ptr ; [+11]
01262366 2BC7 sub eax, edi ; [+11]-([+20]+offset)
01262368 2B45 D8 sub eax, dword ptr ; [+11]-([+20]+offset)-hash
0126236B 8945 E8 mov dword ptr , eax ; API名称字符串HASH:[+11]-([+20]+offset)-hash
0126236E 33C0 xor eax, eax
01262370 8A43 01 mov al, byte ptr ; index1
01262373 8D0440 lea eax, dword ptr
01262376 8B55 F4 mov edx, dword ptr
01262379 8B5482 68 mov edx, dword ptr
0126237D 8B06 mov eax, dword ptr
0126237F FFD2 call edx ; mov eax,byte ptr
01262381 8BD8 mov ebx, eax
01262383 EB 01 jmp short 01262386
01262385 90 nop
01262386 8D45 E4 lea eax, dword ptr
01262389 50 push eax ; 用于保存计算结果的地址指针
0126238A 66:8B4D EC mov cx, word ptr ; API所用的DLL名称HASH
0126238E 66:8B55 E8 mov dx, word ptr ; API名称字符串HASH
01262392 8B45 F4 mov eax, dword ptr ; 基本数据
01262395 E8 02F7FFFF call 01261A9C ; 查找API相关HASH并获取API地址
0126239A 84C0 test al, al
0126239C 8B45 F4 mov eax, dword ptr
0126239F 8B80 E0000000 mov eax, dword ptr ; offset
012623A5 0345 E4 add eax, dword ptr ; 计算结果+offset:API实际地址
012623A8 8945 FC mov dword ptr , eax ; !!!!!!!!!!!!!!!API ADDRESS!!
到这里终于出现了所调用API的实际地址,再后面的代码就是把API地址偏移保存到一个表中,更改CALL 1410000为CALL 1460004,避免以后重复获取API地址;恢复堆栈和现场,跳到API入口继续执行。
重点分析一下这一段代码:
1.根据数据组中dword:[+2C]和byte:[+0]处的值计算壳代码的HASH值:hash=hash(fixaddr+[+2C]+offset,[+0]+0xFF)
2.计算出API所用的DLL名称HASH:[+20]+offset+[+9]+hash
3.计算出API名称字符串HASH:[+11]-([+20]+offset)-hash
4.根据DLL名称HASH和API名称HASH查找导入表字符串数据组,并获取API地址
其中加方括号的为使用随机函数的功能,共使用到了6个随机函数index:
7:mov eax,
8:mov eax,
2:mov eax,
6:mov eax,byte ptr
3:mov eax,
1:mov eax,byte ptr
分析到这里就可以得到还原跳转表的思路了:对每一个数据组计算出API所用DLL名称的HASH和API名称字符串的HASH,call 01261A9C计算出API实际地址,再将地址与上一节得到的IAT比较得到跳转表地址,最后把跳转表地址写入到数据组指定的代码偏移地址。
还原跳转表需要注意的问题:
1.不能直接在代码上patch,因为获取API相关HASH时会用到fixaddr=126099C开始的代码HASH值,我刚开始在这地方困扰了很长时间,总是获取几个API就出错了,无意间才注意到126099C其实就是正在执行的代码。
2.最好在壳代码执行完毕后进行patch,因为可能会有地方校验。
3.注意调用CALL时有的使用地址,有的使用地址指针,不要弄错。
我的选择是在修复完IAT并到达OEP后,在代码段最后位置00689F00进行patch,patch的代码如下:
00689F00 60 pushad ; 保存现场
00689F01 9C pushfd
00689F02 A1 74662601 mov eax, dword ptr
00689F07 8B18 mov ebx, dword ptr ; 基本数据
00689F09 8BBB E0000000 mov edi, dword ptr ; offset
00689F0F 8B6B 18 mov ebp, dword ptr ; 待修正跳转表地址数量
00689F12 8B73 54 mov esi, dword ptr ; 修正跳转表所用的数据组的基址
00689F15 83FD 00 cmp ebp, 0
00689F18 74 71 je short 00689F8B ; 循环结束?
00689F1A 8B46 2C mov eax, dword ptr ; [+2C]
00689F1D 03C7 add eax, edi ; [+2C]+offset
00689F1F 50 push eax
00689F20 33C0 xor eax, eax
00689F22 8A06 mov al, byte ptr ; [+0]
00689F24 05 FF000000 add eax, 0FF ; [+0]+0xFF
00689F29 50 push eax
00689F2A E8 4180BD00 call 01261F70 ; fixaddr=126099C
00689F2F 8BC8 mov ecx, eax
00689F31 8B53 10 mov edx, dword ptr ; 计算代码段HASH时使用的代码起止位置数据表
00689F34 8BC3 mov eax, ebx
00689F36 E8 696EBD00 call 01260DA4 ; hash(fixaddr+[+2C]+offset,[+0]+0xFF)
00689F3B 03C7 add eax, edi ; offset+hash
00689F3D 0346 20 add eax, dword ptr ; [+20]+offset+hash
00689F40 8B4E 09 mov ecx, dword ptr
00689F43 03C8 add ecx, eax ; [+9]+[+20]+offset+hash
00689F45 8B56 11 mov edx, dword ptr
00689F48 2BD0 sub edx, eax ; [+11]-([+20]+offset+hash)
00689F4A 6A 00 push 0
00689F4C 54 push esp ; 用于保存计算结果的地址指针
00689F4D 8BC3 mov eax, ebx
00689F4F E8 487BBD00 call 01261A9C ; 查找API相关HASH并获取API地址
00689F54 58 pop eax ; 取出计算结果
00689F55 03C7 add eax, edi ; 计算出API实际地址
00689F57 BA 98CB6900 mov edx, 0069CB98 ; IAT基址
00689F5C B9 B8090000 mov ecx, 9B8 ; IAT长度
00689F61 3B02 cmp eax, dword ptr ; 查找IAT中与API地址相等的位置
00689F63 74 0C je short 00689F71 ; 是否找到API?
00689F65 83C2 04 add edx, 4 ; 下一个IAT地址
00689F68 83C1 FC add ecx, -4
00689F6B 85C9 test ecx, ecx
00689F6D^ 75 F2 jnz short 00689F61 ; 查找结束?
00689F6F CD 03 int 3 ; 查找结束还未找到:出错
00689F71 8B46 04 mov eax, dword ptr ; 找到API时:
00689F74 0343 24 add eax, dword ptr ; [+4]+baseoffset
00689F77 03C7 add eax, edi ; 计算出待修复跳转表的位置:[+4]+baseoffset+offset
00689F79 66:C700 FF25 mov word ptr , 25FF ; 写入JMP
00689F7E 40 inc eax
00689F7F 40 inc eax
00689F80 8910 mov dword ptr , edx ; 写入跳转表地址
00689F82 03B3 E4000000 add esi, dword ptr ; 取下一个数据组
00689F88 4D dec ebp ; 数量-1
00689F89^ EB 8A jmp short 00689F15
00689F8B 9D popfd ; 循环结束:恢复现场
00689F8C 61 popad
00689F8D^ E9 4EFCFFFF jmp 00689BE0 ; 跳到OEP
再次跳到OEP后清除掉patch的代码,就可以DUMP了。
最后的步骤是用ImportREC写入修复后的IAT,测试运行----
运行出错!
还有暗桩?检查了一下原来还有个“暗桩”:在PE HEADER里,ASPR壳指定SizeOf Headers大小为0x400,然后又故意添加了数个段使PE HEADER的大小刚好是0x400,当用ImportREC修复好IAT后会添加一个新段,这个段就超出SizeOf Headers,导致载入失败。
顺便把重定位信息和大小清0,防止其他错误。
再次运行,一切OK! 这么精彩的文章,还是让小生哥给转过来了,呵呵
:D 好文,又长知识了,谢谢小生! 原帖由 chenguo 于 2009-1-14 22:36 发表 http://www.52pojie.cn/images/common/back.gif
这么精彩的文章,还是让小生哥给转过来了,呵呵
:D
这文的确写得很好啊! 精彩,真辛苦啊。
页:
[1]