--------------------------------------------------------------------------------
【详细过程】
我经过探索实现了对Themida(1.8.5.5版)加密VC++程序的完美脱壳.如果文中的方法只适用于这个特例,则本文就毫无价值....,我把我的方法提供给大家研究,对象是想升级的菜鸟们,请高手不要嘲笑我对脚本或程序的详细注释.
一.脱壳思路
Themida是一款强大的加密加壳工具,特别是1.8.5.5版,它破坏了程序的入口代码并用一段壳代码取代了它,根本模糊了OEP界线.要么脱壳不完全,要么进不了程序代码(菜鸟的肤浅认识).但Themida也暴露出了它自身的弱点:
1.它对入口代码的破坏一般只有几十个字节,对一些固定模式的入口程序,如VC++,VB,Delphi等入口的发现和修复还是比较容易的.Themida对它不熟习的入口代码则脱壳后原样不变!这时,OEP就清晰了.
2.和所有的加壳软件一样,Themida也只能在现场获取API地址,经加密后再写入程序代码中.正确的API地址一定会在某个寄存器中瞬间出现,这就暴露了它加密的蛛丝马迹,捕获在themida代码中出现API地址的代码段就成为了脱壳的突破口.
3.任何程序运行完成后都会"返回到kernel32.7C816FD"(菜鸟的肤浅认识).因此当堆栈顶是"返回到kernel32.7C816FD"时,是从壳代码进入程序代码的重要标志.
我就是抓住以上三点作为突破口实现了对Themida(1.8.5.5)版的完美脱壳.
二.脱壳脚本的说明(测试程序和脚本见附件)
--------------------------------------------------------
完整的脱壳脚本:(ODbgScript 1.65.1.0版 OllyDBG 1.0.10.0版)
data:
var mem
var mem1
var temIAT
var temESI
var temAPI
var APIstr
bphwc
start:
esto
esto
bphws 65d270,"w"
reset:
esto
cmp [65d270],5ec103ad //判断加密段代码是否出现
jnz reset
bphwc 65d270
bphws 65d992,"x"
step:
esto //断点65d992
bphwc
cmp eip,65d273 //以API函数名装载方式则跳
jz step6
cmp eip,65e1c8 //准备退出
jz step5
//----以序列号方式装载则进入-------
or edx,80000000
mov [eax],edx //修复IAT表(写入序列号)
mov temIAT,eax //保存IAT地址
bphws 65d9a0,"x"
step2:
esto //断点65d9a0
bphwc
cmp eax,FFFFFFFF //判断当前函数是否加密新的地址
jnz next
bphws 65d992,"x"
bphws 65d273,"x"
bphws 65e1c8,"x"
jmp step
next:
bphws 65d9d2,"x"
esto //断点65d9d2
bphwc
mov mem1,eax //eax是内存中呼叫API的地址
mov temESI,[esi] //获取转跳标记
bphws 65da7c,"x"
esto //断点65da7c
bphwc 65da7c
cmp temESI,AAAAAAAA
jnz step3
mov [mem1],#FF25# //修复代码中转跳地址
mov [mem1+2],temIAT
bphws 65d9a0,"x"
jmp step2
step3:
mov [mem1],#FF15# //修复代码中呼叫地址
mov [mem1+2],temIAT
bphws 65d9a0,"x"
jmp step2
step5:
bphwc
//---完善INT表,插入库函数名-----
add APIstr," MFC42D.dll MFCO42D.dll MSVCRTD.dll KERNEL32.dll" //MFC42D前加4个空格
mov [410000],APIstr
//--写入平衡堆栈,入口地址和跳出壳的代码
mov [65e1cf],#83C44CB8902E400050C3#
//--恢复应用程序入口代码
mov [402e90],#558BEC6AFF687068410068F830400064A100000000506489250000000083C4945356578965E8C745FC000000006A02#
sti
sti
sti
sti
sti
pause //成功脱壳,然后Dump,稍加修改PE头后,程序正常运行.
//-----以函数名方式装载的进入---------
step6:
mov temAPI,eax //eax 是API函数地址
gn temAPI //显示函数名
log $RESULT_2
add APIstr," " //两个空格
add APIstr,$RESULT_2 //恢复API函数名表(INT表)
bphws 65d992,"x"
esto
bphwc //断点65d992
mov temIAT,eax //保存IAT地址
bphws 65d9a0,"x"
jmp step2
------------------------------------------------------------------
硬件断点的选择
本脚本涉及了65d270,65d992,65d273,65d9a0,65d9d2,65da7c,65e1c8等七个断点,正确选择这些断点是脱壳的关键.如果这些断点只适用你这个特例,则本文就毫无价值.恰恰Themida对不同程序的加密过程(代码)基本相同,只是地址不同,熟习这些断点处的汇编代码对搜索其它程序中类似的断点提供了参考.
Themida的加密代码段只是在加密时出现,加密前后都是其它用途的代码.下面先说如何捕获加密代码段:
1.加密API函数,IAT地址等的代码段的捕获:
多数软件解压后都在401000开始后的段中运行(菜鸟的肤浅认识),首先了解软件解压后执行代码段的长度,只须先用OD运行一次,在内存段中从401000向下观察,本程序长度到4031E7为止,以下都是无意义的全CC字节.在"4031E7"设置写入中断,再次从头运行到中断后,观察401000---4031E7中,出现了很多连续的"9090"字节.这些就是需要加密的代码处.
然后在连续的9090中任选一个地址,比如4030F0,设置为写入中断,F9运行,中断后就进入了themida的"加密代码段"了.试用F8跟踪运行,你逐渐就会发现themida的加密的过程了.现将加密段部分代码注释如下.
2.硬件断点的选择:(在代码中作了原因的批注,若用F8跟踪几次,就会理解了)
加密过程从这里开始....
0065D0AF C785 B10D7409 00000000 mov dword ptr [ebp+9740DB1], 0
【详细过程】
我经过探索实现了对Themida(1.8.5.5版)加密VC++程序的完美脱壳.如果文中的方法只适用于这个特例,则本文就毫无价值....,我把我的方法提供给大家研究,对象是想升级的菜鸟们,请高手不要嘲笑我对脚本或程序的详细注释.
一.脱壳思路
Themida是一款强大的加密加壳工具,特别是1.8.5.5版,它破坏了程序的入口代码并用一段壳代码取代了它,根本模糊了OEP界线.要么脱壳不完全,要么进不了程序代码(菜鸟的肤浅认识).但Themida也暴露出了它自身的弱点:
1.它对入口代码的破坏一般只有几十个字节,对一些固定模式的入口程序,如VC++,VB,Delphi等入口的发现和修复还是比较容易的.Themida对它不熟习的入口代码则脱壳后原样不变!这时,OEP就清晰了.
2.和所有的加壳软件一样,Themida也只能在现场获取API地址,经加密后再写入程序代码中.正确的API地址一定会在某个寄存器中瞬间出现,这就暴露了它加密的蛛丝马迹,捕获在themida代码中出现API地址的代码段就成为了脱壳的突破口.
3.任何程序运行完成后都会"返回到kernel32.7C816FD"(菜鸟的肤浅认识).因此当堆栈顶是"返回到kernel32.7C816FD"时,是从壳代码进入程序代码的重要标志.
我就是抓住以上三点作为突破口实现了对Themida(1.8.5.5)版的完美脱壳.
二.脱壳脚本的说明(测试程序和脚本见附件)
--------------------------------------------------------
完整的脱壳脚本:(ODbgScript 1.65.1.0版 OllyDBG 1.0.10.0版)
data:
var mem
var mem1
var temIAT
var temESI
var temAPI
var APIstr
bphwc
start:
esto
esto
bphws 65d270,"w"
reset:
esto
cmp [65d270],5ec103ad //判断加密段代码是否出现
jnz reset
bphwc 65d270
bphws 65d992,"x"
step:
esto //断点65d992
bphwc
cmp eip,65d273 //以API函数名装载方式则跳
jz step6
cmp eip,65e1c8 //准备退出
jz step5
//----以序列号方式装载则进入-------
or edx,80000000
mov [eax],edx //修复IAT表(写入序列号)
mov temIAT,eax //保存IAT地址
bphws 65d9a0,"x"
step2:
esto //断点65d9a0
bphwc
cmp eax,FFFFFFFF //判断当前函数是否加密新的地址
jnz next
bphws 65d992,"x"
bphws 65d273,"x"
bphws 65e1c8,"x"
jmp step
next:
bphws 65d9d2,"x"
esto //断点65d9d2
bphwc
mov mem1,eax //eax是内存中呼叫API的地址
mov temESI,[esi] //获取转跳标记
bphws 65da7c,"x"
esto //断点65da7c
bphwc 65da7c
cmp temESI,AAAAAAAA
jnz step3
mov [mem1],#FF25# //修复代码中转跳地址
mov [mem1+2],temIAT
bphws 65d9a0,"x"
jmp step2
step3:
mov [mem1],#FF15# //修复代码中呼叫地址
mov [mem1+2],temIAT
bphws 65d9a0,"x"
jmp step2
step5:
bphwc
//---完善INT表,插入库函数名-----
add APIstr," MFC42D.dll MFCO42D.dll MSVCRTD.dll KERNEL32.dll" //MFC42D前加4个空格
mov [410000],APIstr
//--写入平衡堆栈,入口地址和跳出壳的代码
mov [65e1cf],#83C44CB8902E400050C3#
//--恢复应用程序入口代码
mov [402e90],#558BEC6AFF687068410068F830400064A100000000506489250000000083C4945356578965E8C745FC000000006A02#
sti
sti
sti
sti
sti
pause //成功脱壳,然后Dump,稍加修改PE头后,程序正常运行.
//-----以函数名方式装载的进入---------
step6:
mov temAPI,eax //eax 是API函数地址
gn temAPI //显示函数名
log $RESULT_2
add APIstr," " //两个空格
add APIstr,$RESULT_2 //恢复API函数名表(INT表)
bphws 65d992,"x"
esto
bphwc //断点65d992
mov temIAT,eax //保存IAT地址
bphws 65d9a0,"x"
jmp step2
------------------------------------------------------------------
硬件断点的选择
本脚本涉及了65d270,65d992,65d273,65d9a0,65d9d2,65da7c,65e1c8等七个断点,正确选择这些断点是脱壳的关键.如果这些断点只适用你这个特例,则本文就毫无价值.恰恰Themida对不同程序的加密过程(代码)基本相同,只是地址不同,熟习这些断点处的汇编代码对搜索其它程序中类似的断点提供了参考.
Themida的加密代码段只是在加密时出现,加密前后都是其它用途的代码.下面先说如何捕获加密代码段:
1.加密API函数,IAT地址等的代码段的捕获:
多数软件解压后都在401000开始后的段中运行(菜鸟的肤浅认识),首先了解软件解压后执行代码段的长度,只须先用OD运行一次,在内存段中从401000向下观察,本程序长度到4031E7为止,以下都是无意义的全CC字节.在"4031E7"设置写入中断,再次从头运行到中断后,观察401000---4031E7中,出现了很多连续的"9090"字节.这些就是需要加密的代码处.
然后在连续的9090中任选一个地址,比如4030F0,设置为写入中断,F9运行,中断后就进入了themida的"加密代码段"了.试用F8跟踪运行,你逐渐就会发现themida的加密的过程了.现将加密段部分代码注释如下.
2.硬件断点的选择:(在代码中作了原因的批注,若用F8跟踪几次,就会理解了)
加密过程从这里开始....
0065D0AF C785 B10D7409 00000000 mov dword ptr [ebp+9740DB1], 0