关于TMD的脱壳的问题请教公孙秒秒大神 谢谢
本帖最后由 jionychiow 于 2019-10-30 20:59 编辑我前几天看了论坛里面的手脱tmd壳的帖子 就去找了个tmd壳的软件试试 ,刚开始oep什么的都可以按照他的方法来,很容易就找到了oep ,可是我发现我的壳和他的不太一样。我对写脚本一窍不通所以才来求助的
希望大家给点建议谢谢群内大神的手脱TMD的贴子。
链接直达https://www.52pojie.cn/thread-948686-1-1.html
附上我的调试记录
内存写入断点后 F7 F8再F9运行
前三个循环的图片。都一样的。只是开头不一样
得到的第一个代码
第一
01D0DB38 8F00 pop dword ptr ds:
堆栈 =06E13319
ds:=00000000
将eax到数据压入堆栈
第二1
01D0DB3A 8100 E070D16F add dword ptr ds:,0x6FD170E0
ds:=06E13319
将数据加一是不是地址前加一?不懂
第三2
01D0E01E AA stos byte ptr es:
al=90
es:==90
向地址写入单字节90
第四3
01D0E05F AA stos byte ptr es:
al=E8
es:==90
向第三加一的地址写入e8 此e8为call
第五4
01D0E161 AB stos dword ptr es:
eax=766B2EAE
es:==90909090
向第三加一到地址写入四个90为加密到函数api 也就是第二写入90是空了一位90 六个NOp也就是90 用了5个来写call
到这里第一个循环就完了 这里是在六个90 就是NOP 在前面空出第一个nop 在写入E8
第六5
01D0E089 AA stos byte ptr es:
al=E8
es:==90
这里写入E8 但是我发现没有写入90 空一位出来
而这里却是前面不空,直接写E8 最后剩下的才是NOP 不写入了
第七6
01D0E0EE 8803 mov byte ptr ds:,al
al=A1
ds:=90
这里多了一个写单字节。会变动的。知道是什么。在数据框里面查看的话。会发现不是六个一组的NOP组成的。而是很多个。
也不清楚呀。
00529672- E9 90909090 jmp 90E32707
00529677 90 nop
00529678 90 nop
00529679 90 nop
0052967A 90 nop
0052967B 90 nop
0052967C 90 nop
0052967D 90 nop
0052967E 90 nop
0052967F 90 nop
00529680 90 nop
00529681 90 nop
00529682 90 nop
00529683 90 nop
00529684 90 nop
00529685 90 nop
00529686 90 nop
00529687 90 nop
00529688 90 nop
00529689 90 nop
0052968A 90 nop
0052968B 90 nop
0052968C 90 nop
0052968D 90 nop
0052968E 90 nop
0052968F 90 nop
00529690 90 nop
00529691 90 nop
00529692 90 nop
00529693 90 nop
00529694 90 nop
00529695 90 nop
00529696 90 nop
00529697 90 nop
00529698 90 nop
00529699 90 nop
0052969A 90 nop
0052969B 90 nop
0052969C 90 nop
0052969D 90 nop
0052969E 90 nop
0052969F 90 nop
005296A0 90 nop
005296A1 90 nop
005296A2 90 nop
005296A3- E9 A8A84F71 jmp ws2_32.ioctlsocket
005296A8 90 nop
F9运行后 经常AB特征 后变成了MOV语句。。
00529672- E9 DEBC4F71 jmp ws2_32.gethostbyname
00529677 A1 90909090 mov eax,dword ptr ds:
0052967C 90 nop
0052967D 90 nop
0052967E 90 nop
0052967F 90 nop
00529680 90 nop
00529681 90 nop
00529682 90 nop
00529683 90 nop
第八7
01BC3A50 8938 mov dword ptr ds:,edi
edi=04030000
ds:=00000000
第九8
01BFB3C2 8803 mov byte ptr ds:,al
al=E8
ds:=90
第十9
01C450B6 891F mov dword ptr ds:,ebx
ebx=03B039F9
ds:=90909090
后边这里就是结束了 是mov的语句
这里是OEP。。绝对是正宗的。呵呵没有被代码
oep
0052A88C 55 push ebp
真实API
01D0E34E 8985 60FF951A mov dword ptr ss:,eax ; kernel32.lstrcpynA
eax=7C8101B1 (kernel32.lstrcpynA)
ss:=7C80E906 (kernel32.FileTimeToLocalFileTime)
不知道对不对呀
附上脚本 如何修改。请教。大家。谢谢
//变量声明区
var moveax //用来存放通过mov方法写入iat的地址(第一处)
var moveax1 //这里是我加入的。我觉得和原作者的差不多的东西只是加了数。应该没有什么能获取的地方
var movapi //用来存放真实api出现的地方(007F0136这个地址)
var stos390 //存放stos三连方法中写入90的地方(第二处)
var stos3e8 //存放stos三连方法中写入E8的地方(第三处)
var ab //存放独一无二的ab的地方,就是stos三连和二连都用上了的那个写入双字的地方,他的命令机器码是AB,其他的都是AA(第四处)
var stos2 //stos二连方法中写入E8的地方(第五处)
var paichu //需要忽略的语句,我们找到的不需要修补和获取数据的地方(第八处)
var recall //用来放FF15的,这样直接写recall就代替写FF15,如果要改成别的也好修改
var rejmp //用来放FF25的,这样直接写rejmp就代替写FF25,如果要改成别的也好修改
var oep //用来存放OEP的,其实用不上oep,省略了吧
var iat //用来存放对应iat的值,也就是存放了api真实地址的地址,这个可能有点绕,如果你知道什么是iat就又会觉得这里很罗嗦其实
var api //用来存放真实API地址
var popbyte //这里其实mov,因为另一句是POP开始的,所以我写成这样好分辨,是非stos方法中写入E8的地方(第七处)
var popdword //用来存放非stos方法中写入双字的地方(第六处)
var mark //存放临时数据用的变量
var count //存放临时数据的变量
var lsg //存放临时数据的变量
var code //存放代码段起始地址,用于下断
var size //存放代码段大小,用于下断
var addr //用来存放获取到的需要修复的代码地址
//赋值区域,变量需要声明才能使用,下面开始把我们找到的数据进行赋值:
mov movapi,01D0D04A
mov stos390,01D0E01E
mov stos3e8,01D0E05F
mov ab,01D0E161
mov stos2,01D0E089
mov moveax,01D0DB38
mov moveax1,01D0DB3A //这里是我自己加上的。要对应上边的
mov popdword,01BC3A50
mov paichu,01C450B6
mov popbyte,01BFB3C2
mov code,401000
mov size,16F8000
mov recall,15ff //这里要注意,要反着写,这里OD写入的方式是默认从右向左一个字节一个字节写的,我们要写入FF15这里就要按自己写成15ff
mov rejmp,25ff //这里也是一样,原理什么的其实汇编里数据也是这样的,大端模式和小端模式的问题
//清除所有类型的所有断点,防止停在不必要的地方影响脚本运行
bc //这句清楚普通断点
bphwcall //这句清楚所有硬件断点
//这里我没有写清楚内存断点的语句,因为每次重载过后其实内存断点就没了,需要的话也可以写上
//下断点及前置运行,因为直接运行会走到 rep movs byte ptr es:,byte ptr ds: 这句,这里要F7一下,F8一下,才能继续F9
bphws movapi,"x" //这句是在movapi的位置下硬件执行断点
bpwm code,size //在code处,size大小上下内存写入断点
esto //shift+f9的意思,F9是RUN
sti//F7
sto//f8
esto
//上面这段执行完其实刚好停在了movapi的位置,我们可以直接获取API地址了
//这段获取API,然后运行一下直接跳去对比中心对比到了哪了,然后在跳到对应的地方
getapi:
mov api,eax //从eax里得到真实的api地址然后存在api这个变量里面
esto//运行一下
jmp cmps //执行完毕跳到对比中心
//这里处理所有可能出现的地址,然后丢到该去的地方处理
cmps:
cmp eip,movapi //对比eip,如果是movapi就跳去获取真实api
je getapi
cmp eip,stos390 //对比如果是stos三连中写入90的地方,跳过去执行一下再回来
je justrun
cmp eip,stos3e8 //STOS3连写入E8,这里其实可以开始处理了,他后面必然跟着一个ab语句写入双字完成修改调用
je clab
cmp eip,stos2 //STOS二连写入E8,他后面必然跟着一个ab语句写入双字完成修改调用,忽略90语句其实就是为了这里好写,可以把两处并做一处
je clab
cmp eip,popbyte//非stos方法写入E8的地方,要开始获取地址进行处理了
je clpop
cmp eip,popdword //这句上面说了有两种可能,我们在clpop里已经处理了一种可能,如果直接出现很显然是另一种可能,我们单独做处理
je cldw
cmp eip,moveax //这里不用说了吧
je clmoveax
cmp eip,moveax1
je clmoveax1//这两句也是
cmp eip,paichu //这也没啥说的了吧
je justrun
cmp eip,paichu1
jeclab
//如果全部都不是,其实也就说明修复完了,脚本执行完毕了,直接清除所有断点结束脚本进入常规手段脱壳
bc
bphwcall
bpmc //清楚内存断点
msg "end" //弹个窗,可以不写
ret
//这个方法用来在需要忽略的时候运行一下程序到下一个点,然后对比
justrun:
esto
jmp cmps
//这里用来处理非stos方法中写入双字的另一种可能,也就是在对iat进行写入
cldw:
mov iat,edx //用iat保存edx,这个时候edx的值就是iat的地址
esto //然后运行一下,让壳先写,不然你修改了壳执行完语句又写回去了,白写了
mov ,api //这里是把真实的api地址写入到iat对应的地址单元
//这两句是分别把iat的值和api的值追加到"C:\ODLOG\u.txt"这个文件中,是我用来记录数据的,可以删除
wrta "C:\ODLOG\u.txt",iat
wrta "C:\ODLOG\u.txt",api
//这个语句如果填写中的文件不存在会创建,但是如果路径当中的文件夹不存在是不会创建的,直接报错
//因为是追加,所以记录之前要记得删掉以前的,也可以在一开始用个非追加的语句把这个文件清空,防止上次的干扰
jmp cmps //跳回对比中心
//处理moveax
clmoveax://这个其实跟上面是一模一样的,不过他们不同的是一个是从eax里取值,一个是从edx取值,所以写了两段
mov iat,eax
esto
mov ,api
wrta "C:\ODLOG\u.txt",iat
wrta "C:\ODLOG\u.txt",api
clmoveax1://这里多增加了不然出错了
mov iat,eax
esto
mov ,api
wrta "C:\ODLOG\u.txt",iat
wrta "C:\ODLOG\u.txt",api
jmp cmps
//处理popbyte的地方,这里思路上首先获取到需要修补的地址,然后我们要先判断这个地址对不对,因为我们这里其实要写入6个字节的
clpop:
mov addr,edx//这里获取到要修改的地方
mov mark,addr //这里用mark先保存下addr的值,我们要进行判定写入的适当位置,因为壳修改调用其实是把6个字节的本来数据改成了5个字节并填充了一个nop
//但是这个nop有时候是在前面的,有时候是在后面的,那么我们就要判断下我们这个开始写入的位置究竟对不对了
//判断的方法其实就是读取从addr开始,后面有几个NOP,
//有4个的话就说明这个nop在这个E8前面,所以我们就要把addr减一了
//有五个的话我们就能直接写
//有6个的话就说明这里可能有一串调用连在一起,我们就在判断前面有没有NOP,前面没有那我们直接写没问题,前面有的话那我们就停下来手动跳过去数一下
//然后手动修正ADDR的值,然后继续执行脚本进行下一步,我一开始没有这个判断,很多修复错位,导致无法运行
mov count,0 //count置零用来保存后面的nop的数量
//这里写一个循环不停的对比内存单元中指令,如果是nop就把count加一,不是nop了就跳走,然后根据count的值来判定究竟有几个nop并做对应处理
clpop1:
inc mark //自增一
cmp ,#90#,1 //这句是取出mark所对应地址里面的值跟##中间的内容进行对比,需要对比几个字节由后面那个数字决定,这里对比一个字节就行了
jne clpopnop //不等于话才跳的!!!!
inc count //等于就向下执行,count+1
cmp count,6 //这里数到6了就要对前面进行判定了,为什么上面说了
je clpoperro
jmp clpop1
//这是大于5个nop处理的地方
clpoperro:
mov mark,addr //再把addr给mark
dec mark//mark减一
cmp ,#90#,1 //就是判断addr前面的这个值是不是nop
jne clpop2 //逻辑上已经说明了
pause //是nop就暂停下来,手工改
jmp clpop2
//这段根据nop的数据决定是否修改addr,也就是我们开始写入的地址,逻辑上面已经说过了,不解释了
clpopnop:
cmp count,4
jb clpoperro//这里如果小于4那肯定出错了,直接跳向手工处理的位置
cmp count,5
je clpop2
ja clpoperro //这里其实是等于6的情况了,也可以改上面省下来这句
dec addr //addr减一
clpop2:
esto //这里要先运行一下,然后判断走到哪了,因为有可能会走到需要排除的地方,所以加一个判断,同时stos方法中的东西其实可以也集成到这里
//从上面一段开始其实三种方法是共用的了,为了看的清楚我写了两段
clpop22:
cmp eip,popdword //如果后面是写入双字的地方,其实就可以进入下一环了
je clabpanduane9
cmp eip,paichu //如果是需要忽略的语句,就运行一下
je clpoprun
//如果都不是,那就是出错了,直接弹窗口吧,看看逻辑上哪里出错了
msg "error"
pause
//这里跟justrun其实是一样的,就是结尾他们要跳的地方不一样
clpoprun:
esto
jmp clpop22
//处理stos方法的地方了
clab:
mov addr,edi //获取要修改的地方的地址
mov mark,addr //这里开始跟上面是一样的其实,可以直接合并到上面去,然后在对比那里加一个判断是不是ab就后了
mov count,0
clab1:
inc mark
cmp ,#90#,1
jne clabnop
inc count
cmp count,6
je claberro
jmp clab1
clabnop:
cmp count,4
jb claberro
cmp count,5
je clstos2
ja claberro
dec addr
clstos2:
esto
cmp eip,ab
je clabpanduane9
cmp eip,paichu
je clstos2
msg "error"
pause
claberro:
mov mark,addr
dec mark
cmp ,#90#,1
jne clstos2
pause
jmp clstos2
//这里开始下一环,上面判断过后,这里必然就是改写调用写入偏移的地方了,我们直接运行一下,让壳完成对于调用的改写,这样我们才能开始往回改
clabpanduane9:
esto
//这里要判断一下,究竟是写入rejmp还是recall了,到了这里ADDR经过上面的判定必然是正确的地址了,我们对他的位置进行判定,如果是E9,那就说明
//这里是个jmp了,如果不是我们在判断后面一位是不是E9,都不是那肯定就是E8了,也可以判断是不是E8,都不是就肯定是E9了
mov mark,addr
cmp ,#e9#,1
je clabjmp
inc mark
cmp ,#e9#,1
je clabjmp
//这里开始写入,没什么好说的了,写完了直接跳会去进行下一轮判断
clabcall:
mov ,recall
wrta "C:\ODLOG\u.txt",addr
wrta "C:\ODLOG\u.txt",recall
jmp clabdw
clabjmp:
mov ,rejmp
wrta "C:\ODLOG\u.txt",addr
wrta "C:\ODLOG\u.txt",rejmp
clabdw:
add addr,2
mov ,iat
wrta "C:\ODLOG\u.txt",addr
wrta "C:\ODLOG\u.txt",iat
jmp cmps
有没有大神帮忙解答呀。谢谢 有没有人帮忙一下。谢谢了。。 加我660可以15Q30吗 看来要沉下去了 求教大神呀 自己求自己吧。。
页:
[1]