NoobyProtect 1.0.0.1 最小保护 完全修复 手记
第一句:膜拜Nooby,hc版主
第二句:感谢yangjt的热心解答测试,感谢sessiondiy的大力支持
天哪,这玩意太变态了。这个壳拖拖拉拉搞了近半年,终于完成了1个脱壳……汗……还是1001的最小保护……
不过不管怎么说终于修复成功了。第一次觉得notepad的界面如此亲切……[s:12]
进入正题,这个手记将会分5部分写出
1 NoobyProtect 1.0.0.1最小保护 加密技术概貌
2 脚本实现分析
3 脚本使用方法
4 脚本插件出现的BUG
5 展望未来的np
==========================================================================
1 NoobyProtect 1.0.0.1最小保护 IAT加密技术概貌
最小保护没有加密代码段。
但是处理IAT极为猥琐
比如说开头这段
正常的程序为
0100739D > $ 6A 70 PUSH 70
0100739F . 68 98180001 PUSH NOTEPAD.01001898
010073A4 . E8 BF010000 CALL NOTEPAD.01007568
010073A9 . 33DB XOR EBX,EBX
010073AB . 53 PUSH EBX ; /pModule => NULL
010073AC . 8B3D CC100001 MOV EDI,DWORD PTR DS:[<&XXXXXXXXXXXtModu>; |XXXXXXXXXXXtModuleHandleA010073B2 . FFD7 CALL EDI ; \GetModuleHandleA
而np程序为
可以看到,由于10073B2还是CALL EDI,说明np还把类似 mov e**,dword ptr[iat]一类的代码给抽走
再看一段正常的
01007411 POP ECX
01007412 OR DWORD PTR DS:[100AB9C],FFFFFFFF
01007419 OR DWORD PTR DS:[100ABA0],FFFFFFFF
01007420 CALL DWORD PTR DS:[<&msvcrt.__p__fmode>] ; msvcrt.__p__fmode
被np后
01007411 POP ECX
01007412 OR DWORD PTR DS:[100AB9C],FFFFFFFF
01007419 OR DWORD PTR DS:[100ABA0],FFFFFFFF
01007420 CALL NOTEPAD_.0105042E
可见,call [iat]也被抽走了
而被抽走的是什么模样呢
0105042E PUSHAD
0105042F PUSHFD
01050430 CALL NOTEPAD_.01050435
01050435 MOV EBX,DWORD PTR SS:[ESP]
01050438 ADD EBX,23
0105043B XOR EAX,EAX
0105043D XCHG DWORD PTR DS:[EBX-4],EAX
01050440 CMP EAX,0
01050443 JE SHORT NOTEPAD_.0105044C
01050445 XOR BYTE PTR DS:[EBX],0D0
01050448 INC EBX
01050449 DEC EAX
0105044A JNZ SHORT NOTEPAD_.01050445
0105044C POP EAX
0105044D POPFD
0105044E POPAD
0105044F JMP NOTEPAD_.01050458
像IMP之类的东西只能跳楼了,关于IAT的具体技术分析,
参见(建议参照两部分内容看2)
yangjt的“NoobyProtect SE Demo 1.6.1.0 IAT加密初探 ”
XXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXp?tid=39288&extra=page%3D3我的“NPSE 1.0.0.1 API保护 一点点的研究”
XXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXp?tid=30985 ==========================================================================
2 脚本实现分析
面对满屏的CALL ***,有什么好办法?很自然想到脚本
脚本大概做的工作是这几样
1 搜索所有的call,如果是加密的call,就继续,否则跳过
2 对call的handler解密,获取API的名称和DLL名称,并得到该DLL的本机地址
3 将本机地址写入新内存,将该CALL修复成原来代码
下面将具体分析……
脚本初始化
进行搜索
这里需要用到真实的例子讲解
我们不难发现,所有的call handler都是这样的
0105042E 60 PUSHAD
0105042F 9C PUSHFD
01050430 E8 00000000 CALL NOTEPAD_.01050435
01050435 8B1C24 MOV EBX,DWORD PTR SS:[ESP]
01050438 83C3 23 ADD EBX,23
0105043B 33C0 XOR EAX,EAX
0105043D 8743 FC XCHG DWORD PTR DS:[EBX-4],EAX
01050440 83F8 00 CMP EAX,0
01050443 74 07 JE SHORT NOTEPAD_.0105044C
01050445 8033 D0 XOR BYTE PTR DS:[EBX],0D0 //******
01050448 43 INC EBX
01050449 48 DEC EAX
0105044A ^ 75 F9 JNZ SHORT NOTEPAD_.01050445
0105044C 58 POP EAX
0105044D 9D POPFD
0105044E 61 POPAD
0105044F E9 04000000 JMP NOTEPAD_.01050458
很显然,1050445是最关键一句,因为他直接解码了下面jmp以后的程序,我们findop b,#8033??#要找的就是这句话
而??显然就是xor的常数
mov xorstart,b
add xorstart,2a
add xorstart,1b
mov temp1,[xorstart+1]
mov eax,[xorstart+8]
为什么xorstart=关键地址+2a+1b?
01050170 68 75133576 PUSH 76351375
01050175 810424 8EEDCF8A ADD DWORD PTR SS:[ESP],8ACFED8E
运行
d 76351375+8acfed8e,
6B 65 72 6E 65 6C 33 32 2E 64 6C 6C kernel32.dll
00 47 65 74 4D 6F 64 75 6C 65 48 61 6E 64 6C 65 41 00 GetModuleHandleA.
嘿嘿,想到了什么?
原来NPSE为了避免给API下断点(其实就算下了也不会断下,请见参考文章),每次运行API的时候都会检测原来的
API有没有断点,有了断点自然就让你happy一下,重新开始了
我们就从这里入手,拿到函数的地址
然后是
#inc "fixiat.txt"
我们来看看……
有了这个剩下的也不好办,因为还要判断被和谐的是哪种类型的
对比比较
这是抽掉mov edi,[iat]类型的
010501C5 83C4 04 ADD ESP,4
010501C8 61 POPAD
010501C9 64:8F05 00000000 POP DWORD PTR FS:[0]
010501D0 83C4 04 ADD ESP,4
010501D3 8B3D EF010501 MOV EDI,DWORD PTR DS:[10501EF]
010501D9 830424 01 ADD DWORD PTR SS:[ESP],1
010501DD C3 RETN
这是抽掉call [iat]类型的
010504CC D383 C4046164 ROL DWORD PTR DS:[EBX+646104C4],CL
010504D2 8F05 00000000 POP DWORD PTR DS:[0]
010504D8 83C4 04 ADD ESP,4
010504DB FF6424 D4 JMP DWORD PTR SS:[ESP-2C]
比较发现什么?
把第二个call [iat]的程序弄得正常一点
010504CD 83C4 04 ADD ESP,4
010504D0 61 POPAD
010504D1 64:8F05 00000000 POP DWORD PTR FS:[0]
010504D8 83C4 04 ADD ESP,4
010504DB FF6424 D4 JMP DWORD PTR SS:[ESP-2C]
当两次add esp,4后,call [iat]执行的是Jmp,因为函数已经结束,而mov e**,[iat]还
必须进行赋值,并且跳过一个字节
嘿嘿……那还犹豫什么?
不幸的是
某些特殊函数(目前估计是原来的jmp [iat])不是add esp,4
01050042 83C4 08 ADD ESP,8
01050045 FF6424 D0 JMP DWORD PTR SS:[ESP-30]
所以我们就只能搜索#83c4#了
看代码
到这里还都挺顺利的,为啥还要判断是不是EAX
呵呵,这又是一个脚本的BUG。
编译mov eax,dword ptr[401000]时,脚本会给你写成
0100739D > 8B05 00104000 MOV EAX,DWORD PTR DS:[401000]
而实际应该是
0100739D > A1 00104000 MOV EAX,DWORD PTR DS:[401000]
可见脚本修改的多占了一个字节,那下面一句基本就完蛋了
所以针对是不是EAX的问题还要手修一下
终于到最后了……
==========================================================================
3 脚本使用方法
1 到达OEP;)估计先运行后再DUMP,用插件找也行(感谢hc版主)
2 运行脚本,泡杯咖啡
3 用UIF修复
对于UIF的使用方法介绍一下(感谢yangjt)
4 DUMP,注意顺序!
5 用IMP修复,不用说了吧
6 用PE工具删掉TLS,优化OK
如果还出现问题,那又是脚本的BUG。比如修复这个记事本时
呵呵,你只有先改下脚本
//cmp $RESULT,10047a4(本例)
//jnz debug_1
//pause
改完了一直TAB,直到
readstr [shijidll],$RESULT
mov sdll,$RESULT
len [shijifunc]
readstr [shijifunc],$RESULT
知道API后记录,让他运行,结束后再到那个CALL里手工修复一下,再UIF……
==========================================================================
4 脚本插件出现的BUG
群里的朋友请原谅我那天的不冲动,当一个脚本运行10几分钟以后出现一个BUG让你前功尽弃,
就算神也要发火的…………
总之先膜拜插件作者,接着提出一些很不方便的BUG,顺便提供下临时的解决方案,以备下次使用
1 inc或dec几次后莫名其妙变成别的值
试下
mov temp,***
inc temp
mov ***,temp
2 有时候mov eax,temp会出现错误
先执行一句 mov temp,0
(尽管你原来的temp就是数值型的)
3 对eax不优化导致的问题
手填……
4 gpa出错
……暂无解决方法
==========================================================================
5 展望未来的np
其实np是个很ws的……
建议更加ws
1 取消对原api的0cch判断
2 加载DLL内存镜像后删除原DLL的输入表信息
3 ……无语……再WS就不是人了
膜拜nooby
==========================================================================
另外再次感谢yangjt
200字以内,仅用于支线交流,主线讨论请采用回复功能。