shaopeng 发表于 2008-11-25 19:32

Themida1.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
      movmemory,$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"
movtmpbp,eip
run
cmpeip,tmpbp+2
      jnznext
      run
next:
bphwc
      movStr0,"断点地址:"
      addStr0,#0D0A#
movmem,eip
movmem4,eip         //eip=Addr_4
itoa eip
movStr4,"Addr_4="
      addStr4,$RESULT
      addStr4,#0A0D#
      find mem4,#83BD????????000F8497000000#
      movmem5,$RESULT       //Addr_5
      itoa mem5
      movStr5,"Addr_5="
      addStr5,$RESULT
      submem,4000
find mem,#83BD????????640F82??020000#
      movmem0,$RESULT+7    //Addr_0
      itoa mem0
      addStr0,"Addr_0="
      addStr0,$RESULT
      addStr0,#0D0A#
      find mem0,#C1C003#
      find $RESULT,#0385????7409#,40
      movmem3,$RESULT+6   //Addr_3
      itoa mem3
      movStr3,"Addr_3="
      addStr3,$RESULT
      addStr3,#0D0A#
findon:
find mem0,#AD#       //从Addr_0开始查找
      cmp$RESULT,0
      jz   stop
      movmem0,$RESULT
find mem0,#01C8#,40
      cmp$RESULT,0   
jnzfindoff
      addmem0,40
      cmpmem0,mem5
      ja   stop            //超过Addr_5后停止
      jmpfindon
findoff:
      bp   $RESULT
      addmem0,40
      itoa $RESULT
      jmpfindon
stop:
run
@1:
      sti
      gn   eax
      cmp$RESULT,0
      jz@1      
itoa eip
      movStr1,"Addr_1="
      addStr1,$RESULT
      addStr1,#0D0A#
bpwm tmp,csize
      run
@3:
sti
      cmp ,ecx
      jnz @3
itoa eip
movStr2,"Addr_2="
addStr2,$RESULT
addStr2,#0A0D#
      addStr0,Str1
      addStr0,Str2
      addStr0,Str3
      addStr0,Str4
      addStr0,Str5
      msgStr0
      pause

          -----------------------------------------------------------
      脚本1的使用非常简单,用OD打开要解密的软件,直接运行脚本1,第一次弹出的对话框给出了6个确定的断点.
      Themida每次加密代码都有变化,特征码很难捕获。比如要捕获的特征代码如果是 mov ,ecx:

      那么,代码 mov ,ecx可以有多种变化,如:

0068475C    51               push    ecx
0068475D    813424 1F15FD3Exor   dword ptr , 3EFD151F
00684764    8F00             pop   dword ptr
00684766    52               push    edx
00684767    BA 00000000      mov   edx, 0
0068476C    01C2             add   edx, eax
0068476E    8132 1F15FD3E    xor   dword ptr , 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 , ebx
0067970B    5B               pop   ebx
0067970C    8F00             pop   dword ptr
0067970E    8130 AF61DB7F    xor   dword ptr , 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 ,#90E9#//将jb改为jmp,去除代码扫描
   
   
    bp Addr_1   
    bp Addr_2   
    bp Addr_3   
    bp Addr_4
    bp Addr_5   

First:
    run
    cmp eip,Addr_1
    jzA1
    cmp eip,Addr_2      
    jzA2
    cmp eip,Addr_3
    jzA3
    cmp eip,Addr_4
    jzA4
    cmp eip,Addr_5
    jzA5
    jmpFirst

A1:   
    mov temAPI,eax   //eax 是API函数地址
    gn temAPI         //显示函数名
    log $RESULT
    add APIstr,""
    add APIstr,$RESULT_2//恢复API函数名(INT表)
    jmp First
A2:   
    mov temIAT,eax
    cmpedx,10000
    janext
    xoredx,80000000
    mov ,edx      //修复IAT表(写入序列号)
    jmp First
next:
    mov ,temAPI
    jmp First

A3:
    mov mem1,eax          //eax是内存中呼叫API地址
    mov temESI,      //获取转跳标记
    jmp First

A4:
    cmp temESI,AAAAAAAA
    jnz step3
    mov ,#FF25#      //修复代码中转跳地址
    mov ,temIAT   
    jmp First

step3:
    mov ,#FF15#      //修复代码中呼叫地址
    mov ,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    ;USER32.DialogBoxParamA
004026B2   .- FF25 48304000 jmp   dword ptr    ;kernel32.ExitProcess
004026DC   .- FF25 24304000 jmp   dword ptr    ;kernel32.GetModuleHandleA

0040272A   $- FF25 04304000 jmp   dword ptr
00402730   $- FF25 00304000 jmp   dword ptr

      注意到,40272A、402730后面没有出现函数名称,但和中都有确定的值。用alt-M 打开Memory map,发现comdlg32.dll库是红色显示,我什么也没做,关掉Memory map后,居然正常显示了如下:(不能解释其原因)

0040272A   $- FF25 04304000 jmp   dword ptr     ;comdlg32.GetOpenFileNameA
00402730   $- FF25 00304000 jmp   dword ptr     ;comdlg32.GetSaveFileNameA

      现在打开脚本3(OEP修复),将没有“$-”符号的那3个函数的相应的代码(照原样写),如下添加在脚本3中:

          mov ,#FF25A4304000#//<--对应 USER32.DialogBoxParamA
    mov ,#FF2548304000#//<--对应 kernel32.ExitProcess
    mov ,#ff2524304000#//<--对应 kernel32.GetModuleHandleA
    mov ,77D3B144      //<--在该地址中查到的值(注意倒着写),下同
    mov ,7C81CAFA      
    mov ,7C80B731      
          bp402652                   //设置对应的断点,下同
          bp4026B2
          bp4026DC
   
      为什么要写入脚本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 ,#FF25A4304000#
    mov ,#FF2548304000#
    mov ,#ff2524304000#
    mov ,77D3B144
    mov ,7C81CAFA
    mov ,7C80B731
          bp402652
          bp4026B2
          bp4026DC
          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    ;kernel32.GetModuleHandleA
       在堆栈中看到:
0013FF7C   0066E2F0/CALL 到 GetModuleHandleA
0013FF80   00000000\pModule = NULL

       这就是入口的第1个API调用。记下后,再F9,中断在VM虚拟机代码段中:
00B9CD7E    F3:A4         rep   movs byte ptr es:, byte ptr

       下面的状态条中显示:
       ecx = 00000004
       ds:==00
       es:==00
       原来是向中写入代码,代码值就是当前堆栈中的 003E1FF8 中的值。404000就是GetModuleHandleA 句柄地址。
       继续F9,中断在
00402652    - FF25 A4304000 jmp   dword ptr             ;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             ;kernel32.ExitProcess
       堆栈中出现
0013FF7C   0066E309/CALL 到 ExitProcess
0013FF80   00000000\ExitCode = 0
       请暂时不要关闭OD,整个入口代码都清楚了。将OD代码转到40261C处(乱码开始位置),逐条汇编如下:

       汇编语言               对应代码
       push0                  6A00
       call4026DC             E8B9000000
       mov   ,eax       A300404000
       push0                  6A00
       push401B92             68921B4000
       push0                  6A00
       push64               6A64
       push         FF3500404000
       call402652             E814000000
       push0                  6A00
       call4026B2             E86D000000
       int3                     CC               <--最后添上一个int3,让其返回系统

       4.脚本2的最后完善
   ---------------------------------
       现在打开脚本2,在标签A5:的bc后面写入(添加)如下代码:
       (1)修复OEP(完整抄录OD上汇编后出的2进制码):
       mov,#6A00E8B9000000A3004040006A0068921B40006A006A64FF3500404000E8140000006A00E86D000000CC#
       (2)为平衡堆栈,实现对OEP的转跳,继续写入:(前面记录过栈项的差值=60)
       mov,#81C460000000B81C26400050C3#
      (即对应下列汇编的二进制代码:
         add   esp,60
         mov   eax,40261C
         pusheax
         retn      )
       (3)再写入如下代码,作用是将API函数名称写入适当空白处(如403600)减轻dump后的录入文字工作。
       mov,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,APIstr                           //4xxxxx表示找一段空白,写入函数名称      
      mov,#81C460000000B8xxxx4x0050C3#       //xxxx4x入口地址反向写入
      mov,#558BEC6AFF90909090909090909090909090909090909090909090909083C4945356578965E8C745FC000000006A02#                     //代码长度由现场决定,4xxxxx表示OEP入口地址(乱码开始地址),添加的代码几乎无须改动
      sti
      sti
      sti
      sti   
      将脚本2存盘一次,再次运行它,当你具有一定经验后,可能会得到满意的结果。
      3.请注意,当须要把三个脚本用于新的程序时,请一定将前一个程序在脚本2、脚本3中添加的代码删除,否则可能有不希望的结果发生。
      4.通过三个脚本解密的程序,可以dump了,但dump后的程序还要加工才能运行。如修改IID导入表,恢复INT表,还原IAT表中数据,瘦身等,这些是PE文件基本知识,不涉及破解知识,不在此讨论

wgz001 发表于 2008-11-25 20:44

试炼品解压时CRC校验错误   :(

x80x88 发表于 2008-11-25 20:52

难度太高,纯灌水支持!

小糊涂虫 发表于 2008-11-26 14:54

对我来说有难度...:L

my1229 发表于 2008-11-27 10:33

支持楼主,下载了解压包出错。

xieyuzhe 发表于 2008-12-3 11:16

但dump后的程序还要加工才能运行。如修改IID导入表,恢复INT表,还原IAT表中数据,瘦身等,这些是PE文件基本知识,不涉及破解知识,不在此讨论


这些能稍微说明一下吗,我还是没搞懂

cdygr 发表于 2008-12-4 10:36

鹏鹏兄弟,好厉害哟!学习了,虽然不懂.以后多指点

lmw303 发表于 2008-12-5 06:46

太强大了!学习一下!

什么也不是 发表于 2008-12-5 12:33


   有视频教程就好啦

小瞬子 发表于 2008-12-5 18:32

学习学习..嘿嘿..
页: [1] 2 3 4 5
查看完整版本: Themida1.9.1.0版的通法破解