XuZhenG 发表于 2009-2-7 14:55

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 部分的代码戛然而止。



-------------------------------------告别-----------------------------------------
真的要远去了,再见……

zapline 发表于 2009-2-7 15:46

沙发
太佩服了
不知道还要学多久才能到这地步

creantan 发表于 2009-2-7 15:47

膜拜下太强了。。。学习了。。。

babaloveyou 发表于 2009-2-7 17:23

自古英雄出少年

estelle 发表于 2009-2-7 19:32

太强了 精神我们学习

wgz001 发表于 2009-2-7 19:40

强大膜拜加学习
有空的时候可以再来逛逛
祝学业有成

a2213572 发表于 2009-2-8 10:25

希望早日能再看到大大的教學!

419611868 发表于 2009-2-8 16:52

来看看了啊

老万 发表于 2009-3-4 19:18

太复杂了,继续努力学习。

paulbaby3000 发表于 2009-3-6 02:40

高考完再回来吧...
页: [1] 2
查看完整版本: NP1160DEMO全保护ShellCode-TLS部分完整逆向