【文章标题】: Themida1.9.1.0版的通法破解
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
感谢kanxue坛主和okdodo大师提供的插件。
上一篇文章《Themida的另类破解》只适用于Themida v1.8.5.5版本。那时我还以为它是Themida的最高版本,因为我用的Themida是从www.pediy.com的工具页上下载的,标签是new(现在也如此)!直到文章发表后网友问能否用于Themida v1.9.1.0时,我才在网上看到了它,下载后加密了几个软件后,才知道它的利害!经过十多天的努力,终于也给破了。
一、请下载okdodo大师的插件“invisible”
用Themida1.9.1.0版加密的软件,有很强的反anti能力,在OD v1.10版上不能运行。请在bbc.pediy.com的精华篇中下载okdodo的插件“invisible.dll”,添加到OD文件夹的plugin中,并删除HideOD.dll(或改名为HideOD.dll.bak)。这样Themida v1.9.1.0加密的软件就可以在OD中运行了。
二、Themida v1.9.1.0的通法破解
因为1.9.1.0版本是1.8.5.5版本的升级,在重要的代码段中随机插入了大量的垃圾代码和花指令。不同的软件,甚至同一软件第二次加密其代码都是不一样的!这跟破解的“通用性”带来极大困难。但我终于搞出了一个通用的破解方法!但要使用三个脚本,反复用OD加载要解密的软件。三个脚本分别叫:脚本1(断点侦测)、脚本2(API修复)、脚本3(OEP修复)。
1.脚本1(断点侦测)的使用:脚本如下(已改进为全自动了,用它替换附件中的脚本1)
-------------------------------------------
//Themida 1.9.1.0的断点设置脚本(改进版)
data:
var memory
var csize
var tmp
var temp
var tmpbp
var mem
var mem0
var mem1
var mem2
var mem3
var mem4
var mem5
var Str0
var Str3
var Str4
var Str5
var Str1
var Str2
star:
gmi eip,CODEBASE
mov memory,$RESULT
gmi eip,CODESIZE
mov csize,$RESULT
add csize,memory
find memory,#0000000000000000000000000000000000000000000000#
mov tmp,$RESULT
bphws tmp,"w"
esto
esto
esto
bphwc tmp
sto
find memory,#0000000000000000000000000000000000000000000000#
mov tmp,$RESULT
sub csize,tmp
find memory,#909090909090909090909090909090909090#
mov temp,$RESULT+8
bphws temp,"w"
mov tmpbp,eip
run
cmp eip,tmpbp+2
jnz next
run
next:
bphwc
mov Str0,"断点地址:"
add Str0,#0D0A#
mov mem,eip
mov mem4,eip //eip=Addr_4
itoa eip
mov Str4,"Addr_4="
add Str4,$RESULT
add Str4,#0A0D#
find mem4,#83BD????????000F8497000000#
mov mem5,$RESULT //Addr_5
itoa mem5
mov Str5,"Addr_5="
add Str5,$RESULT
sub mem,4000
find mem,#83BD????????640F82??020000#
mov mem0,$RESULT+7 //Addr_0
itoa mem0
add Str0,"Addr_0="
add Str0,$RESULT
add Str0,#0D0A#
find mem0,#C1C003#
find $RESULT,#0385????7409#,40
mov mem3,$RESULT+6 //Addr_3
itoa mem3
mov Str3,"Addr_3="
add Str3,$RESULT
add Str3,#0D0A#
findon:
find mem0,#AD# //从Addr_0开始查找
cmp $RESULT,0
jz stop
mov mem0,$RESULT
find mem0,#01C8#,40
cmp $RESULT,0
jnz findoff
add mem0,40
cmp mem0,mem5
ja stop //超过Addr_5后停止
jmp findon
findoff:
bp $RESULT
add mem0,40
itoa $RESULT
jmp findon
stop:
run
@1:
sti
gn eax
cmp $RESULT,0
jz @1
itoa eip
mov Str1,"Addr_1="
add Str1,$RESULT
add Str1,#0D0A#
bpwm tmp,csize
run
@3:
sti
cmp [eax],ecx
jnz @3
itoa eip
mov Str2,"Addr_2="
add Str2,$RESULT
add Str2,#0A0D#
add Str0,Str1
add Str0,Str2
add Str0,Str3
add Str0,Str4
add Str0,Str5
msg Str0
pause
-----------------------------------------------------------
脚本1的使用非常简单,用OD打开要解密的软件,直接运行脚本1,第一次弹出的对话框给出了6个确定的断点.
Themida每次加密代码都有变化,特征码很难捕获。比如要捕获的特征代码如果是 mov [eax],ecx:
那么,代码 mov [eax],ecx 可以有多种变化,如:
0068475C 51 push ecx
0068475D 813424 1F15FD3E xor dword ptr [esp], 3EFD151F
00684764 8F00 pop dword ptr [eax]
00684766 52 push edx
00684767 BA 00000000 mov edx, 0
0068476C 01C2 add edx, eax
0068476E 8132 1F15FD3E xor dword ptr [edx], 3EFD151F
00684774 5A pop edx
或者
00679700 51 push ecx
00679701 53 push ebx
00679702 BB AF61DB7F mov ebx, 7FDB61AF
00679707 315C24 04 xor dword ptr [esp+4], ebx
0067970B 5B pop ebx
0067970C 8F00 pop dword ptr [eax]
0067970E 8130 AF61DB7F xor dword ptr [eax], 7FDB61AF
(新改进的脚本1已经成为全自动了)
当你把这6个断点地址写入了脚本2后(可以打开脚本2直接修改)就可以关掉OD了。
2.脚本2(API修复)的使用
----------------------------------------------------
//本脚本适用于Thmida 1.9.1.0版本
bphwc
bc
data:
var mem
var mem1
var temIAT
var temESI
var temAPI
var APIstr
var Addr_0
var Addr_1
var Addr_2
var Addr_3
var Addr_4
var Addr_5
Init:
//用对话框中的断点值修改下列各断点,Addr_1,Addr_2的值必须单步跟踪后确定
//只须修改这6个地址
mov Addr_0,676d13
mov Addr_1,67763b
mov Addr_2,679714
mov Addr_3,679889
mov Addr_4,679cd6
mov Addr_5,67cc30
start:
esto
esto
bphws Addr_0,"x"
esto
bphwc Addr_0
mov [Addr_0],#90E9# //将jb改为jmp,去除代码扫描
bp Addr_1
bp Addr_2
bp Addr_3
bp Addr_4
bp Addr_5
First:
run
cmp eip,Addr_1
jz A1
cmp eip,Addr_2
jz A2
cmp eip,Addr_3
jz A3
cmp eip,Addr_4
jz A4
cmp eip,Addr_5
jz A5
jmp First
A1:
mov temAPI,eax //eax 是API函数地址
gn temAPI //显示函数名
log $RESULT
add APIstr," "
add APIstr,$RESULT_2 //恢复API函数名(INT表)
jmp First
A2:
mov temIAT,eax
cmp edx,10000
ja next
xor edx,80000000
mov [eax],edx //修复IAT表(写入序列号)
jmp First
next:
mov [eax],temAPI
jmp First
A3:
mov mem1,eax //eax是内存中呼叫API地址
mov temESI,[esi] //获取转跳标记
jmp First
A4:
cmp temESI,AAAAAAAA
jnz step3
mov [mem1],#FF25# //修复代码中转跳地址
mov [mem1+2],temIAT
jmp First
step3:
mov [mem1],#FF15# //修复代码中呼叫地址
mov [mem1+2],temIAT
jmp First
A5:
bc
//---下面位置在运行脚本3后,写入OEP修复代码,若要再次用于新的程序,则应该将添加的代码删除
pause
------------------------------------------------------
脚本2的使用更简单,当用脚本1的6个地址把本脚本中对应地址修改好后存盘,用OD再次打开该软件,运行脚本2。注意:这6个地址不能有丝毫差错!程序运行如飞,很快停止在断点Addr_5上。
注意到这时堆栈项是:13FF64,而刚打开OD时栈项是13FFC4,差值是60,后面平衡堆栈(脚本3中)要用。
将OD代码段转到401000开始的位置,用OD分析代码一次,你将看到所有的秘密都显示出来了。以提供的FindFile为例,转到API函数表位置(API集中转跳位置402646——402736),选出代码前面没有“$-”符号的部分(本例只有3个):
(若全部都没有“$-”符号,再用OD分析一次)
00402652 .- FF25 A4304000 jmp dword ptr [4030A4] ; USER32.DialogBoxParamA
004026B2 .- FF25 48304000 jmp dword ptr [403048] ; kernel32.ExitProcess
004026DC .- FF25 24304000 jmp dword ptr [403024] ; kernel32.GetModuleHandleA
0040272A $- FF25 04304000 jmp dword ptr [403004]
00402730 $- FF25 00304000 jmp dword ptr [403000]
注意到,40272A、402730后面没有出现函数名称,但[403004]和[403000]中都有确定的值。用alt-M 打开Memory map,发现comdlg32.dll库是红色显示,我什么也没做,关掉Memory map后,居然正常显示了如下:(不能解释其原因)
0040272A $- FF25 04304000 jmp dword ptr [403004] ; comdlg32.GetOpenFileNameA
00402730 $- FF25 00304000 jmp dword ptr [403000] ; comdlg32.GetSaveFileNameA
现在打开脚本3(OEP修复),将没有“$-”符号的那3个函数的相应的代码(照原样写),如下添加在脚本3中:
mov [402652],#FF25A4304000# //<--对应 USER32.DialogBoxParamA
mov [4026B2],#FF2548304000# //<--对应 kernel32.ExitProcess
mov [4026DC],#ff2524304000# //<--对应 kernel32.GetModuleHandleA
mov [4030A4],77D3B144 //<--在该地址中查到的值(注意倒着写),下同
mov [403048],7C81CAFA
mov [403024],7C80B731
bp 402652 //设置对应的断点,下同
bp 4026B2
bp 4026DC
为什么要写入脚本3中呢?原来程序在运行了脚本2后,Themida早已侦查到了系统内存中出现了“调试器”,若继续运行,会在运行一段时间后中止,得不到最后结果。当你在脚本3中写入了相应代码后存盘,关闭OD。
3.脚本3(OEP修复)的使用
---------------------------------------------
data:
var Addr_5
start:
esto
esto
mov Addr_5,67cc30 // <----写入由脚本1得到的Addr_5
bphws Addr_5,"x"
esto
bphwc
//----下面位置在运行脚本2后,添加与OEP有关的断点处代码,若要再次用于新的程序,则应该将全部代码删除
mov [402652],#FF25A4304000#
mov [4026B2],#FF2548304000#
mov [4026DC],#ff2524304000#
mov [4030A4],77D3B144
mov [403048],7C81CAFA
mov [403024],7C80B731
bp 402652
bp 4026B2
bp 4026DC
bpwm 404000,1000 //404000的确定是根据402000是代码区,403000是资源数据区
pause
------------------------------------------------------
脚本3的作用实际上就是将OD运行到底(Addr_5的值一定要根据脚本1得到的修改)。按F9,由于在脚本中添加了如上代码,程序将中断在上面4个断点之一上。添加这些代码的作用就是恢复被VM虚拟的API(在入口代码段中),且运行后在将在堆栈中揭示其秘密。
核对无错误后,用OD再次打开软件,加载脚本3,程序停在Addr_5上。按F9,OD中断在
004026DC .- FF25 24304000 jmp dword ptr [403024] ; kernel32.GetModuleHandleA
在堆栈中看到:
0013FF7C 0066E2F0 /CALL 到 GetModuleHandleA
0013FF80 00000000 \pModule = NULL
这就是入口的第1个API调用。记下后,再F9,中断在VM虚拟机代码段中:
00B9CD7E F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
下面的状态条中显示:
ecx = 00000004
ds:[esi]=[003E1FF8]=00
es:[edi]=[00404000]=00
原来是向[00404000]中写入代码,代码值就是当前堆栈中的 003E1FF8 中的值。404000就是GetModuleHandleA 句柄地址。
继续F9,中断在
00402652 - FF25 A4304000 jmp dword ptr [4030A4] ; USER32.DialogBoxParamA
堆栈中显示:
0013FF6C 0066E2FC /CALL 到 DialogBoxParamA
0013FF70 00400000 |hInst = 00400000
0013FF74 00000064 |pTemplate = 64
0013FF78 00000000 |hOwner = NULL
0013FF7C 00401B92 |DlgProc = FindFile.00401B92
0013FF80 00000000 \lParam = NULL
这就是入口的第2个API调用,其参数堆栈中显示非常清楚。因为程序将立即进入DialogBoxParamA,直到关闭窗口后才退出,下面就交替F9或alt-F9,直到窗口出现。(会反复多次按F9或alt-F9,这是因为脚本中设置了“bpwm 404000,1000”的原因),我不知道怎样在运行中清除它?
关闭程序窗口,程序中断在
004026B2 - FF25 48304000 jmp dword ptr [403048] ; kernel32.ExitProcess
堆栈中出现
0013FF7C 0066E309 /CALL 到 ExitProcess
0013FF80 00000000 \ExitCode = 0
请暂时不要关闭OD,整个入口代码都清楚了。将OD代码转到40261C处(乱码开始位置),逐条汇编如下:
汇编语言 对应代码
push 0 6A00
call 4026DC E8B9000000
mov [404000],eax A300404000
push 0 6A00
push 401B92 68921B4000
push 0 6A00
push 64 6A64
push [404000] FF3500404000
call 402652 E814000000
push 0 6A00
call 4026B2 E86D000000
int3 CC <--最后添上一个int3,让其返回系统
4.脚本2的最后完善
---------------------------------
现在打开脚本2,在标签A5:的bc后面写入(添加)如下代码:
(1)修复OEP(完整抄录OD上汇编后出的2进制码):
mov [40261C],#6A00E8B9000000A3004040006A0068921B40006A006A64FF3500404000E8140000006A00E86D000000CC#
(2)为平衡堆栈,实现对OEP的转跳,继续写入:(前面记录过栈项的差值=60)
mov [Addr_5],#81C460000000B81C26400050C3#
(即对应下列汇编的二进制代码:
add esp,60
mov eax,40261C
push eax
retn )
(3)再写入如下代码,作用是将API函数名称写入适当空白处(如403600)减轻dump后的录入文字工作。
mov [403600],APIstr
sti
sti
sti
sti //4个单步
(4)写完后存盘,关闭OD,再次打开OD,加载脚本2,程序停在OEP处。若按F9,程序运行良好。
至此,Thmida 1.9.1.0加密的软件完美解密!
三、破解思路整理
三个脚本,翻来复去地运行,有点头晕,整理思路:
运行脚本1,得到6个断点地址--->写入脚本2,运行脚本2得到几个“无关联函数”将其信息写入脚本3,并设置相应断点--->运行脚本3,从堆栈中获取OEP入口代码信息并回写入脚本2---->运行脚本2,实现完美解密。
四、后记
1.本文的方法适用于Thmida 1.8---1.9的各种版本。1.8以前的版本我没有看过,1.9.1.0以后的也没有看过,很难说通吃各种版本,但对于大多数被Themida加密的程序应该是没有问题的。(试验得不多,若有例外,研究后改进)其实我毫无保留地给出了我破解的详细思路和作法,让有兴趣者可以在此基础上加以发展,写出Themida脱壳机。(网上有个Themida脱壳机,不知什么版本,也根本不能运行)
2.对于用VC++编写的程序,运行脚本3不会有太好的结果,原因是没有“$-”符号的函数太多,大多是无用的(VC-6编辑器几乎将MFC42D.DLL、MSVCRTD.dll等库中函数全部装载,不管你调用不调用)而windows下的API又很少使用,但VC++的入口代码又具有相对固定的模式,甚至可以nop掉一堆SEH的设置代码,程序对入口的堆栈长度设置(如:add esp,-6C)也不敏感(参考我《对themida1855加密VC++程序的完美脱壳》一文)对于用VC++编写的程序在运行脚本1后,运行脚本2一次,获取入口地址后,在脚本2的A5:标签后直接添加如下代码:
mov [4xxxxx],APIstr //4xxxxx表示找一段空白,写入函数名称
mov [Addr_5],#81C460000000B8xxxx4x0050C3# //xxxx4x入口地址反向写入
mov [4xxxxx],#558BEC6AFF90909090909090909090909090909090909090909090909083C4945356578965E8C745FC000000006A02# //代码长度由现场决定,4xxxxx表示OEP入口地址(乱码开始地址),添加的代码几乎无须改动
sti
sti
sti
sti
将脚本2存盘一次,再次运行它,当你具有一定经验后,可能会得到满意的结果。
3.请注意,当须要把三个脚本用于新的程序时,请一定将前一个程序在脚本2、脚本3中添加的代码删除,否则可能有不希望的结果发生。
4.通过三个脚本解密的程序,可以dump了,但dump后的程序还要加工才能运行。如修改IID导入表,恢复INT表,还原IAT表中数据,瘦身等,这些是PE文件基本知识,不涉及破解知识,不在此讨论。
谢谢你耐心地读完本文!希望能对你有所帮助。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2008年09月22日 23:10:50
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: