NP1160DEMO全保护ShellCode-TLS部分完整逆向
寒假快要结束了。。。可能未来一年半时间内我在论坛上将不会发表主题了。
毕竟高考还是(***)的制度。
逆向再搞也不能在高考中取得加分。
Special thanks to fly , 海风 , Nooby , hmily , a__p 哥,多了我就不列举了,
反正感谢所有曾经帮助过我的人们。
最后,
再见,珍重……
首先声明,最新的NoobyProtect版本太萎缩运行太慢不好玩。。。
我直接从HDD里面拉出来一个历史的1160版本DEMO加了个全保护试炼搞一下。。。
感谢Nooby同学提供给我们这么好的学习机会
TLS部分代码完整跟踪:
开始又是代码解密
01075F83 9C pushfd
01075F84 53 push ebx
01075F85 51 push ecx
01075F86 E8 00000000 call calc_nps.01075F8B
01075F8B 8B1C24 mov ebx, dword ptr ss:[esp]
01075F8E 83C3 25 add ebx, 25
01075F91 33C9 xor ecx, ecx
01075F93 874B FC xchg dword ptr ds:[ebx-4], ecx
01075F96 83F9 00 cmp ecx, 0
01075F99 74 06 je short calc_nps.01075FA1
01075F9B 8033 60 xor byte ptr ds:[ebx], 60 ; 异或解密代码
01075F9E 43 inc ebx
01075F9F^ E2 FA loopd short calc_nps.01075F9B
01075FA1 83C4 04 add esp, 4
01075FA4 59 pop ecx
01075FA5 5B pop ebx
01075FA6 9D popfd
01075FA7 E9 04000000 jmp calc_nps.01075FB0
01075FAC 0000 add byte ptr ds:[eax], al
01075FAE 0000 add byte ptr ds:[eax], al
01075FB0 837C24 08 01 cmp dword ptr ss:[esp+8], 1
01075FB5 0F85 1C010000 jnz calc_nps.010760D7
01075FBB 68 20F6D17E push 7ED1F620 ; 此处。。。
01075FC0 810424 EB683582 add dword ptr ss:[esp], 823568EB
01075FC7 64:FF35 0000000>push dword ptr fs:[0] ; 安装结构化异常处理
01075FCE 64:8925 0000000>mov dword ptr fs:[0], esp
01075FD5 60 pushad
01075FD6 CC int3 ; int3触发异常
然后呢
0006F9A8 0006FA08Pointer to next SEH record
0006F9AC 01075F0BSE handler
很简单,拉到0x1075F0B处,设置int3断点(请注意是int3断点,千万不要用HE断点,看下面就知道)
拦下来
01075F0B 9C pushfd ; 这个就是异常处理了
01075F0C 53 push ebx
01075F0D 51 push ecx
01075F0E E8 00000000 call calc_nps.01075F13
01075F13 8B1C24 mov ebx, dword ptr ss:[esp]
01075F16 83C3 25 add ebx, 25
01075F19 33C9 xor ecx, ecx
01075F1B 874B FC xchg dword ptr ds:[ebx-4], ecx
01075F1E 83F9 00 cmp ecx, 0
01075F21 74 06 je short calc_nps.01075F29
01075F23 8033 50 xor byte ptr ds:[ebx], 50 ; 又是代码解密,但这次是0x50
01075F26 43 inc ebx
01075F27^ E2 FA loopd short calc_nps.01075F23
01075F29 83C4 04 add esp, 4
01075F2C 59 pop ecx
01075F2D 5B pop ebx
01075F2E 9D popfd
01075F2F E9 04000000 jmp calc_nps.01075F38
01075F34 0000 add byte ptr ds:[eax], al
01075F36 0000 add byte ptr ds:[eax], al
01075F38 60 pushad
01075F39 8B4424 24 mov eax, dword ptr ss:[esp+24] ; mov eax,pExceptionRecord
01075F3D 8B4C24 2C mov ecx, dword ptr ss:[esp+2C] ; mov ecx,pContext
01075F41 8B30 mov esi, dword ptr ds:[eax] ; mov esi,ExceptionCode
01075F43 0371 18 add esi, dword ptr ds:[ecx+18] ; /检测硬件断点捕获情况
01075F46 0371 14 add esi, dword ptr ds:[ecx+14]
01075F49 81EE 03000080 sub esi, 80000003
01075F4F 70 2B jo short calc_nps.01075F7C ; 如果有硬件断点这里就跳了,NOP掉★
01075F51 8B99 C1000000 mov ebx, dword ptr ds:[ecx+C1] ; /检测是否为单步方式
01075F57 83E3 01 and ebx, 1
01075F5A 3BF3 cmp esi, ebx
01075F5C 77 1E ja short calc_nps.01075F7C ; 如果TF位为1,这里就跳了, NOP掉★
01075F5E 8381 B8000000 0>add dword ptr ds:[ecx+B8], 4 ; Exception_Context中eip+=4 希望之后的异常后到这里执行
01075F65 8B81 B8000000 mov eax, dword ptr ds:[ecx+B8] ; 在这里放一个int3断点就能知道返回后所停的位置了★
01075F6B 8038 CC cmp byte ptr ds:[eax], 0CC ; 检测下次异常所停的地方是否有int3断点
01075F6E^ 0F84 96FFFFFF je calc_nps.01075F0A ; 如果有,则跳到非正常代码区,程序会出错退出,NOP掉★
01075F74 61 popad
01075F75 B8 00000000 mov eax, 0 ; 如果没检测到调试器,eax=0
01075F7A EB 06 jmp short calc_nps.01075F82
01075F7C 61 popad
01075F7D B8 FFFFFFFF mov eax, -1 ; 如果检测到调试器,eax=-1 (32-bit:0xFFFFFFFF)
01075F82 C3 retn ; 返回到系统执行区
★ NOP掉 上述三处 跳转之后就可以放心大胆的用int3断点和HE断点了,
不过建议使用 int3 断点 和 F9
shift + F9 有时候会把OD的 int3断点交到程序的异常处理过程中去
然后剩下来的代码就会像花指令一样,没隔几个就会出现int3,用上面的这个过程检测调试器,Nop掉以上三处之后
在指定位置放一个int3断点,这样每次都能知道下次代码所停的位置。。。
然后我们就需要超常的耐心和毅力,因为NP的特点就是萎缩
有效代码抠出来分析:
01075FDA 8B4424 2C mov eax, dword ptr ss:[esp+2C] ; eax = ImageBaseAddress;
01075FE2 8B0C24 mov ecx, dword ptr ss:[esp] ; ecx = ImageBaseAddress;
01075FE5 2BC1 sub eax, ecx ; eax -= ecx;
01075FEB 05 52010200 add eax, 20152 ; eax += 0x20152;
01075FF0 BB 1B300000 mov ebx, 301B ; ebx = 0x301B;
01075FF9 FE0408 inc byte ptr ds:[eax+ecx] ; / (BYTE *)(eax+ecx) += 1;
01075FFC 40 inc eax ; eax += 1;
01076001 4B dec ebx ; ebx-=1
01076002^ 75 F5 jnz short calc_nps.01075FF9 ; \循环
以上那个循环的作用是从RVA = 20152的程序内存开始,Size = 301B处每个字节+1
Nooby老师精彩点评:
这个是用来干掉输入表的,因为TLS执行的时候已经不需要了.加壳后程序无IAT,所以TLS里干掉,反dump.
下面
01076008 B8 A02B3554 mov eax, 54352BA0 ; eax = 0x54352BA0;
01076011 80B8 6329D2AC E>cmp byte ptr ds:[eax+ACD22963], 0E9 ; 判断0x1075503处是否为jmp far (0xE9)
01076018^ 0F85 96FFFFFF jnz calc_nps.01075FB4 ; 这里 Nooby 老师说不能跳,★
0107601E 81B0 8029D2AC C>xor dword ptr ds:[eax+ACD22980], 2CFE1EC0 ; 异或
01076028 B8 02000000 mov eax, 2 ; eax=2;
★ 此处不能跳,
如果单步跟踪,会发现cmp x1,x2 实际上是相等的,但是jnz会跳。
如果在后面的代码(不跳才会执行)下int3断点,再F9就会过去,没有问题,
至于为什么还没有搞明白,Nooby老师来讲解一下。
Nooby老师精彩点评:
这个查的是EP的CC,OD把你忽悠了
下面有一个cpuid指令,关于这个指令的详细内容可以在这里查询
http://www.sandpile.org/ia32/cpuid.htm
但是cpuid及后面那个循环作用不明(可能是垃圾指令)
Nooby老师精彩点评:
demo版限制,仅限本机,否则无法解密壳数据.
01076031 0FA2 cpuid ; | __in eax = 2h;
01076033 8ACA mov cl, dl ; 我这里 dl = 0x78('x'),cl = 0
01076039 02CC add cl, ah ; 我这里 ah = 0xB1 + cl = 0x29 (溢出)
0107603B 8B4424 2C mov eax, dword ptr ss:[esp+2C] ; eax = ImageBaseAddress;
0107603F 8B1424 mov edx, dword ptr ss:[esp] ; edx = ImageBaseAddress;
01076042 2BC2 sub eax, edx ; eax -= edx;
01076048 05 00400200 add eax, 24000 ; eax += 0x24000;
0107604D BB 3D000000 mov ebx, 3D ; ebx = 0x3D;
01076056 300C10 xor byte ptr ds:[eax+edx], cl ; /又开始异或解密了? cl == 0x29
01076059 40 inc eax
0107605A 4B dec ebx
0107605B^ 75 F9 jnz short calc_nps.01076056 ; \循环 ebx 作计数器 ebx == 0x3D
0107605D 8B4424 2C mov eax, dword ptr ss:[esp+2C] ; eax = ImageBaseAddress;
01076061 05 51400200 add eax, 24051 ; eax += 0x24051;
0107606A 8BF4 mov esi, esp ; esi = esp;
0107606C 33DB xor ebx, ebx ; ebx = 0;
0107606E 66:53 push bx ; push 0
01076070 44 inc esp ; esp += 1;
01076075 8A38 mov bh, byte ptr ds:[eax] ; / bh = ; // 0xD1
01076077 80FF 00 cmp bh, 0
0107607A 74 08 je short calc_nps.01076084
0107607C F6D7 not bh ; 用 not 来解密字串
0107607E 66:53 push bx ; push bh
01076080 44 inc esp
01076081 40 inc eax
01076082^ EB F1 jmp short calc_nps.01076075 ; \ 循环解密 ★
01076084 33DB xor ebx, ebx ; ebx = 0;
0107608A 8BFC mov edi, esp ; edi = (char *)"Protected by NoobyProtect"
0107608C 66:53 push bx ; push 0x0000
01076092 44 inc esp
01076093 40 inc eax
01076094 8A38 mov bh, byte ptr ds:[eax] ; bh = byte ;
0107609A 80FF 00 cmp bh, 0
0107609D 74 10 je short calc_nps.010760AF ; /又是一个循环
0107609F F6D7 not bh ; not 解密
010760A5 66:53 push bx ; push bx
010760A7 44 inc esp
010760A8 40 inc eax
010760AD^\EB E5 jmp short calc_nps.01076094 ; \ 循环解密 ★
010760AF 8BD4 mov edx, esp ; edx = (char *)"NoobyProte媡"
★上面有两个★循环,解密内容分别为
第一个循环
0006F960 00 50 72 6F Pro
0006F97074 65 63 74 65 64 20 62 79 20 4E 6F 6F 62 79 50tected by NoobyP
0006F98072 6F 74 65 63 74 2E rotect.
第二个循环
0006F9604E 6F 6F 62 79 50 72 6F 74 65 8B 74 NoobyProte媡. // "媡" 难道是防伪标志?
010760B1 6A 40 push 40
010760B7 52 push edx ; push (char *)"NoobyProte媡"
010760B8 57 push edi ; push (char *)"Protected by NoobyProtect"
010760B9 6A 00 push 0 ; push 0
010760BF B8 F037B02A mov eax, 2AB037F0 ; eax = 0x2AB037F0
010760C4 FF90 85F951D6 call dword ptr ds:[eax+D651F985] ; call user32.MessageBoxA
实际上调用 MessageBoxA
从SDK中可以找到
#define MB_ICONASTERISK 0x00000040L
所以,
上面的代码其实就是,
MessageBox(NULL,"Protected by NoobyProtect","NoobyProte媡",MB_ICONASTERISK);
传说中的 Nag... 就在这里了。。。
接下来。。。
010760CA 8BE6 mov esp, esi ; 恢复Stack
010760CC 61 popad ; 恢复寄存器
010760CD 64:8F05 0000000>pop dword ptr fs:[0] ; 恢复 SEH chain
010760D4 83C4 04 add esp, 4
010760D7 33C0 xor eax, eax
010760D9 50 push eax
在 push eax 之后,程序会触发int3异常,
由于SEH 已经被卸载,
所以系统会自动调用默认的异常处理程序处理异常。该异常将会被抛弃,并且TLS call back将会返回。
之后程序会被OD停在EP
至此,NP 1160 TLS callback 部分的代码戛然而止。
-------------------------------------告别-----------------------------------------
真的要远去了,再见…… 沙发
太佩服了
不知道还要学多久才能到这地步 膜拜下太强了。。。学习了。。。 自古英雄出少年 太强了 精神我们学习 强大膜拜加学习
有空的时候可以再来逛逛
祝学业有成 希望早日能再看到大大的教學! 来看看了啊 太复杂了,继续努力学习。 高考完再回来吧...
页:
[1]
2