马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
[size=0.9][size=0.9][url=]01-寻找OEP[/url][url=]02-异常回调反硬件断点[/url][url=]03-反断点异常分析[/url][url=]配置环境[/url][url=]分析异常[/url][url=]突破反硬件断点[/url][url=]04-填充IAT的地方[/url][url=]05-获取API地址的地方[/url][url=]06-写OD脚本自动操作[/url][url=]08-dump内存[/url][url=]09-修复输入表(依据新建的IAT[/url]
在 15PB学习时的随手笔记,简单记录并分享给大家,当时初接触脱壳相关知识,一些地方可能理解的不到位,若有偏差,望各位斧正; 饮水思源,感谢15pb的每一位老师!感谢15pb营造的良好学习氛围!
01-寻找OEPpushad后,esp定律失败(壳oep处有花指令,按键1添加一个nop失效,手动右键-二进制修改为90 在主模块代码起始地址,使用Ctrl+B搜索“FF 15 ?? ?? ?? ??”,找到调用IAT的地方(或者FF 25;手动找太慢,直接搜索) 调用IAT确实是FF 15,排除宝蓝的BC++、Delphi程序,是VS的 程序跑起来后,在主模块ctrl+f搜索指令“sub esp 0x58”,果然找到,下面还有一个call,一般就是GetVersion,断定就是VC 6.0程序 除此之外,还可以VC 6.0 还有winMain函数的特征(winMain、sub esp 0x58、getVersion 找到真实oep
02-异常回调反硬件断点IAT处设置硬件写入断点,发现并没有断下,真实oep处设置硬件执行,也没有断下,猜测有反硬件断点的技术; 硬件断点没有触发->程序有反调试->会清除硬件断点或者让硬件断点失效 清除硬件断点的方法有哪些?相当于有哪些方法可以修改寄存器? SetThreadContext 异常回调函数中可以修改寄存器
搜索SetThreadContext函数下断,发现并未断下,说明是异常回调起的作用(并未实测,win10虚拟机中的OD,ctrl+g不能搜API名)
03-反断点异常分析配置环境分析异常不忽略程序异常运行程序,一共有7处异常,依次如下: 其中3、4尽管看起来发生的位置和异常类型是一致的,但实际上触发的位置是不同的,从堆栈能看出来二者不同(就像是,同一个API,a和b两处都可以调用,尽管被调用的API相同,但是调用的位置a和b是不同的) 哪一个异常对硬件断点清0?(在异常点设置硬件断点,看能不能断下,能断下说明当前异常点前面的异常处理函数中没有对硬件断点清0) 第二个异常处下硬件执行,重新执行,能够断下来,说明第一个异常无辜 第三个异常下断,也能断下来,说明1、2无辜 第四个异常下断,没有断下来,说明第三次异常处理中,对硬件断点清0 重新运行,至第三次异常,通过堆栈窗口,发现第三次的SEH处理程序是43AF42,光标移动到此,直接enter,使其反汇编窗口中跟随,发现疑似设置清零寄存器的代码(硬件断点基于寄存器,寄存器清零即消除硬件断点 将此地址设置为硬件执行断点(下面才是硬件断点清零的操作,所以此处可以下断),重新运行至此时,直接dump内存保存下来(dump时不用动其他设置,直接点脱壳即可,因为仅仅是为了看代码,能不能运行无所谓;之所以在此处dump,是为了后面IDA打开时,能直接停在43AF42时 IDA中打开上述dump后的文件,打开就在43AF42处(如上所言) IDA中,structures窗口-edit-add struct type来添加新的结构体类型(mac 没有insert按键) 最终操作后如下,确实是一个断点清零0的操作(具体结构体添加的操作,见逆向实战-笔记3-IDA使用-结构体部分) IDA中,F5解析为C代码(进一步验证 (硬件断点设置和取消,是通过设置寄存器来实现的的;是经过一个context结构体的,而非直接改dr0、Dr1这些单个的位
突破反硬件断点已经找到消除硬件断点的地方了,只需要跳过即可,可以将0043AF57以下的代码到0043AF72全部nop 恢复之前的异常触发环境,使其能忽略异常(调试选项-异常-钩都打上、strongOD-option-skip some exception也打上) 找到真实oep的地方,设置硬件执行断点,在消除硬件断点的前面也设置硬件执行断点(42为消除、86为oep 重新运行,首先会断在42处(因为设置了硬件断点,他要消除),在这将关键代码nop掉,这样,OEP那就能成功断下来 像这样,只要每次设置硬件断点时,程序若要消除这些硬件断点,一定会跳到42处,只要跳到这,就将其nop掉,这样,后面的硬件断点就能正常断下了
04-填充IAT的地方05-获取API地址的地方写脚本的一般思路:API地址获取后,保存地址到临时变量,在填充IAT时,填充这个临时变量的值(即真正API的地址),而非经过壳代码处理后的加密值 从8A填充IAT的地方,F7单步向下走,时刻关注寄存器,看是否出现“xxx.yyy”的形式(尤其是eax寄存器 若遇到循环,想办法跳出,5个关键点:向上跳的循环、循环跳到哪、二者之间的条件跳转、条件跳转何时跳出循环何时仍在循环内、下断点在跳出循环的位置 第一个循环(crtl+F7自动跑,F7暂停) 1:向上跳,标示是一个循环; 2: 向上跳的循环,跳到此; 3/4: 12之间的Jxx条件跳转; 由下到上,即由1-2之间找跳转,1、3、4均是; 先测试1的,若跳出循环,则下在5,测试,发现程序跑到真实oep处,不对,舍去; 再测试3,发现正常循环内,3压根执行不到; 再测试4,若要出循环,就不跳,在6处下断,F9运行至此,再ctrl+F7,发现可以出循环,正确 第二个循环 自上而下测试,先测试1处的,要跳出,需要在1下面,即4,但4也是往上跳,因此再往下,断点下载5,F9运行至此,继续F7单步; 3处,发现无论跳与不跳,都要执行到1,都会再往上跳,故舍去; 跳出第二个循环后,就要慢点F7了,时刻关注寄存器是否出现xxx.yyy的形式(重点eax) 跳出第二个循环后,继续F7,发现又进入了第一个循环,再次F9使其跳出循环1,然后又进入循环2,也再次F9跳出循环2,继续F7,最终终于到此 99处下断,测试一下,是否每次运行完此条,eax都会出现xxx.yyy,发现确实如此 也注意到,这条指令是出现在循环1内部的,想想也对,这种操作肯定是需要循环操作的,因此,以后再遇到循环,先看其内部,有没有这种操作,先测试再说,万一就是呢 先不管99处是否是获取API地址的call,反正运行99后,寄存器中会出现xxx.yyy这种形式,这样就够了,这样就能够写脚本了(停在9F,反正eax有真正的函数地址,即xxx.yyy形式) 注意: 妈耶,我都要被自己啰嗦死了(可能这就是菜吧
06-写OD脚本自动操作三个通用的地址 OEP 填充IAT 获取API地址 (执行完相关指令的下一位置,而非当前位置)
通用思路 获取API地址处:获取寄存器的值,赋值给临时变量(xxx.yyy,即API真正地址 填充IAT处:直接填充临时变量的值,即填充真实API地址,而非被壳修改过的 OEP处:一直运行,直至运行到OEP,标志填充IAT结束
特殊地址:清除硬件断点的地方 步骤
// 1. 找到相关地址(前三个是通用的)
MOV vOEP,00409486 // OEP地址
MOV vGetAPIAddr,438F9F // 获取API地址
MOV vWriteIATAddr,43918c // 写入IAT地址
MOV vHardwarePointAddr,0043AF51 // 清除硬件断点的地方
MOV vAPIaddr,0// 临时变量存储真实API地址
// 2. 设置断点(先清除其他,防干扰)
BPHWC// 清除所有硬件断点
BC// 清除所有软件断点
BPHWS vHardwarePointAddr,"x" // 在此处下断,运行到此即停止
BPHWS vOEP,"x" //
BPHWS vGetAPIAddr,"x" //
BPHWS vWriteIATAddr,"x" //
//(记住大体形式,举一反三)
// 3.0 循环:F9运行程序
LOOP_1:
RUN
// 3.1 清除硬件断点:若断在此,则将其NOP掉10h个字节
CMP eip,vHardwarePointAddr
JNZ SIGN_1
fill vHardwarePointAddr,22,90//NOP 10h个字节
// 3.2 获取API地址:若断在此,则保存API地址到临时变量
SIGN_1:
CMP eip,vGetAPIAddr
JNZ SIGN_2
MOV vAPIaddr,eax
// 3.3 填充IAT:若断在此,则填充真实API地址(临时变量)
SIGN_2:
CMP eip,vWriteIATAddr
JNZ SIGN_3
mov [edi], vAPIaddr//依据填充IAT的指令,来决定目的是谁
// 3.4 OEP:若断在此,则结束,否则继续循环
SIGN_3:
cmp eip,vOEP
JE EXIT_1
JMP LOOP_1
// 3.5 弹框,标示结束
EXIT_1:
MSG "修复完毕"09-修复输入表(依据新建的IAT08-dump内存
|