声明:我对彩色球的投注一点兴趣都没有,更不幻想有个什么计算公式得到几个号码去中500万大奖。我对《3D定胆杀码霸主》壳的兴趣完全是学习加密思想,本文也只是学习后的一笔记,绝无盗版之意。何况我这个菜鸟,要破解这样的硬壳无异于以卵击石。请《3D定胆杀码霸主》作者放心。
《3D定胆杀码霸主》加密壳(以下简称《3D》)最显著的特点是:超强反跟踪技术、反静态分析(乱码和乱序技术)、代码临时写入与覆盖技术。它基本放弃了断点SHE防范技术和CRC校验技术。其中任何一项技术都会让“菜鸟”陷入痛苦的深渊。《3D》综合了太多的加密技术,这是我放弃破解它的理由。或许我“太菜”,把一个技术平平的软件说成了“吃人妖怪”,让大侠见笑了!
一、超强的反跟踪技术
1.《3D》让“Olldbg”陷入不能自拔的痛苦境地:
如果你试图用OD去打开《3D定胆杀码霸主》,那么你会立即得到windows发送错误报告的报歉通知。也不知OD程序是怎样设计的,加载一个还未运行的软件就死掉了,3D居然发现了OD这样的软肋?先看《3D》的入口代码:
00AD1014 B8 00000000 mov eax,0 ;程序入口
00AD1019 60 pushad
00AD101A 0BC0 or eax,eax
00AD101C 74 68 je short 00AD1086 ;必跳
00AD101E E8 00000000 call 00AD1023 ;以下代码不会执行
00AD1023 58 pop eax
00AD1024 05 53000000 add eax,53
00AD1029 8038 E9 cmp byte ptr ds:[eax],0E9
00AD102C 75 13 jnz short 00AD1041
00AD102E 61 popad
00AD102F EB 45 jmp short 00AD1076
00AD1031 DB2D 3710AD00 fld tbyte ptr ds:[AD1037] ;不会执行的实数入栈
00AD1037 FFFF ???
00AD1039 FFFF ???
00AD103B FFFF ???
00AD103D FFFF ???
00AD103F 3D 40E80000 cmp eax,0E840
00AD1044 0000 add byte ptr ds:[eax],al
00AD1046 58 pop eax
00AD1047 25 00F0FFFF and eax,FFFFF000
00AD104C 33FF xor edi,edi
00AD104E 66:BB 195A mov bx,5A19
00AD1052 66:83C3 34 add bx,34
00AD1056 66:3918 cmp word ptr ds:[eax],bx
00AD1059 75 12 jnz short 00AD106D
如果你用OD加载,则屏幕显示到00AD1031就死机了。查看AD1031代码,原来它是一个压入实数的指令,但它指定的[AD1037]地址中是一个非法的实数表示法。我猜想OD装载程序时要分析代码,它试运行“fld tbyte ptr ds:[AD1037]”就死机了。这就是OD不能装载的原因。
解决的办法是:用16进制编辑器将“00AD1031 DB2D 3710AD00”中的3710AD00改成3F10AD00或4E10AD00等,即改为“fld tbyte ptr ds:[AD104E]”,它指定的实数就有意义了。实际上从00AD101E到00AD1076中的代码是根本不会被执行的,随便改为什么或填0都可以。后面的解压也不会用这部分作为源码,在OD中用命令“HR 00AD1037”可以证实这一点。也不知为什么OD要自作多情?好了,现在OD可以正常调试《3D》了,但是你不要高兴得太早,你是不会得到结果的。除非你知道在什么地方有反anti转跳!
2.看见别人在调试,《3D》就晕倒:
《3D》在运行时,大概会扫描windows的全部的进程,只要有调试程序在运行而不管自己的父进程是不是“调试程序”,《3D》就会弹出警告框并且退出。也就是说,只要你打开了SoftICE或Olldbg调试器等,不管你加不加载任何其它被调试程序,运行《3D》也是不行的。跟踪《3D》表明,它发现SoftICE或Olldbg等也是用CreateFile去查找“\\.\SIWVID”或“\\.\NTICE”等字串的方式。但麻烦的是冗长的查找代码是临时生成的,你无法在《3D》运行前修改其代码。
3.自编的调试软件也能被《3D》识破:
现成的调试软件都有其特征字串或代码,只要《3D》收集得多,它的识别能力就越强。我自编一个怎样?看你怎么办?让我大跌眼镜的是《3D》居然也能识别!目前我只知道创建一个进程有一个API函数是CreateProcessA,如果参数选择为“NORMAL_PRIORITY_CLASS”,则加载《3D》道也相安无事。《3D》没有发现任何不妥,因该模式不会向调试器发送任何消息,你也得不到任何《3D》内部的消息。若参数选择为“DEBUG_PROCESS”且什么事都不做,则《3D》也会毫不犹豫地退出!奇了!
原来,参数NORMAL_PRIORITY_CLASS和参数DEBUG_PROCESS的区别是调试进程中有无“stDE.u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT”消息。在使用DEBUG_PROCESS参数时,当《3D》被加载且在运行前,调试程序会收到一个“int 3”中断。就是这个int 3被《3D》发现且利用的,它是怎样发现的却不得而知?现在就连单是用CreateProcessA加载《3D》,调试器什么都不做都不可能,更不说怎样调试了。(我真的觉得我是鸡蛋碰石头了!)
4.对《软件加密技术内幕》(看雪学院著)“3.3.2 Debug API机制”中“调试寄存器”的改进:
我是为调试《3D》才买的《软件加密技术内幕》,学到了不少过去不知道的知识。但是书中第98页所指出的“中断在程序入口”的流程,似乎有点小题大做?好象不需要调用Ntdll.NtContinue设断?(声明:我不知道Ntdll.NtContinue是干什么的)用下面的方法可以轻松地在程序入口(或任何地方)设断:
(1)当收到CREATE_PROCESS_DEBUG_EVENT消息后(当程序加载时必然到达),写入Regs.Dr0=入口地址,Regs.Dr7=0x401(或0x101),Regs.ContextFlags=CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
(2)当拦截到EXCEPTION_SINGLE_STEP消息时,程序就中断在入口处(Regs.Dr0)了。
注:如果在EXCEPTION_DEBUG_EVENT消息后设断或在“第一次”EXCEPTION_BREAKPOINT消息后设断都是不能成功的。
在我自己的调试程序中证实,这个方法是可行的且简便的。
二、《3D》毫不在意你插入的int3断点和硬件断点
当你按前面的方法修改了代码且能用OD加载后,《3D》毫不在意你在什么地方插入int 3断点和硬件断点。只要不是被它自己写入的代码覆盖不会用SHE方法去抹去你的硬件断点的。它是不会用CRC去检查文件的完整性的。真还有点大家风度!不过一切设断都没用,因为它早就知道该要退出了!
三、实在令人头痛的乱码乱序技术
所谓乱码乱序技术,是将一大段代码,分割成苦干小块,每块中又插入苦干花指令或可执行但没有实际意义的代码,或将一个简单的指令改写为十分复杂的代码形式,最后用没完没了的上下转跳连接起来。让跟踪者陷入迷魂阵中。随便举个例子:
00E4A3CA ^\E9 B33BFFFF jmp 00E3DF82
00E4A3CF BE 1279ADBB mov esi,BBAD7912
00E4A3D4 ^ E9 F8C7FEFF jmp 00E36BD1
00E4A3D9 29D8 sub eax,ebx
00E4A3DB ^ E9 6BCCFFFF jmp 00E4704B
00E4A3E0 68 A457E020 push 20E057A4
00E4A3E5 FF3424 push dword ptr ss:[esp]
00E4A3E8 59 pop ecx
00E4A3E9 68 342F0000 push 2F34
00E4A3EE 893424 mov dword ptr ss:[esp],esi
00E4A3F1 ^ E9 57F9FEFF jmp 00E39D4D
00E4A3F6 01CB add ebx,ecx
00E4A3F8 8B0C24 mov ecx,dword ptr ss:[esp]
00E4A3FB 83C4 04 add esp,4
00E4A3FE 331C24 xor ebx,dword ptr ss:[esp]
00E4A401 ^ E9 BD94FFFF jmp 00E438C3
00E4A406 ^ 0F8E B3D4FEFF jle 00E378BF
00E4A40C C0E3 08 shl bl,8
00E4A40F C0E3 08 shl bl,8
00E4A412 ^ 0F85 AC18FFFF jnz 00E3BCC4
00E4A418 ^ E9 2F32FFFF jmp 00E3D64C
00E4A41D 05 3F6BA441 add eax,41A46B3F
00E4A422 ^ E9 200AFFFF jmp 00E3AE47
00E4A427 FECF dec bh
00E4A429 81EC 02000000 sub esp,2
00E4A42F ^ E9 3605FFFF jmp 00E3A96A
00E4A434 BF 4050AA29 mov edi,29AA5040
00E4A439 29FD sub ebp,edi
00E4A43B ^ E9 3DEDFEFF jmp 00E3917D
00E4A440 330424 xor eax,dword ptr ss:[esp]
00E4A443 ^ E9 45C8FFFF jmp 00E46C8D
00E4A448 81F1 4AB8D5D5 xor ecx,D5D5B84A
00E4A44E 05 61012A6C add eax,6C2A0161
00E4A453 ^ E9 E2F2FEFF jmp 00E3973A
这还只是它“乱序”的代表,要体会它的“乱码”技术,不要我来举例,你只要用OD打开稍加跟踪就知道了。
四、绝不直接调用现存的API函数
《3D》为了调用一个API函数,也费尽了心思。它加密了需要的API函数字串,需要时它也不解密出字串。它把kernel32.dll装入内存,从头开始查找字串,你可以在00EB8CA2设断,在状态栏中可以看到kernel32.dll中每一个函数都被扫描一遍,不清楚它按什么来确定所需要的函数(不是字串比较)。
下面我想说的只是怎样到达它的核心代码部分,省去你从头跟踪的麻烦。
1.在00AD1259设断:
从00AD1132—00AD1254是一段写入到00E18400—011F6107代码的解压程序。在00AD1259设断可以很快从循环中跳出来。
2.当第一个断点任务完成后,在00E6EB9F设断:
00E6EB9F断点是一个重要的任务分配retn点,程序每完成一项任务后回到这里,再重新转跳到新的地址中去完成另一个任务。任务地址在堆栈中可以看到。它的转跳地址是:(这是在自编的调试程序中记录下来的)
00E1BF6B 7C801D77 7C801D77 00E1BF6B 7C801D77 00E1BF6B 7C80A7D4
00EA5AC8 00E1BF6B 00E1BF6B 00E1BF6B 00E1BF6B 00E1BF6B 7C812ADE
00E1BF6B 7C814EEA 00EAF8F5 00EB15B0 00EB15B0 00EB15B0 00E1BF6B
7C809B47 7C809B47 7C809B47 00EA69F2 00EAF8F5 00EB7284 00EB8097
00EAF8F5 01749178 00EAF8F5 00EB8097 00EB8097 00EB8097 00EBBB69
00EBF0C2 00ED6B05 00EDC4E2 00E1BF6B 7C809E01 7C809E01 7C809E01
7C809E01 7C809E01 7C809E01 00EE8B4B 00EB8097 01701EDE 00EB8097
01702493 00E1D21A
它第一个00E1BF6B转跳,好象是调用“LoadLibraryA”,和前面说的一样,它从kernel32的第一个函数字串开始查找,找到后再换算成地址后call,整个代码冗长,没完没了的转跳和一些莫明其妙的指令,会让你累到吐血(其实也是一种战术)!其最后一个00E1D21A转跳就是显示“请退出调试,重新启动本程序对话框”的程序。为了这个对话框,它也是从查找kernel32中的字串开始,最后转跳到ntdll中,在一段代码中千万次地转跳,这个对话框才“逐渐”显示出来,好象进入了API函数的最底层。(可能是它照搬了API中的MessageBoxA的最底层代码?)总之它绝不用API中的现存函数。
3.用“HW 00EAEADB”命令,在00EAEADB地址写入时断下:
设断后,内存地址打开在00EAEADB。运行程序,当发现该地址值变成FF 35时,打开代码段中相同的地址,将00EAEAEF: jz 00EAEB63改为jnz 00EAEB63;00EAEB15:jnz 00EAEB63改为jz 00EAEB63。这是为了跳过一个SHE的“非法指令”(00EAEB9F)异常(好象是3D中唯一一个SHE异常)。
4.在00EAF192设断:
在00EAF192设断是为了让你看到在00EAF18C转跳的最后一刻,才把从00EAF196开始执行的代码准备好。绝对是把“遇山才开路,见水才搭桥”和“过河拆桥”的技术应用到极致。
5.在00E6AB1F设断:
在00E6AB1F设断是为了让你看到没完没了的转跳。00E6EB1F:jmp [edi+eax*4]是一个转跳地址分配器。好了,还有一段查找“\\.\SIWVID”或“\\.\NTICE”等字串的代码在什么地址实在是想不起来了,也不想从头跟踪去找了。它也是临时才解压出来,事前不可能设断和修改。在调试器中修改了也没用,因为它远不止一处地设置反跟踪代码。
五、用WinHex程序将运行中《3D》从内存中dump出来
将WinHex程序打开,再运行《3D》,它运行得很正常。你可以将main.exe(3D的主程序名)dump出来,删除最后部分的一些信息代码,保存为*.exe文件,基本上就是《3D》脱壳后的代码。可以用OD打开保存的*.exe文件,对401000开始的代码分析,只是不知道“入口地址”该设定在什么地方?从代码形式上看,它大概是用VC++写成的很通常的程序,调用API也很正规。只是IAT表已被装载无法还原。
可能该软件就是用一个现在十分流行的“某某加壳软件”封装的,只是我太菜没接触过这个加壳软件而已。
花了这么多时间,一无所获!…………一无所获吗?肯请高手指点迷津!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)