首页
社区
课程
招聘
Themida的简单脱壳
发表于: 2007-6-17 20:38 27580

Themida的简单脱壳

2007-6-17 20:38
27580

Themida的简单脱壳

很久没有写过文章了,看到还有人问Themida脱壳,简单写几句。不涉及VM。
脱的是NP1012的GameMon,Ring0玩不过,郁闷,更郁闷的是眼睛吃不消。

1. Dump点搜索:

   以前的版本,挂在用CreateFile加载的kernel32!Sleep,就很容易找到dump
   位置,这个似乎不同了,也不知道是什么版本.

   先随便找个地方下断,比如对第1个区段下写入断点,目的是等VM所在内存分
   配,然后在VM内搜索出口:

   popad
   popfd
   ret

   对找到的所有结果下断,写个脚本记录一下调用过程:

   var        counter
   var        ret_addr

   mov        counter,0
   eob        l_record
   eoe        l_record
   run

l_record:
   mov ret_addr,[esp]       
   log ret_addr

   esto

   结果:

........
00DDCBD3   Breakpoint at 00DDCBD3
           ret_addr = 0088A795
00E42EF0   Breakpoint at 00E42EF0
           ret_addr = 0088B084
00E4F835   Breakpoint at 00E4F835
           ret_addr = 0088BAA2 <---- 这个
00E42EF0   Breakpoint at 00E42EF0
           ret_addr = 02FA1A7E
00E4F835   Breakpoint at 00E4F835
           ret_addr = 0046228E
00DDCBD3   Breakpoint at 00DDCBD3
           ret_addr = 00402750
00E4F835   Breakpoint at 00E4F835
           ret_addr = 00460327
00E42EF0   Breakpoint at 00E42EF0
           ret_addr = 00402790
00E5D56F   Breakpoint at 00E5D56F
           ret_addr = 00410D1E
           Thread 000005E4 terminated, exit code 0
           Thread 00000184 terminated, exit code 0
           Thread 000003DC terminated, exit code 0
   
  最后几项的返回地址在原程序了,还有个02FA1A7E是模仿的GetVersion。
  Ctrl-F2重来,在0088BAA2下断,还有一两次SMC解码(大概有,记不清了),
  很容易找到dump点:

0088C432   /0F86 01000000    jbe GameMon.0088C439
0088C438   |F5               cmc
0088C439   \9D               popfd
0088C43A    C3               retn
   
-> 这里:

007D4BF3    68 54235132      push 32512354
007D4BF8  ^ E9 B9C6FFFF      jmp GameMon.007D12B6
007D4BFD    68 C4711C55      push 551C71C4
007D4C02  ^ E9 AFC6FFFF      jmp GameMon.007D12B6
007D4C07    FB               sti
007D4C08    D125 06E03800    shl dword ptr ds:[38E006],1

2. 避开IAT加密

写脚本挂到CreateFile加载的kernel32!VirtualAlloc,记录分配size,
当出现连续分配0x1000,0x2000,0x10000,最后的1个出来就到了.

00EDA072   Hardware breakpoint 1 at 00EDA072
           counter = 00000007
           ret_addr = 007D787D
           cbSize = 00001000
00EDA072   Hardware breakpoint 1 at 00EDA072
           counter = 00000008
           ret_addr = 007D789F
           cbSize = 00002000
00EDA072   Hardware breakpoint 1 at 00EDA072
           counter = 00000009
           ret_addr = 007D78BF <---- 这里
           cbSize = 00010000 | UNICODE "=::=::\"

007D78BF    8985 A9271406    mov dword ptr ss:[ebp+61427A9],eax
007D78C5    8BB5 31321406    mov esi,dword ptr ss:[ebp+6143231]            ; GameMon.007CE59A
007D78CB    8B9D 41051406    mov ebx,dword ptr ss:[ebp+6140541]
007D78D1    89B5 05081406    mov dword ptr ss:[ebp+6140805],esi            ; GameMon.007CE59A
007D78D7    899D C9021406    mov dword ptr ss:[ebp+61402C9],ebx
007D78DD    8B9D 41051406    mov ebx,dword ptr ss:[ebp+6140541]
007D78E3    8B0B             mov ecx,dword ptr ds:[ebx]
007D78E5    83F9 00          cmp ecx,0
007D78E8    0F84 DF0A0000    je GameMon.007D83CD

其中007D78C5 mov esi,dword ptr ss:[ebp+6143231] 为IAT相关数据.

007CE59A     84B059E0    DD0B4D7D    4000A3B1    E000A52F
007CE5AA     A000A54A    E000C9EF    FFFFFFFF    DDDDDDDD
007CE5BA     10945DAF    FD0B4D7D    200043CA    8000BC4E
........
.......
007D065A     EEEEEEEE    DDDDDDDD    F684BB2D    DD0B4D91
007D066A     0001BDBE    AAAAAAAA    FFFFFFFF    DDDDDDDD
007D067A     EEEEEEEE    DDDDDDDD    00000000    00000000
007D068A     07B540EB    0823007D    087A007D    FFFF007D
   
数据以FFFFFFFF DDDDDDDD EEEEEEEE DDDDDDDD结束,包括API Name的Hash,
对应要patch的JMP [IAT]代码地址。fly的做法是patch代码,对于
JMP [IAT]代码的修复,我是另写的程序,代码后面贴。

把这段数据binary copy保存下来,Themida用过要清0。先取干净的IAT。
走几步:

007D7A5B    61               popad
007D7A5C    B9 5D71A45E      mov ecx,5EA4715D <--------- key1
007D7A61    BA 6D5D107F      mov edx,7F105D6D <--------- key2
007D7A66    AD               lods dword ptr ds:[esi]
007D7A67    89B5 05081406    mov dword ptr ss:[ebp+6140805],esi            ; kernel32.77EAF541

记下这2个值。下面和以前一样,避开IAT加密,NOP 4个JE。

007D7B83    83BD 4D251406 01 cmp dword ptr ss:[ebp+614254D],1
007D7B8A    0F84 39000000    je GameMon.007D7BC9
007D7B90    3B8D 712E1406    cmp ecx,dword ptr ss:[ebp+6142E71]            ; kernel32.77E40000
007D7B96    0F84 2D000000    je GameMon.007D7BC9
007D7B9C    3B8D 85041406    cmp ecx,dword ptr ss:[ebp+6140485]            ; USER32.77D10000
007D7BA2    0F84 21000000    je GameMon.007D7BC9
007D7BA8    3B8D 650E1406    cmp ecx,dword ptr ss:[ebp+6140E65]            ; ADVAPI32.77DA0000
007D7BAE    0F84 15000000    je GameMon.007D7BC9
007D7BB4    8D9D BA422606    lea ebx,dword ptr ss:[ebp+62642BA]
007D7BBA    FFD3             call ebx                                      ; GameMon.007C5348

PATCH自校验:

007D7A28    C1E9 08          shr ecx,8
007D7A2B    33C8             xor ecx,eax
007D7A2D    4A               dec edx
007D7A2E  ^ 0F85 EAFFFFFF    jnz GameMon.007D7A1E
007D7A34    8BC1             mov eax,ecx
007D7A36    F7D0             not eax
007D7A38    3985 E5021406    cmp dword ptr ss:[ebp+61402E5],eax  <----- 校验
007D7A3E    0F84 17000000    je GameMon.007D7A5B
007D7A44    83BD D11E1406 00 cmp dword ptr ss:[ebp+6141ED1],0

得到干净的IAT后,保存tree。

Ctrl-F2重来,停在dump点。把前面保存的数据binary paste
到原位置(这些步骤可以写脱壳机来做,原来也搞过,但有些细节老变,有bound dll的
也不同,就算了)。运行Patch修复被改为NOP/CALL的原JMP [IAT]代码,Dump,Fix IAT。

代码以前的,有点乱,懒得去改了.

int main()
{

        HANDLE        hThemida        = 0;
        DWORD        dwBegin                = 0x007CE59A;                                // iat数据起始地址
        DWORD        dwEnd                = 0x007D0682;                                // iat数据结束地址
        PDWORD        lpBuff                = 0;
        DWORD        dwCounter        = (dwEnd - dwBegin) / 4;
        DWORD        imageBase        = 0x00400000;

       
        //       

        hThemida = OpenProcess(PROCESS_ALL_ACCESS,FALSE,0x68C);
        if(!hThemida)
        {
                printf("Failed to open the process!\r\n");
                return 0;
        }
       
        lpBuff = (PDWORD)new BYTE[dwEnd - dwBegin];
        ReadProcessMemory(hThemida,
                                        (PVOID)dwBegin,
                                        lpBuff,
                                        dwEnd - dwBegin,
                                        NULL);

        DWORD i = 0;

        DWORD key1 = 0x5EA4715D;        // ecx        ; NP1012 GameMon数据,什么版本的Themida?
        DWORD key2 = 0x7F105D6D;        // edx

        DWORD lastData        = 0;                // 上1个数据
        DWORD currData        = 0;                // 当前数据
        DWORD iatAddr;                        // IAT地址
        DWORD patchAddr;                // PATCH地址
        BYTE  opcode[6];

        while(TRUE)        // 每轮循环处理1个API
        {
                if(i >= dwCounter)
                        break;
               
                // 0xEEEEEEEE,0xDDDDDDDD dll结束标记

                if((0xEEEEEEEE == lpBuff[i]) && (0xDDDDDDDD == lpBuff[i + 1]))
                {
                        i += 2;                // 跳过2个DWORD
                        continue;
                }

                // 取DWORD
                currData = lpBuff[i] ^ lastData;
               
                _asm mov eax,currData
                _asm ror eax,3
                _asm mov currData,eax

                currData -= key2;

                _asm mov eax,currData
                _asm rol eax,0x10
                _asm mov currData,eax
               
                currData ^= key1;
                lastData = lpBuff[i];

                i++;

                if(currData >= 0x10000)        // currData为API名hash
                {
                        //
                        // 取API地址,若为特殊API另行处理(4个JE)...
                        //

                        iatAddr = lpBuff[i];
                       
                        _asm mov eax,iatAddr
                        _asm rol eax,5
                        _asm mov iatAddr,eax
                       
                        iatAddr += key1;
                        iatAddr += imageBase;

                        i++;
                       
                        //
                        // 将API地址保存到IAT...
                        //

                        // 若后续的2个DWORD为FFFFFFFF,DDDDDDDD代表该API
                        // 不需要PATCH代码(JMP [IAT])

                        while(!((0xFFFFFFFF == lpBuff[i]) && (0xDDDDDDDD == lpBuff[i + 1])))
                        {
                                // 下一个数据为Patch地址
                               
                                patchAddr = lpBuff[i];
                               
                                _asm mov eax,patchAddr
                                _asm rol eax,3
                                _asm mov patchAddr,eax
                                       
                                patchAddr += imageBase;
                               
                                if(0xAAAAAAAA == lpBuff[i + 1])        // jmp [iat]
                                {
                                        *((PWORD)opcode) = 0x25FF;
                                        i += 2;        // 跳过patch地址及AAAAAAAA
                                }
                                else // call [iat]
                                {
                                        *((PWORD)opcode) = 0x15FF;
                                        i += 1;        // 跳过patch地址
                                }
                               
                                *(PDWORD)&opcode[2] = iatAddr;
                               
                                WriteProcessMemory(hThemida,(PVOID)patchAddr,opcode,6,NULL);
                        }
                       
                        // 到这里,即发现FFFFFFFF,DDDDDDDD
                        i += 2; // 跳过标记
                }
                else
                {
                        if(0xBBBBBBBB == lpBuff[i])
                        {
                                //
                                // 不处理的API,将API真实地址送入IAT...
                                //
                                i++;        // 跳过BBBBBBBB
                               
                                iatAddr = lpBuff[i];
                               
                                _asm mov eax,iatAddr
                                _asm rol eax,5
                                _asm mov iatAddr,eax
                                       
                                iatAddr += key1;
                                iatAddr += imageBase;
                               
                                i++;        // 跳过IAT地址
                               
                                //
                                while(!((0xFFFFFFFF == lpBuff[i]) && (0xDDDDDDDD == lpBuff[i + 1])))
                                {
                                        patchAddr = lpBuff[i];
                               
                                        _asm mov eax,patchAddr
                                        _asm rol eax,3
                                        _asm mov patchAddr,eax
                                       
                                        patchAddr += imageBase;
       
                                        if(0xAAAAAAAA == lpBuff[i + 1])        // jmp [iat]
                                        {
                                                *((PWORD)opcode) = 0x25FF;
                                                i += 2;        // 跳过patch地址及AAAAAAAA
                                        }
                                        else // call [iat]
                                        {
                                                *((PWORD)opcode) = 0x15FF;
                                                i += 1;        // 跳过patch地址
                                        }
                                       
                                        *(PDWORD)&opcode[2] = iatAddr;
                                       
                                        WriteProcessMemory(hThemida,(PVOID)patchAddr,opcode,6,NULL);
                                       
                                }
                               
                                // 到这里,即发现FFFFFFFF,DDDDDDDD
                                i += 2; // 跳过标记
                        }
                        else
                        {
                                //
                                // 不满足currData >= 0x10000,但下1个值又不是BBBBBBBB
                                // 断不下,实际代码的处理是落到
                                // if(currData >= 0x10000)同样的代码
                                //
                                TRACE("Wrong\n");
                        }
                }
        }

        delete [] (PBYTE)lpBuff;
       
        CloseHandle(hThemida);

        //
        return nRetCode;
}


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (81)
雪    币: 319
活跃值: (2459)
能力值: ( LV12,RANK:980 )
在线值:
发帖
回帖
粉丝
2
看不懂也要死顶
2007-6-17 20:49
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
看不懂???还是顶一下
2007-6-17 21:09
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
膜拜啊膜拜中。。。。
2007-6-17 22:02
0
雪    币: 1969
活跃值: (46)
能力值: (RANK:550 )
在线值:
发帖
回帖
粉丝
5
顶softworm
2007-6-17 22:35
0
雪    币: 202
活跃值: (77)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
6
呵呵,好文要顶起
2007-6-17 22:37
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
还是看不懂!

学脱加密壳到底该怎么学啊......
2007-6-17 22:41
0
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
8
感谢分享,这文章好像不简单
2007-6-17 22:53
0
雪    币: 8209
活跃值: (4518)
能力值: ( LV15,RANK:2473 )
在线值:
发帖
回帖
粉丝
9
脱壳真难,学不进去
2007-6-17 23:09
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
天书.
2007-6-17 23:31
0
雪    币: 325
活跃值: (97)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
11
似懂非懂。。
先随便找个地方下断,比如对第1个区段下写入断点,目的是等VM所在内存分
   配,然后在VM内搜索出口:

   popad
   popfd
   ret

   对找到的所有结果下断,写个脚本记录一下调用过程:

   var  counter
   var  ret_addr

   mov  counter,0
   eob  l_record
   eoe  l_record
   run

l_record:
   mov ret_addr,[esp]  
   log ret_addr

   esto

   结果:

........
00DDCBD3   Breakpoint at 00DDCBD3
           ret_addr = 0088A795
00E42EF0   Breakpoint at 00E42EF0
           ret_addr = 0088B084
00E4F835   Breakpoint at 00E4F835
           ret_addr = 0088BAA2 <---- 这个

不懂为什么要这么找
而且为什么要在这里dump呢?直接等EIP在CODE段的时候DUMP不好嘛?是不是ANTI-Virtual Machine的原因
2007-6-17 23:34
0
雪    币: 217
活跃值: (15)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
  顶偶像softworm
2007-6-17 23:37
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
天书级
2007-6-18 00:27
0
雪    币: 304
活跃值: (82)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
14
神人的脱壳教程就是与众不同
2007-6-18 09:00
0
雪    币: 263
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
楼主把脱好的文件发上来吧, 我等菜鸟好学习
2007-6-18 09:11
0
雪    币: 255
活跃值: (207)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
16
看来楼上已经习惯以马甲身份出现了.
2007-6-18 09:14
0
雪    币: 263
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
我是golds7n的马甲
2007-6-18 09:15
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
18
好文章,学习+支持~~~
2007-6-18 09:18
0
雪    币: 12
活跃值: (1895)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
看不懂!
呵呵,好文要顶起
2007-6-18 09:31
0
雪    币: 204
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
好文啊 ,及时解决了小生困难,

但是看不懂。
2007-6-18 09:31
0
雪    币: 1301
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
21
看不懂,就因为看不懂,所以我要更努力地学习。谢谢,学习了。
2007-6-18 10:27
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
22
to foxbabu:

在那里dump是包含了被VM的stolen codes的,如果贴VM区段
运行是需要这些的,当然你猜出stolen codes就不必了。

好象Themida的changelog提过Virtual Anti-Dump改了,估计
补区段也未必能跑起来。NP我现在是斗不过,要是非得看
CodeVirtualizer就要命了

“太虚伪了”是哪位的马甲? GameMon我没有补区段,也没有
修复VM保护代码,有这精神不如搞dump_wmimmc呵呵,慢慢来,
身体要紧。
2007-6-18 10:39
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
23
gold甲?      黄金甲?
2007-6-18 11:39
0
雪    币: 114
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
虫子的简单对我们来说就是超难
2007-6-18 15:55
0
雪    币: 427
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
NP有个超简单的破解方法,答案在国外的一些游戏外挂里面。绝对让你想不到的方法。
2007-6-18 18:35
0
游客
登录 | 注册 方可回帖
返回
//