脱壳之简单加密壳
本帖最后由 Nattevak 于 2021-12-22 19:29 编辑一、简单分析与解密
脱壳最重要的三步:找原始OEP,转存文件,修复文件
压缩壳按照这三步就可以完成脱壳,而加密壳因为对PE文件的信息进行了加密处理,找到OEP只是刚开始,还需要将加密之后的代码、数据进行还原才能够完成脱壳
注:遇到未知壳最通用的办法还是单步跟踪,将程序载入OD,到达OEP
1.找OEP
先看代码段,查看IAT调用的opcode, 是FF15还是FF25, FF15就是VS程序,FF25就是宝蓝
公司的程序。
再看链接器版本,6.0,可以确定是VC6.0
所以调试程序,设置api断点GetVersion和GetSystemTimeAsFileTime,等断到主模块调用时栈回溯,找到OEP
往上找一下就能找到OEP
2.进入CALL简单分析一下,一堆花指令,简单NOP大法一下
由于JMP都是往下跳,没有跳到其他地方,这部分的代码都是向下运行,故将JMP都NOP掉,化简代码部分
最终化简代码
032B000A B8 5B00E260 MOV EAX,0x60E2005B ;加密的值
032B0012 35 15151515 XOR EAX,0x15151515 ;还原IAT函数
032B001A 50 PUSH EAX ;将函数压入堆栈
032B001F C3 RETN ;返回真正的IAT函数
3.因为此处的IAT都已经加密了,所以我们需要进行解密
选中地址并在数据窗口中跟随
因为此处IAT是动态的获取一个地址再填充进去的,所以我们下一个硬件写入断点
程序断下,我们发现有一个eax赋值给的操作,查看一下eax和edi
分析上段代码,发现并没有对EAX的操作,都是对EAX指向内容的操作,所以EAX应该来源于VirtualAlloc的返回值
再向上分析,找到了GetProcAddress
4.解密IAT
①NOP很多代码,保留未知操作部分与填充IAT部分即可
004385B9 FF15 CC924300 CALL DWORD PTR DS:
004385BF 90 NOP
004385C0 90 NOP
004385C1 90 NOP
004385C2 90 NOP
004385C3 90 NOP
004385C4 90 NOP
004385C5 90 NOP
004385C6 90 NOP
004385C7 90 NOP
004385C8 90 NOP
004385C9 90 NOP
004385CA 90 NOP
004385CB 90 NOP
004385CC 90 NOP
004385CD 90 NOP
004385CE 90 NOP
004385CF 90 NOP
004385D0 90 NOP
004385D1 90 NOP
004385D2 90 NOP
004385D3 90 NOP
004385D4 90 NOP
004385D5 90 NOP
004385D6 90 NOP
004385D7 90 NOP
004385D8 90 NOP ; 拷贝16字节到xmm0
004385D9 90 NOP
004385DA 90 NOP
004385DB 90 NOP
004385DC 90 NOP
004385DD 8B55 CC MOV EDX,DWORD PTR SS:
004385E0 90 NOP ; 填充16个字节到
004385E1 90 NOP
004385E2 90 NOP
004385E3 90 NOP
004385E4 90 NOP ; 拷贝16字节到xmm0
004385E5 90 NOP
004385E6 90 NOP
004385E7 90 NOP
004385E8 90 NOP
004385E9 90 NOP ; 填充16个字节到
004385EA 90 NOP
004385EB 90 NOP
004385EC 90 NOP
004385ED 90 NOP
004385EE 8907 MOV DWORD PTR DS:,EAX ; 填充IAT
②NOP异或部分,直接填充IAT
修改
004385C8 8907 MOV DWORD PTR DS:,EAX
004385CA 90 NOP
004385CB 90 NOP
004385CC 90 NOP
修改
004385EE 90 NOP ; 填充IAT
004385EF 90 NOP
③手工填充IAT
在获取IAT和填充IAT的位置分别下断点
程序运行之后,手工将未加密的IAT复制到加密填充的位置
成功手工修复一个地址
④使用通用脚本修复IAT,原理与③相同
// 1.定义变量
MOV dwOEP,00409486
MOV dwGetAPI,004385BF
MOV dwWriteIAT,004385F0
// 2. 清除环境
BC // 清除所有软件断点
BPHWC // 清除所有硬件断点
BPMC// 清除内存断点
// 3. 设置断点
BPHWS dwOEP, "x" //当执行到此地址时产生中断.
BPHWS dwGetAPI, "x" //当执行到此地址时产生中断.
BPHWS dwWriteIAT, "x" //当执行到此地址时产生中断.
// 4. 循环
LOOP0:
RUN // F9
CMP dwGetAPI,eip
JNZ CASE1
MOV dwTMP,eax
JMP LOOP0
CASE1:
CMP dwWriteIAT,eip
JNZ CASE2
MOV ,dwTMP
JMP LOOP0
CASE2:
CMP dwOEP,eip
JNZ LOOP0
MSG "已到达OEP"
脚本运行完成后务必ESC一下!!
⑤使用特定脚本修复IAT,原理与②相同
// 1.定义变量
MOV dwOEP,00409486
MOV dwPatch1,004385C8
MOV dwPatch2,004385EE
// 2. 清除环境
BC // 清除所有软件断点
BPHWC // 清除所有硬件断点
BPMC// 清除内存断点
// 3. 设置断点
BPHWS dwOEP, "x" //当执行到此地址时产生中断.
BPHWS dwPatch1, "x" //当执行到此地址时产生中断.
// 4. 循环
LOOP0:
RUN // F9
CMP dwPatch1,eip
JNZ CASE1
FILL dwPatch1,5,90 //NOP 5个字节
ASM dwPatch1,"MOV DWORD PTR DS:,EAX" //将当前指令修改为 MOV DWORD PTR DS:,EAX
FILL dwPatch2,2,90
BPHWC dwPatch1
JMP LOOP0
CASE1:
CMP dwOEP,eip
JNZ LOOP0
MSG "OEP已到达"
5.DUMP完成脱壳
二.单步跟踪分析
先回到程序入口点,看第一个CALL,00438456处调用了004383A0,观察发现并没有入栈操作,故没有参数,而调用完后,返回值EAX没有被使用,可知这个CALL没有参数也没有返回值,所以这个CALL在C语言中的函数原型应为void fun(void),进入这个CALL,大致查看其功能,发现有对FS寄存器的操作代码
访问FS寄存器一般是访问线程TEB以及PEB结构体,再加上获取kernel32基址的话需要访问PEB结构体中的模块链表。继续向下分析,发现有函数名称的字符串注释,包括LoadLibraryA、GetModuleHandleA、VirtualProyect等函数名,再查看调用call和全局变量的值,发现均为0,所以当执行到访问全局变量和调用时已经初始化了。
所以可以推测出此函数就是初始化函数地址的函数
单步步过483A0这个CALL,查看CALL内的全局变量,观察初始化后的值,可以看到初始化了一些函数地址
在438040地址内存中存放的是400000,即映像基址
在438044地址内存中存放的是1000,即代码RVA
继续向下分析,发现两处VirtualProtect的调用,观察两处调用的参数
第一处VirtualProtect修改的地址是401000,即代码段基址,修改的属性为可读可写可执行,故猜测下面会有写入代码的操作,而第二次VirtualProtect调用之前,只有CALL 438290 ,猜测其中有修改代码段的代码
先查看一下代码段代码
单步步过这个CALL,发现代码段被修改了,恢复为正常代码
故猜测这个CALL内部应该是执行了解密代码段的操作,回车进去简单分析一下
可以很明显的发现与0x15的异或操作,故此处CALL的功能就是解密代码段
继续分析第二处VirtualProtect,修改的属性为可读可执行,故此处是恢复代码段的内存属性
再向下分析,是信息框的弹出以及对信息框返回值的判断,发现CALL 4384E0再信息框确定之后才会执行源程序代码
在MessageBoxA下面的代码,除了条件判断就是一个CALL一个JMP,还是退出进程的函数,而且本程序的映像基址是400000,不需要重定位,剩下的只有修复IAT了
而CALL后面的JMP多半就是跳转到原始OEP的,看一下JMP的地址,为409486,查看409486,一看就知道是VC6.0或者易语言的程序入口
再查看一下这个地址所在的区段,是第一个区段,基本上就能确定是原始OEP
后面就仍是对IAT的分析了,上文已写出分析过程,故不再赘述。
加密IAT的大致逻辑:
1.在局部变量中保存了一段shellcode代码
2.使用GetProcAddress获取函数地址,加密处理,保存到局部变量中
3.申请内存,将局部变量准备好的代码拷贝到申请的内存中
4.将申请内存的首地址填充到IAT
而破解加密IAT的方法就是将未加密的函数地址填充到IAT即可
本帖最后由 邓振振 于 2021-12-22 20:29 编辑
大佬,你好。下载了坛子的The Enigma Protector
主要搞.net的,写了个winform。用The Enigma Protector 加密了,大佬有兴趣帮忙看看The Enigma Protector 这个软件的加壳强度怎么样吗
「WindowsFormsTest.exe」 链接:https://www.aliyundrive.com/s/yBaHRuVM7Ba 落红护花 发表于 2021-12-22 20:44
enigma秒破,dump就行
感谢,我想问一下你知道有没有支持.net core加密的软件,加密后的可以在linux运行。准备买dnguard旗舰版,但是加密的程序只能在win上运行。找了好久没找到适合的。 签到学习一下。 邓振振 发表于 2021-12-22 20:19
大佬,你好。下载了坛子的The Enigma Protector
主要搞.net的,写了个winform。用The Enigma Protector ...
enigma秒破,dump就行 邓振振 发表于 2021-12-22 20:54
感谢,我想问一下你知道有没有支持.net core加密的软件,加密后的可以在linux运行。准备买dnguard旗舰版 ...
winlicense试试,忘了是不是有Linux支持,我一直用的是这个 邓振振 发表于 2021-12-22 20:54
感谢,我想问一下你知道有没有支持.net core加密的软件,加密后的可以在linux运行。准备买dnguard旗舰版 ...
还有那个dnguard只有一年服务吧 学习了,谢谢分享。 小白看不懂系列 很专业,收藏起来慢慢学习