NoobyProtect 1.0.0.1 最小保护 完全修复 手记
NoobyProtect 1.0.0.1 最小保护 完全修复 手记第一句:膜拜Nooby,hc版主
第二句:感谢yangjt的热心解答测试,感谢sessiondiy的大力支持
天哪,这玩意太变态了。这个壳拖拖拉拉搞了近半年,终于完成了1个脱壳……汗……还是1001的最小保护……
不过不管怎么说终于修复成功了。第一次觉得notepad的界面如此亲切……:lol
进入正题,这个手记将会分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:[<&KERNEL32.GetModu>; |kernel32.GetModuleHandleA
010073B2 .FFD7 CALL EDI ; \GetModuleHandleA
而np程序为
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
010073AC E8 638D0400 CALL NOTEPAD_.01050114 //这句被和谐了
010073B1 2F DAS
010073B2 FFD7 CALL EDI
可以看到,由于10073B2还是CALL EDI,说明np还把类似 mov e**,dword ptr一类的代码给抽走
再看一段正常的
01007411POP ECX
01007412OR DWORD PTR DS:,FFFFFFFF
01007419OR DWORD PTR DS:,FFFFFFFF
01007420CALL DWORD PTR DS:[<&msvcrt.__p__fmode>] ;msvcrt.__p__fmode
被np后
01007411POP ECX
01007412OR DWORD PTR DS:,FFFFFFFF
01007419OR DWORD PTR DS:,FFFFFFFF
01007420CALL NOTEPAD_.0105042E
可见,call 也被抽走了
而被抽走的是什么模样呢
0105042E PUSHAD
0105042F PUSHFD
01050430 CALL NOTEPAD_.01050435
01050435 MOV EBX,DWORD PTR SS:
01050438 ADD EBX,23
0105043B XOR EAX,EAX
0105043D XCHG DWORD PTR DS:,EAX
01050440 CMP EAX,0
01050443 JE SHORT NOTEPAD_.0105044C
01050445 XOR BYTE PTR DS:,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加密初探 ”
http://www.unpack.cn/viewthread.php?tid=39288&extra=page%3D3
我的“NPSE 1.0.0.1 API保护 一点点的研究”
http://www.unpack.cn/viewthread.php?tid=30985
==========================================================================
2 脚本实现分析
面对满屏的CALL ***,有什么好办法?很自然想到脚本
脚本大概做的工作是这几样
1 搜索所有的call,如果是加密的call,就继续,否则跳过
2 对call的handler解密,获取API的名称和DLL名称,并得到该DLL的本机地址
3 将本机地址写入新内存,将该CALL修复成原来代码
下面将具体分析……
脚本初始化
gmi eip,CODEBASE
mov findtemp,$RESULT
alloc 2000
add $RESULT,100 //做IAT用
mov newiat,$RESULT
mov nnewiat,newiat
进行搜索
startfind:
findop findtemp,#E8????????# //查找所有call
cmp $RESULT,0//找不到就大功告成啦
je overfind
mov savelast,$RESULT //存一下找到的地址
mov temp,[$RESULT+1]
add temp,$RESULT
add temp,5
mov myeip,temp
mov temp,
cmp temp,e89c60//对该CALL的头3字节判断,以达到判断是否为np加密的call
jnz next //不是去死
mov b,myeip
mov calleip,myeip
findop b,#8033??#//找到关键一句
mov temp1,$RESULT
add temp1,2
mov xorbyte, //得到xor的常数
mov ecx,xorbyte
mov xorbyte,cl//将xor的常数赋到xorbyte中
这里需要用到真实的例子讲解
我们不难发现,所有的call handler都是这样的
0105042E 60 PUSHAD
0105042F 9C PUSHFD
01050430 E8 00000000 CALL NOTEPAD_.01050435
01050435 8B1C24 MOV EBX,DWORD PTR SS:
01050438 83C3 23 ADD EBX,23
0105043B 33C0 XOR EAX,EAX
0105043D 8743 FC XCHG DWORD PTR DS:,EAX
01050440 83F8 00 CMP EAX,0
01050443 74 07 JE SHORT NOTEPAD_.0105044C
01050445 8033 D0 XOR BYTE PTR DS:,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,
mov eax,
为什么xorstart=关键地址+2a+1b?
01050170 68 75133576 PUSH 76351375
01050175 810424 8EEDCF8AADD DWORD PTR SS:,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一下,重新开始了
我们就从这里入手,拿到函数的地址
add xorstart,1b //解码
mov temp1,
mov eax,
mov temp2,eax
xor temp1,xorbyte
xor temp2,xorbyte
shl xorbyte,8
xor temp1,xorbyte
xor temp2,xorbyte
shl xorbyte,8
xor temp1,xorbyte
xor temp2,xorbyte
shl xorbyte,8
xor temp1,xorbyte
xor temp2,xorbyte
add temp1,temp2
mov shijidll,temp1
find temp1,#2e646c6c# //因为函数名和dll名是连着的,所以搜索.dll
add $RESULT,5 //+5就是函数名
mov shijifunc,$RESULT
然后是
#inc "fixiat.txt"
我们来看看……
len //得到dll名长度
readstr ,$RESULT //读取dll名
mov sdll,$RESULT
len //得到function长度
readstr ,$RESULT//读取function名
mov sfunc,$RESULT
gpa sfunc,sdll //得到函数本机地址
mov realapi,$RESULT
mov ,realapi //写入新地址IAT
有了这个剩下的也不好办,因为还要判断被和谐的是哪种类型的
对比比较
这是抽掉mov edi,类型的
010501C5 83C4 04 ADD ESP,4
010501C8 61 POPAD
010501C9 64:8F05 00000000 POP DWORD PTR FS:
010501D0 83C4 04 ADD ESP,4
010501D3 8B3D EF010501 MOV EDI,DWORD PTR DS:
010501D9 830424 01 ADD DWORD PTR SS:,1
010501DD C3 RETN
这是抽掉call 类型的
010504CC D383 C4046164 ROL DWORD PTR DS:,CL
010504D2 8F05 00000000 POP DWORD PTR DS:
010504D8 83C4 04 ADD ESP,4
010504DB FF6424 D4 JMP DWORD PTR SS:
比较发现什么?
把第二个call 的程序弄得正常一点
010504CD 83C4 04 ADD ESP,4
010504D0 61 POPAD
010504D1 64:8F05 00000000 POP DWORD PTR FS:
010504D8 83C4 04 ADD ESP,4
010504DB FF6424 D4 JMP DWORD PTR SS:
当两次add esp,4后,call 执行的是Jmp,因为函数已经结束,而mov e**,还
必须进行赋值,并且跳过一个字节
嘿嘿……那还犹豫什么?
不幸的是
某些特殊函数(目前估计是原来的jmp )不是add esp,4
01050042 83C4 08 ADD ESP,8
01050045 FF6424 D0 JMP DWORD PTR SS:
所以我们就只能搜索#83c4#了
看代码
find calleip,#83c4#
add $RESULT,3
find $RESULT,#83c4#
add $RESULT,3 //找两次add esp,?,再到下一行代码
mov ebx,[$RESULT]
cmp bl,ff //首字节判断
jnz fixexx //如果不是jmp dword (call ),就走
mov ,#FF15# //call 的头两个字节是FF15,
mov temp,savelast
add temp,2
mov ,newiat //填入IAT
jmp overiat
fixexx:
opcode $RESULT //case 如果不是jmp
//此时的代码为MOV E??,DWORD PTR DS:
mov temp,$RESULT_1
alloc 100
mov [$RESULT],temp //到个临时空间里玩玩
add $RESULT,4
readstr [$RESULT],3 //读取E??
mov temp,savelast
scmp $RESULT,"EAX",3
到这里还都挺顺利的,为啥还要判断是不是EAX
呵呵,这又是一个脚本的BUG。
编译mov eax,dword ptr时,脚本会给你写成
0100739D > 8B05 00104000MOV EAX,DWORD PTR DS:
而实际应该是
0100739D > A1 00104000 MOV EAX,DWORD PTR DS:
可见脚本修改的多占了一个字节,那下面一句基本就完蛋了
所以针对是不是EAX的问题还要手修一下
scmp $RESULT,"EAX",3
jnz fixexx_1
mov ,a1//eax填入
inc savelast
mov ,newiat
jmp overiat
fixexx_1:
mov temp2,"mov " //如果不是eax
add temp2,$RESULT
add temp2,",dword ptr["
itoa newiat
add temp2,$RESULT
add temp2,"]" //以上字符串组合就是mov e**,
asm savelast,temp2 //修复一下
终于到最后了……
opcode savelast
add savelast,$RESULT_2 //为查找下一个做准备
mov findtemp,savelast
jmp startfind
==========================================================================
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 ,$RESULT
mov sdll,$RESULT
len
readstr ,$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 上传附件
脚本
npse101
试炼品
完成品(NAG没去)
{:299_842:}
我是来膜拜楼主的! 看不懂。观望中。。。。 2# NONAME剑人
感谢发布经验~附件最少能传1M的吧? 我是来膜拜的…… 完完全全看不懂呀 2# NONAME剑人
感谢发布经验~附件最少能传1M的吧?
Hmily 发表于 2009-8-21 12:35 http://www.52pojie.cn/images/common/back.gif
汗,那个大概2MB左右,加了点音乐:lol
还有NPSE101的原版,估计绝版了 脱壳用的吧 再次感谢版主:loveliness: