首页
社区
课程
招聘
学习脱壳 ASProtect 2.1x SKE 的详细笔记
发表于: 2024-9-7 17:39 7954

学习脱壳 ASProtect 2.1x SKE 的详细笔记

2024-9-7 17:39
7954

本文是学习学习脱壳之 ASProtect 2.1x SKE 简单加壳的 98NOTEPAD(MC++),原文链接(https://bbs.kanxue.com/thread-24450.htm)
所做的笔记,文中有对一些存在的问题进行修正,还对脱壳过程中的思路进行详细的说明。

1.需要用的工具有WIn10系统,吾爱破解OD,IDA8.3,ImPortRec1.7,LordPe—WIn10修正版,都可以在吾爱破解上找到。
2.ASProtect 2.1x SKE这款壳不像UPX,Aspack等压缩壳,很容易就脱掉,其中需要做的事情有,寻址OEP,修复IAT,恢复被替换的代码(比如说程序调用MessageBoxA这个API,应该是CaLL MessageBoxA,被替换成Call XXXXXX,XXXXX是个固定的地址),修复一些BUG。
3.在开始之前需要对Od进行设置 其中第一个HidePEB,
是对抗壳反调试,不选上运行会弹出提示,ShowBar的话是为了能够使用底部的命令行。第二是,要对Od的异常处理进行设置,选项-》调试设置-》异常把所有勾都去掉
这边涉及的异常是非法访问内存,比如说eax=0,mov ebx,[eax],最下面的同时忽略一些指定的异常或范围,00000000....FFFFFFFF,表示把任何异常都忽略,连硬件断点都会失效。
4.寻找OEP,设置好刚才的就把NotePad拖入OD,会弹出一个对话框意思是这个段代码被压缩过需要分析随便点是或否都行。接着到了入口点

这边位置还在代码段,这段代码是壳的代码杂乱无章的,一看就没了兴趣,别管这些,我们只要知道壳,运行n个代码后会申请一段内存,把代码放到上面跑,其中主要做了两件事解密原始程序的代码,制造内存访问错误异常,当然还有反调试,按下30多次 Shift+F9(忽略异常继续执行) 命令后程序跑起来了。这边不需要数运行多少次只要找到第二次出现

这个指令就到最后一次异常了,此时原程序代码已经被解密,也是离壳执行到原程序入口最近的地方了,这时候按下快捷键ALT+M找到401000按下F2给这个代码块下断点, ,再按一次Shift+F9,程序就停在了程序入口点。
这时候代码被OD识别成了数据,我们点击右键-》分析-》从模块中删除分析
这是代码就被OD正常识别了

我们可以看到4010d3处的代码被修改成了 call 02410000,(02410000这个地址每次运行都有可能不一样)而且不止一处被修改,很显然我们需要还原他。往下翻我们找到

4010e2这一处并没有被替换,我们知道壳把部分的调用API函数的代码都改成了Call 02410000 ,我们接着使用这个0x4064F4地址在底部命令行按下dd 0x4064F4 (upload/attach/202409/1004848_E6TUGT7ENK8X2NR.webp)
可以看到406504 0000000 ,这个000000表示用来分割不同DLL函数地址的,利用这个我们就可以找到IAT表的开始的地址,00406508 E1022275

上面这些都已经被加密了,没被加密的话应该是7开头的地址
后面还有备注函数名。我们向上翻找到4062f0,这边可以看到这个函数名advapi32.RegOpenKeyA,再往上数到第四个地址 4062e0 数值为00000000,这边就是分隔用的,下一个地址4062e4就是iat的开始了。
5.IAT还原,要还原IAT,就要看看壳对IAT表做了哪些事情,这样我们才有思路,这边我们刚开始需要做一件事情
点一下A字后面红色圆心的按钮,这样方便查看我们下了哪些硬件断点,有时候会出现一个问题,明明我们下了硬件断点但是打开查看却发现没有下,所以最好是不要关闭这个界面。接下来我们找到选项-》调试设置-》异常,把非法访问异常给勾上,这样做我们就不会老是断下,再Shift+F9忽略继续运行了,接着在命令行执行命令dd 0x4062e4,选中0x4062e4这一行IAT的起点,右键找到断点-》硬件写入-》byte,接着F9运行,看0x4062e4地址上的数值变化,运行到004062E4 的值变成8A14F25D,这时候把硬件断点删了,找到选项-》调试设置-》异常,把非法访问异常勾去掉,再Shift+F9,忽略异常运行,运行几次后出现 mov byte ptr ds:[ecx],0xB6 这个代码就可以了停了,因为在Shift+F9运行之后断下IAT表已经解密完了,就不能分析它的解密流程了。这个代码再跟一断时间就可以找到IAT解密的地方了,
接下去代码怎么跟踪,Shift+F9忽略异常运行?不完全对这段代码看起来杂乱无章后面几行还是可以给我们下断点的,
我们在pop ecx 这个指令下F2断点,因为这个指令的地址每一次运行都不一样我这边就不说地址了,接着Shift+F9运行就断在在pop ecx这个指令这边,接着找到选项-》调试设置-》异常,把非法访问异常勾去掉,现在就不需要异常打扰了咱们只要找到解密代码的函数在哪里就行了,现在就F8,单步一直走,
走到call 0xxx7818 停住继续F8你会发现IAT表部分被解密了并且注释里出现了很多函数名。我们可以确定这个函数包含了解密函数,要找到最深层的解密函数我们还需要跟进,同时这个函数
前三个和后三个都是指令

这就让我们可以很容易定位到它的位置,右键-》查找-》所有参考文本字串,找到文本字串为ASCII "85\r\n",双击点进去就可以找到call xxx7818 类似的函数,接下来F7单步步入这个函数内部,再F8单步一直走,
走到XXX795a的地方也就是函数结尾retn指令前面最后一个call下个断点,F9运行,EIP又在XXX795a断下,然后在iat数据窗口
可以看到此时已经有一个函数被还原了,注释里面也有了函数名,继续F9运行,EIP又在XXX795a断下,浏览iat数据窗口,发现没有变,重复上面的动作,发现有时候有新的API被还原有时候却没有,我们可以假设XXX795a,这个地址就是调用还原iat表的函数,而且每调用一次只还原一个API,为什么有时候调用却没有还原呢?带着这个问题我们跟进这个函数内部,F8在这个函数内部一直走如果走到函数返回,继续F9运行断下,F7步入,F8步过,
xxx7725这个地址就是对iat表的某一项把他API地址进行写入,只要执行到这边它就还原iat表,我们做个猜测,只要让他能够一直执行到这边,是不是把那些没有还原的iat表中的某些函数都给还原了,带着这个疑问我们在od上分析函数的执行流程显然很吃力,要是能够让他在ida上展示出来就好了,这个壳是零几年的到现在应该有20年了吧感觉现在用ida分析他有点像降维打击,现在ida太强大了,这段代码是在申请的内存中运行的,把notepad.exe拖到ida中分析找不到这段代码,那必须让ida去调试这个程序,然后再把包含这部分代码的内存块dump下来分析。我们现在是用OD在调试这个程序,我们可以先记住这个函数的地址方便等下用ida附加的时候找到这个代码位于哪个段,接着再把这个段dump下载分析,现在点击OD的插件-》StrongOD-》Detach,这时候调试器就从OD中剥离出来可以用IDA附加了,打开ida,选择Debuggger-》Attach-》LocalWindowsDebugger,
点击OK附加,按g键,出现一个名称Jmp to Address 意思是跳到哪个位置我们在输入框输入刚才记下的地址,可以看到
前面这个debug050,这个可能是debug056或者其他的数值,这个只是ida对内存块的命名,不用管它什么意思。接着点击debuggger-》terminate process,他会给个提示点yes,前面这些英文看不懂可以百度一下什么意思。
选择Current Segment 点OK,这样IDA就保存了这段内存中的代码,因为每次运行存放这段代码的内存基址都会变但是他们位于这段代码的相对地址是不会变的。代码地址=模块基址+相对地址,用这个公式可以算出Od中某个地址在ida出现的位置。现在我们可以通过ida找到XXX795a调用的xxx75B8函数在函数执行流程图的样子
可以看到xxx75B8函数内部中粉红色的代码是这样的

给 mov eax, [ebp+arg_8] 这段代码下F2断点,观察esi的值分别是96,89,77,当等于89的时候会还原iat,其他两个啥也不干,结合前面的流程图得出esi等于77的时候执行了右边的流程条件合适是有可能去解密iat代码的也就是执行到黄色的那部分代码,把esi的值都改为89,试过了程序出错,所以当esi等于77的时候,把他改为89的时候,成功了,iat都还原了。
可以在esi赋值的地方打补丁,重新把程序载入OD,先走一遍刚才的流程,再在Call 0xxx7818下F2断点,F9运行断下再取消断点,再在xxx75D6这边下F2断点,F9运行,断下之后再取消断点,,点击插件-》StrongOD-》Alloc Memory ,就是申请一段内存块

在OD中把这段代码改成jmp aaaaaaaa ,aaaaaaaa是 刚才申请得到的内存的首地址,申请新内存里面写个判断,

这边下F2断点,因为xxx7818这个函数执行完就IAT解密完成,现在是在这个函数内部,F9运行后断下,再取消断点,然后选中刚才打补丁的地方右键撤销选择处修改,这样是为了避免稍后有程序调用出错或者被检测修改了代码,可以看到IAT被完整的还原了。
6.恢复被替换的代码,现在用刚才讲的方法把程序运行到OEP,我们找到选项-》调试设置-》异常,把非法访问异常去掉,按照前面的方法,运行到OEP,找到选项-》调试设置-》异常,把非法访问异常勾上。到达OEP之后,程序已经被壳还原了,接着我们需要做的是恢复被替换的代码,这边我们还是利用刚才申请的那段内存写修复代码。
在此之前如果让自己想想怎么修复被修改的代码,是不是毫无头绪,我当时也是这么想的,不知道从何下手。没有任何提示。这就要跟踪这个call XXX0000了,既然这个CALL可以实现调用API函数功能那么函数内部一定会出现某个API函数,而且这个API函数跟XXX0000函数被调用的地址绑定了,所以这个CALL一共做了两件事情,先根据自身的位置获取对应的API,接着调用这个API。所以我们做个补丁在这个函数内部获取到API的地方,跳到我们写的代码中去修复。当然整个修复代码逻辑顺序是这样的,遍历代码块找到一处为Call XXX0000的地址,先记下该地址因为后面要根据这个地址去修改,然后跳到这个地址去执行
Call XXX0000,执行到我们的补丁处此次就获得了与该地址绑定的API函数地址了,因为补丁是jmp aaa0037,直接跳到我们代码中去了,因为在执行Call XXX0000之前,我们已经保存当前需要修复的地址,又知道当前需要修复的函数名,下一步需要通过函数名遍历IAT表获取存放该函数的地址的地址,获取到地址后,就开始修复,修复完毕。保存刚刚修复好的地址,继续遍历修复。贴修复代码之前,先找到XXX0000函数内部获取到API名称的地方,跟踪这个函数肯定可以找到,我这边时间有限没去跟,可以执行到最后一次异常的地方也就是第二次出现mov dword ptr ds:[eax],0x855CCAEF,搜索二进制字节,右键-》查找-》二进制字串
点确定。

接着就在刚刚申请到的内存地址写入以下代码,我这边给出二进制字节码,

01100012 cmp eax,0x10B0000 ;与0x10B0000比较是不是相同,其中0x10B0000,你们的可能不一样需要改成你们的

01100027 mov dword ptr ds:[0x1100100],edx ;把call 0x10B0000的地址存放在0x1100100,0x1100100你们的可能不一样需要改成你申请内存的基址多少然后加0x100

01100042 mov ecx,dword ptr ds:[0x1100100] ;相同的话eax存放的地址等于了API的地址,先把刚才保存的call 0x10B0000地址取出,这边0x1100100修改跟前面一样

01100053 mov edx,dword ptr ds:[0x1100100];取出刚刚修复的地址这边0x1100100修改跟前面一样

将上面二进制字节复制,然后找到刚刚申请到的内存地址复制到那边去,由于代码在的基址不一样有以下几处有加粗字体的地方需要改一下。改完之后此时EIP在OEP这边,需要修改EIP为我们刚刚写入代码的首地址也就是 存放mov edx,0x401000,这段代码的地方,找到我们写的代码xx00025 jmp short 01100025 ,这边下个F2断点,接着F9运行,程序断下,可以查看转到OEP看看下面代码是否被修复。修复完成后就是跟脱壳UPX,ASPACK压缩壳一下正常脱壳了。脱壳之后运行程序打开文件会出错。
原因是原程序都是jmp到API地址,但是壳它改成CALL,我们还原的时候也是恢复成CALL,有意思的是壳这样做没事,我们恢复之后运行出错,这个应该是壳埋下的一个坑。其实也就ComDlg32这个模块通过jmp XXXXX调用它的导出函数,其他模块都是以CALL的形式,所以以后脱壳后只要把这个模块CALL改成JMP就完美脱壳了。

00401000   push NOTEPAD.0040D001
00401005   call NOTEPAD.0040100B
00401000   push NOTEPAD.0040D001
00401005   call NOTEPAD.0040100B
mov dword ptr ds:[eax],0x855CCAEF
mov dword ptr ds:[eax],0x855CCAEF
004010CC  push ebp
004010CD  mov ebp,esp
004010CF  sub esp,0x44
004010D2  push esi
004010D3  call 02410000
004010CC  push ebp
004010CD  mov ebp,esp
004010CF  sub esp,0x44
004010D2  push esi
004010D3  call 02410000
004010E1 push esi
004010E2 call dword ptr[0x4064F4] USER32.CharNextA
004010E8 mov esi,eax
004010E1 push esi
004010E2 call dword ptr[0x4064F4] USER32.CharNextA
004010E8 mov esi,eax
0040650C  0F1A4557
00406514  3F0EBE5B
00406518  DFB3232B
00406520  BE255ECC
0040650C  0F1A4557
00406514  3F0EBE5B
00406518  DFB3232B
00406520  BE255ECC
push 0x21FF4E8  ; ASCII "85\r\n"
push 0x21FF4E8  ; ASCII "85\r\n"
debug059:022875ED  xor  eax, eax       ;eax清零
debug059:022875EF  mov  al, [ebx+3Bh]  ;eax赋值96
debug059:022875F2  cmp  esi, eax  ;esi等于96就执行左边的流程
debug059:022875F4  jnz  short loc_2287654
debug059:022875ED  xor  eax, eax       ;eax清零
debug059:022875EF  mov  al, [ebx+3Bh]  ;eax赋值96
debug059:022875F2  cmp  esi, eax  ;esi等于96就执行左边的流程
debug059:022875F4  jnz  short loc_2287654
debug059:022875D0   mov     eax, edx
debug059:022875D2   dec     eax
debug059:022875D3   sub     eax, 2
debug059:022875D6  movzx  esi, byte ptr [eax];esi的赋值的地方
debug059:022875D9   mov     eax, [ebp+arg_8]
debug059:022875D0   mov     eax, edx
debug059:022875D2   dec     eax
debug059:022875D3   sub     eax, 2
debug059:022875D6  movzx  esi, byte ptr [eax];esi的赋值的地方
debug059:022875D9   mov     eax, [ebp+arg_8]
debug059:022875D6  movzx   esi, byte ptr [eax] ;
debug059:022875D6  movzx   esi, byte ptr [eax] ;
02570000 movzx esi,byte ptr ds:[eax];执行刚才被补丁覆盖的代码
02570003   cmp esi,0x96              
02570009   je short 02570018         
0257000B   cmp esi,0x89
02570011   je short 02570018
02570013   mov esi,0x89
02570018 mov eax,dword ptr ss:[ebp+0x10];执行刚才被补丁覆盖的代码
0257001B jmp 022475DC ;跳回刚才打补丁的地方下一个没被覆盖的代码
现在就在call xxx7818 的地方的下一句指令
0224EC0E   test al,al
02570000 movzx esi,byte ptr ds:[eax];执行刚才被补丁覆盖的代码
02570003   cmp esi,0x96              
02570009   je short 02570018         
0257000B   cmp esi,0x89
02570011   je short 02570018
02570013   mov esi,0x89
02570018 mov eax,dword ptr ss:[ebp+0x10];执行刚才被补丁覆盖的代码
0257001B jmp 022475DC ;跳回刚才打补丁的地方下一个没被覆盖的代码
现在就在call xxx7818 的地方的下一句指令
0224EC0E   test al,al
02217188   mov dword ptr ss:[ebp-0x10],eax        
0221718B   mov eax,0x700
02217190   call 021F254C ;这边修改成JMP AAAAA,AAAAA就是我们刚刚申请到的内存地址也是刚才恢复IAT写代码的首地址
02217195   mov dword ptr ss:[ebp-0x1C],eax        
02217198   mov byte ptr ss:[ebp-0x11],0x0
02217188   mov dword ptr ss:[ebp-0x10],eax        
0221718B   mov eax,0x700
02217190   call 021F254C ;这边修改成JMP AAAAA,AAAAA就是我们刚刚申请到的内存地址也是刚才恢复IAT写代码的首地址
02217195   mov dword ptr ss:[ebp-0x1C],eax        
02217198   mov byte ptr ss:[ebp-0x11],0x0
BA 00 10 40 00 80 3A E8 75 12 8B 42 01 03 C2 83 C0 05 3D 00 00 0B 01 75 03 EB 0C 90 42 81 FA E0
4F 40 00 72 E0 EB FE 89 15 00 01 10 01 60 FF E2 90 90 90 90 90 90 90 60 B8 E4 62 40 00 90 39 10
75 20 8B 0D 00 01 10 01 C7 01 FF 15 00 00 89 41 02 61 90 8B 15 00 01 10 01 90 90 90 EB BE 90 90
90 90 83 C0 04 3D 24 65 40 00 7E D2 EB E3

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 73
活跃值: (324)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
比较详细,感谢分享
2024-9-7 21:59
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2024-9-8 07:41
0
游客
登录 | 注册 方可回帖
返回
//