邪恶八进制信息安全团队技术讨论组's Archiver

天下奇毒 2005-3-11 01:44

[转载]拆解教程

========================================================================
ED!SON's Windows 95 拆解教程 V1.00        (中文翻译: JIM TYAN)
========================================================================
附:ED!SON教程的示范程序下载
目录
========
1.  Windows 拆解简介
2.  SoftICE for windows 2.00 快速入门
3.  如何找注册码
   3.1  Task Lock 3.00 - 一个简单的、仅有注册号保护的拆解例子
   3.2  Command Line 95 - 容易的“用户名/注册号"注册方式的拆解
4.  为Command Line 95制作注册器
5.  当程序调用一个函数时,PUSH和CALL这些指令是如何工作的
6.  关于VB程序
附录
========
A.  如何让SOFTICE载入符号
B.  函数的语法
C.  到哪里获取软件
D.  如何与我联系
1. WINDOWS拆解简介
===================================
拆解一个WINDOWS程序要比拆解一个在DOS下运行的程序要容易得多。因为在WINDOWS
中,只要WINDOWS的函数被使用了,想对任何真正要寻找蛛丝马迹的人隐藏什么东西
是比较困难的。
你需要的第一个(常常也是唯一的一个)工具就是SoftICE/Win 2.oo, 它是NuMega
出品的一个非常强大的调试工具。有些人发现它很难用,但我会教你如何使用它进行
有效的调试,也希望你们能够明白我将要告诉你们的:-)。 你应该看看在附录A 中有
关SoftICE/Win 2.oo的一些资料。我在安装SoftICE中从来没有碰到任何问题, 如果
你碰到了,我只好让你查它的参考手册了。
你所需要的所有软件的URL列在附录C中
- ED!SON, [email]edison@ccnux.utm.my[/email]

2. SOFTICE/WIN 2.OO 快速入门
=========================================
首先你应该对SOFTICE的屏幕有个大概的了解

|--------------------|
|    寄存器窗     |  用 'R' 编辑
|--------------------|
|    数据窗      |  用 'D' 列出某个地址的数据, 用 'E' 编辑
|--------------------|
|    代码窗      |  用 'U' 反汇编, 'A' 加入汇编代码
|--------------------|
|    命令窗      |  在这里输入命令和读出结果
|--------------------|
其它重要的功能键(在缺省的键定义下)
'H'/F1  - 在线帮助
F5/Ctr+D - 运行
F8     - 单步执行,切入子程序和中断
F10    - 单步执行,跳过子程序和中断
F11    - 跳出子程序,回到CALL的下一条指令
3. 如何寻找注册码
=============================
最容易的练习莫过于找个共享软件来试试把它注册。
3.1 Task Lock 3.00 - 一个简单的、仅有注册号保护的拆解例子
=================================================================
这是一个简单的保护,只有一个而且不会变化的注册码。
3.1.1 检查程序
===========================
它是16 bit程序,还是32 bit的? 在哪里输入注册信息? 在它的帮助里有没有
发现关于注册的一些线索?在你继续往下读之前,去把它们找一找!
....你现在应该在找吧!.....你是在找吗?.....你找到了吗?.......
好,你现在知道了,它是一个32位的WINDOWS 95应用程序,你可以在一个选择了
“Register|Register..."菜单就会弹出来的对话框中,输入一个注册码来注册
程序。通过阅读它的帮助,你也知道可以有两种注册方式:Individual和Site
License。所以这里对合法的注册码就可能有两次核对。
3.1.2 捕捉注册代码
===========================
注册码通常在普通的WINDOWS文字框中输入。为了检查输入的注册码, 程序必须
采用下面这些函数中的一个来把文字框中的内容读出来。
      16-bit               32-bit
      ------               ------
      GetWindowText          GetWindowTextA, GetWindowTextW
      GetDlgItemText          GetDlgItemTextA, GetDlgItemTextW
32-BIT函数的最后一个字母告诉我们函数是使用单字节还是双字节字符串。双字
节的注册码是很少见的。
也许你已经体会到我的意思了...“如果我能在GetWindowText时中断...", 你
的确能这样做!但是你首先必须确认这些符号已经由SOFTICE载入了(详见附录A)
在SoftICE中设定一个“陷阱"(实际上我们叫断点),你得先按Ctrl+D进入调
试状态,然后用命令BPX,后面跟著是函数的名字或者内存地址。因为Task Lock
是32位程序,所以我们在GetWindowTextA处设一个断点。 如果这个不行, 我们
可以再试其它的。
象这样在SoftICE中输入:
      :bpx getwindowtexta
如果你得到“No LDT"这样的错误信息,就要注意不要运行其它程序。我已经注
意到Norton Commander/Dos 会干扰这个功能。 你可以列出所有断点来检查一下
是否设好断点:
      :bl
你会看到这样的信息:
      00)  BPX USER32!GetWindowTextA C=01
你可以再按一次Ctrl+D,从SoftICE中退出,。
好了,不管怎么样,你已经设定好了断点以捕捉任何对GetWinowTextA的调用。
现在我们来在该输入注册码的地方输入一些数字,然后按下OK....你只得到了
一个信息框告诉你输入的注册码是无效的。看来不是GetWindowTextA....我们
来试试GetDlgItemTextA。首先删除旧的断点:
      :bc 0
(0 表示在断点列表中的第0个断点)
然后设定新的断点:
      :bpx getdlgitemtexta
再来试一次.....

3.1.3 在调试器中
=====================
哇!行了!你已经在SoftICE中了,就在函数GetDlgItemTextA开始的地方。按
F11键,回到调用函数的地方。现在你到了SGLSET.EXE的内部,如果你还没把握
的话,看看代码窗和数据窗中间的一行,你应该看到这样的东西:
      ----------SGLSET!.text+1B13----------
你可以这样禁止一个断点:
      :bd 0
以后想再开启它的话,可以这样:
      :be 0
代码窗的第一行是:
      CALL   [USER32!GetDlgItemTextA]
按几次Ctrl+Up 直到你看到下面这几行。如果对汇编一点也不懂的话,看我加的 注解吧:
      RET                     ; 函数结束
      PUSH   EBP                ; 函数开始
      MOV    EBP, ESP
      SUB    ESP, 0000009C
      PUSH   ESI
   >  LEA    EAX, [EBP-34]         ; EAX = EBP-34
      PUSH   EDI
      MOVE   ESI, ECX
      PUSH   32                ; 输入字符串的最大长度
   >  PUSH   EAX                ; 输入字符串的缓冲地址
      PUSH   000003F4            ; 控制标识
      PUSH   DWORD PTR [ESI+1C]      ; 对话框的句柄
      CALL   [USER32!GetDlgItemTextA]  ; 取得输入
PUSH指令保存那些数值以供后面使用。我已经在重要的地方加上了一个 '>'字符
作上记号. 看这几行我们就知道字符缓冲区的地址保存在EAX中,而EAX等于 EBP-34。
所以我们来看看EBP-34那里有什么:
      :d ebp-3
你应该能在数据窗中看到你输入的东西。下面我们得来找开始核对输入注册码的
地方。按F10一步一步地单步运行直到你发现与EBP-34有点关系的地方.... 你不
用单步运行多久,就会看到这些代码:
   >  LEA    EAX, [EBP+FFFFFF64]         ; EAX = EBP-9C
      LEA    ECX, [EBP-34]             ; ECX = EBP-34
      PUSH   EAX                    ; 保存 EAX
      PUSH   ECX                    ; 保存 ECX
   >  CALL   00403DD0                ; Call 一子程序
      ADD    ESP, 08                 ; 删除保存的信息
      TEST   EAX, EAX                ; 检查返回值
      JNZ    00402BC0                ; 如果不是零的话跳转
对我来说,马上就可以看出这象是一个字符比较程序。它们工作起来就是这样:
      * 输入两个字符串
       如果相同就返回零
       否则返回非零
那为什么程序要用一个字符串来和你输入的相比较呢?看它是不是合法的!(可
能你已经想到了)。那么是什么东西躲在 [EBP+FFFFFF64] ? SoftICE处理负数
还不是很好,所以得算算:
      100000000 - FFFFFF64 = 9C
在 SoftICE 用这样的命令:
      :? 0-FFFFFF64
100000000 对SoftICE来说太大了,但它还是给出了相同的结果。
现在是...来看看什么东西躲在EBP-9C那里的时候了...这样输入命令:
      :d ebp-9c
数据窗口会显示出一大排数字 ─ 注册码!但是记住我前面说过的...两种注册
方式对应两个注册码....所以你把这些注册码抄下来以后,继续用F10单步运行
....我们会遇到这些代码:
   >  LEA    EAX, [EBP-68]             ; EAX = EBP-68
      LEA    ECX, [EBP-34]             ; ECX = EBP-34
      PUSH   EAX                    ; 保存 EAX
      PUSH   ECX                    ; 保存 ECX
   >  CALL   00403DD0                ; 再次调用子程序
      ADD    ESP, 08                 ; 删除保存的信息
      TEST   EAX, EAX                ; 检查返回结果
      JNZ    00402BFF                ; 如果非零跳转
你在EBP-68处找到了什么?不错吧...另一个注册码。
      :d ebp-68
我们的练习结束了,希望一切顺利。

3.2  Command Line 95 -容易的用户名/注册码方式的注册、注册器
=================================================================
这是一个非常好的例子,注册码的计算也很简单。

3.1.1 检查程序
===========================
检查程序以后你知道它是32位的应用程序,要求输入名字和注册码。 让我们开始!
3.1.2 捕捉代码
===========================
我们象拆解TaskLock那样 ─ 设置断点。我们可以在两个可能性最大的两个函数
都设上断点:GetWindowTextA 和 GetDlgItemTextA. 按下Ctrl+D 进入SoftICE, 然后:
      :bpx getwindowtexta
      :bpx getdlgitemtexta
接下来进入注册对话框,输入一个名字和一些数字(多数情况下是一个整数),
我是这么写的,然后按OK....
      Name:  ED!SON '96
      Code:  12345
程序在 GetDlgItemTextA 处停住了,就象TaskLock一样。我们按F11回到调用它
的地方。用 Ctrl+Up卷动窗口直到看到这些:
      MOV    ESI, [ESP+0C]
      PUSH   1E                    ; 最大长度
      PUSH   0040A680                ; 缓冲地址
      PUSH   000003ED                ; 控制柄
      PUSH   ESI                    ; 对话柄
      CALL   [User32!GetDlgItemTextA]
数字40A680引起了我们的注意,看看那里有什么:
      :d 40a680
如果没有我们输入的名字,数据窗口里有些什么呢?好了,我们来研究下面的一 段代码:
      PUSH   00
      PUSH   00
      PUSH   000003F6                ; 控制柄
      MOV    EDI, 0040A680             ; 保存缓冲区地址
      PUSH   ESI                    ; 对话柄
      CALL   [User32!GetDlgItemInt]
GetDlgItemInt 和 GetDlgItemText差不多,但它从文字框中返回一个整数。
它出在EAX中返回来的,所以单步运行通过这些代码,再来看看寄存器窗....
对我而言是:
      EAX=00003039
十六进制数3039是多少? 输入:
      :? 3039
我们得到:
      00003039  0000012345  "09"
      ^ 16进制  ^ 十进制    ^ ASCII
正如你看到(和已经猜到)的那样,它是你输入的注册码。OK,下面怎么办?让
我们来看下面的代码:
      MOV    [0040A548], EAX            ; 返回注册码
      MOV    EDX, EAX                ; 同时保存在DX中

3.1.3 计算注册码
==========================
这样注册码就算出来了
      MOV    ECX, FFFFFFFF             ; 这几行计算字符长度
      SUB    EAX, EAX                ; .
      REPNZ SCASB                    ; .
      NOT    ECX                    ; .
      DEC    ECX                    ; ECX <-- 长度
      MOVSX  EAX, BYTE PTR [0040A680]      ; 读入40A680处的一字节
      IMUL   ECX, EAX                ; ECX = ECX * EAX
      SHL    ECX, 0A                 ; 左移 0A 次
      ADD    ECX, 0002F8CC             ; 结果加上2F8CC
      MOV    [0040A664], ECX
验证合法性....
      CMP    ECX, EDX                ; 比较
      JZ    00402DA6                ; 如果相同就....
当你运行到比较这一步时,就可以得到你真正的注册码:
      :? ecx
对我而言它是:
      000DC0CC  0000901324
也就是说我的正确的注册码是901324.
按F5或者Ctrl+D让它运行,然后用正确的注册码(十进制)再来一次。这一次 成功了!


4.  为COMMAND LINE 95制作注册器
=========================================
我们把上面计算注册码的代码翻译成C语言程序。 最明了的计算公式就是:
code=((uppercase_first_char * length_of_string) << 0x0A) + 0x2f8cc;
注(1): 别忘了一件事 就是把所有字符转成大写
  (2): "<< 0x0A" 实际上就是 "乘以 2^10"
完整的程序就是:
      #include <string.h>
      #include <stdio.h>
      int main()
      {
           unsigned long code;
           unsigned char buffer[0x1e];
           printf("CommandLine95 Keymaker by ED!SON &#39;96\n");
           printf("Enter name:  ");
           gets(buffer);
           strupr(buffer);
           code = ( ((unsigned long)buffer[0] *
                (unsigned long)strlen(buffer))
                << 0x0A) + 0x2f8cc;
           printf("Your code is: %lu", code);
           return 0;
      }
爽吧?

4. 当程序调用一个函数时,PUSH和CALL这些指令是如何工作的
============================================================================
我们重新来看看TaskLock中的这段代码:
      PUSH   32                ; 字符串最大长度
      PUSH   EAX                ; 字符缓冲区地址
      PUSH   000003F4            ; 控制标识
      PUSH   DWORD PTR [ESI+1C]      ; 对话框柄
      CALL   [USER32!GetDlgItemTextA]  ; 获得字符串
如果认为是C语言编译出来的话,这个CALL应该是这样:
  GetDlgItemTextA(hwndDlg, 0x3F4, buffer, 0x32);
            ^ [ESI+1C]    ^ EAX
PUSH把数据保存在叫做堆栈的地方。每个PUSH把新的数据放在堆栈的顶部,被调
用的子程序就挨个地检查躺在堆栈中的数据,按照定义来使用它们。
附录
A. 让SoftICE载入符号
==============================
你可以用exp getwindowtext命令来检查SoftICE是否已经为GetWindowText装入
了符号,象这样:
     :exp getwindowtext
如果你没有得到所有GetWindowText函数的列表,你就得编辑\SIW95\WINICE.DAT,
在"Examples of export symbols that can be included for chicago"这段文字
以后的那些 &#39;exp=&#39;的行首去掉&#39;;&#39;,为了节省内存,选择最重要的几个就可以了:
  kernel32.dll
  user32.dll
  gdi32.dll
编辑完后,重新起动计算机使其生效。

B. 函数语法
============================
如果你看看下面的函数声明,对我们上面讲到的函数调用就容易明白了:
int GetWindowText(int windowhandle, char *buffer, int maxlen); int
GetDlgItemText(int dialoghandle, int controlid, char *buffer,
             int maxlen);
int GetDlgItemInt(int dialoghandle, int controlid, int *flag, int type);
这些函数的详细描述,可参考Windows/Win32编程手册

C. 如何与我联系
================
On IRC (EFNet):      In #Ucf96, #Cracking
By e-mail:         [email]edison@ccnux.utm.my[/email] OR [email]an461165@anon.penet.fi[/email] On my
homepage:      [url]http://www.geocities.com/SoHo/2680/cracking.html[/url]
======================================================================
译后记:
花了一个下午的时间(上班时间啊!),译完这篇拆解教程。不知大家看不看
得懂?ED!SON的文章是很浅显易懂的,我担心会让我毛手毛脚地闹出歧义来,
如果真有什么错误的话,帐应该算在我头上(请大家来信指出)。ED!SON的这
篇文章对初学者来说非常值得一读,我就是看完这篇文章以后,马上连续CRACK
了几个程序(就这么快、这么简单!)下次我就把CRACK WINZIP6.2的过程讲讲
吧(WINZIP6.2的注册器已经泛滥成灾了,就当练习练习吧)。欢迎来信交流,
你CRACK了什么程序,别忘了告诉我你用的方法;如果觉得我翻译得还算看得下
去话,就给些鼓励吧。
CFIDO ID : Jim Tyan
EMAIL : [email]jimtyan@tasteful.com[/email]
田学军    8878888 CALL 29549  [email]jimtyan@tasteful.co[/email]

天下奇毒 2005-3-11 01:44

实验对象:例题Chap4-eg-1.zip ;

破解工具:W32Dasm,Hiew;

1、思路提示:首先要试运行要破解的程序,了解其一些提示信息,如:文本/NAG屏/按钮等等.最重要的就是出错信息 .如: "Wrong serial.."记下,因为你在后面要用到它,你需要它来找到call出错messagebox的地方.当 W32Dasm反编译后你会看到一屏幕难懂的汇编码 .你还记得刚要你记下的那个message么? 此时单击在工具栏里的串式数据参考SDR (=String Data Reference).这个功能可是非常有用的.

在串式数据参考SDR中找到那个提示信息(它也许只显示了信息的一部分) ,此时双击它,来到相关代码处,再分析源代码。

2、运行crackme,输入假的序列号,点击CHECK,出现错误提示:"Incorrect try again!! "记下。

3、将crackmer备份一份,用W32DASM反汇编它。

4、一旦完成反汇编 , 点串式数据参考(string data reference)按钮 , 在列出的字符串列表中找到 : "Incorrect try again!! " 并双击它 . (注:如代码中有多处有此字串,你再次双击后,光标将出现在下一代码上)

5、关闭这个窗口回到主窗口 , 你应该能够看到下面这一行 :

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401595(C)
|
:004015AD 6A40              push 00000040

* Possible StringData Ref from Data Obj ->"CrackMe"//错误提示窗口的标题
                       |
:004015AF 686C304000          push 0040306C

* Possible StringData Ref from Data Obj ->"Incorrect try again!!"//错误提示处,我们来到这一行
                       |
:004015B4 6874304000          push 00403074
:004015B9 8B4DE0            mov ecx, dword ptr [ebp-20]


6、现在你必须从这行起向上找 , 直到找到有这样的命令为止 :cmp,jne,je,test 等等 .

CMP = 比较 (如 CMP EAX, EBX) <- 比较EAX和EBX
JE = 如果相等就跳转
JNE = 如果不相等就跳转
JL = 如果小于就跳转
JLE = 如果小于等于就跳转
JA = 如果大于就跳转
JAE = 如果大于等于就跳转
JMP = 无条件跳转

7、注意这一行代码:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401595(C)
|
:004015AD 6A40              push 00000040


:00401595(C) 是代码位置而不是offset,表示指令由00401595一行跳转到此 .此时你在利用菜单的转到代码位置功能或按shift+F12,在对话框中输入:00401595,你将来到此:


:00401585 8D4DE4            lea ecx, dword ptr [ebp-1C]
:00401588 51               push ecx
:00401589 8D55F4            lea edx, dword ptr [ebp-0C]
:0040158C 52               push edx

* Reference To: KERNEL32.lstrcmpA, Ord:02FCh
                       |
:0040158D FF1500204000        Call dword ptr [00402000]
:00401593 85C0              test eax, eax
:00401595 7516              jne 004015AD


8、此时你借助SOFTICE动态调试能很快找到序列号,在这我们今天用暴力法破解,注意 :

:0040158D FF1500204000        Call dword ptr [00402000]//真假序列号比较核心(调用函数lsrcmpa比较)
:00401593 85C0              test eax, eax//用eax当旗帜,如相等,则eax=0
:00401595 7516              jne 004015AD//如不跳转则注册成功

看明白了吗?要让程序接受任何注册码就只要把JNE (=不相等就跳)改成JE (=相等就跳).或把改成空指令nop(什么也不执行),这样前一各改法要注册就只能输入错误的注册码,后者可任意注册码。

9、将绿色的光条移到 :00401595 7516 jne 004015AD上 , 在窗口底部有一行字指示这句命令的偏移地址 , 此处为 @Offset 00001595h. 这就是应该修改的地方了 .



10、启动hiew, 打开crackme.exe,按 F4 然后选择 decode mode, 按 F5 输入偏移地址1595(@Offset 00001595h). 你应该看到下面这几行 :

?00401593: 85C0 test eax,eax
?00401595: 7516 jne .0004015AD

11、这就是修改的地方了,按F3进入修正状态,在机器码处直接用7416代替7516,按F9存盘。或在这一行按F2或回车进入小汇编修改状态,输入正确的指令。

12、第二种修改方法是用两个NOP指令(NOP指令机器码是90,是一个字节)代替机器码:7516,即改为:9090

13、运行 crackme, 随便输入几个字符试试 . 成功了 ! (当然这只是对那些简单的程序有效)

页: [1]
© 1999-2008 EvilOctal Security Team