zjid520 发表于 2007-12-18 19:48:55

菜鸟学算法<二>注册流程篇

作者:鹭影依凌
转贴自:一蓑烟雨
声明:
1.以下内容都是个人在学习中的一些心得体会,写给新手的,高手飘过
2.文章难免有疏漏之处,欢迎各位兄弟批评指正
3.本文原创于UnPaKcN,如转载,请保持文章完整性


有了第一篇的基础知识学习
下面我们以AuRRa公司的龙头产品Media-WorkShop为实例来进行第二篇


学习目标
1.培养良好的"破解书写习惯"
2.学会判断程序段代码的功能
3.学会逆向分析和爆破


一、培养良好的破解习惯

1.在指令右侧注释栏填写必要的跟踪注释
应该着重注释的内容
(1).寄存器的值(数值/字符串)

(2).运算表达式
便于完成将来对算法的总结

(3).CALL功能
确定哪些CALL是关键CALL,哪些是无大意义的CALL
比如:
A.经过一个CALL后,字符串中的小写字母全部变为大写字母
这样的CALL,显然没有必要跟进去
B.又如
CALL XXXXXXXX
TEST EAX,EAX
JExxxxxxxx  //跳向注册成功

这样的CALL一般来说是关键CALL(算法CALL)
很有必要跟进去探个究竟

(4).代码段的功能
确定代码段的功能,以便从全局的角度对整个注册流程进行分析


2.调试过程中,不同阶段,观察侧重点不同

A.首次调试
通常需要着重观察反汇编窗口和信息提示窗口

B.区段集中调试时期
应多注意寄存器和堆栈窗口

C.某条特殊指令
集中跟随数据窗口


3.一气呵成,又不拘泥
所谓一气呵成,就是指已经有了思路,那么就要一鼓作气,将其进行到底
不然,开工一半,中途搁置
过几天再拿起来...晕,又得从头分析

不拘泥
不是每个软件都是软柿子,不是每个软件设计者都不懂得ANTI-DBG
要知道,现在的程序员的软件保护意识越来越强的!!

暂时没有思路就先放一放,说生活中的某个细节会突然触发你的灵感

如果只是为了破解而破解,那么建议你放弃CRACKER这条路
学习破解只是一种很好的学习别人程序设计思路的方式
并在学习过程中体会到自身提高的乐趣



二、过程分析笔记

!!!阅读建议:
由于本篇是完全面向新手的文章,读者可以先下载注释笔记,然后浏览下
然后边调试边看以下分析内容,效果会更好
切记:一定要自己亲手调试

在本例中
我们用提示字符串进行注册代码段的定位
提示信息:"invalid username or registration code"

向上看代码,一般是在程序段的段首下断点

关于"段首"的概念
可以这样理解:在向上代码段中出现第一个retn的下方
一般是push esp,push -1,push A,B格式的


本例中,我们在地址0045BDB0处F2下断

流程大致应该是这样的:
1.运行程序,输入试练码,程序被断下

2.进行首次跟踪
主要完成些重要数据的记录(旁白注释)

3.将程序细分段,确定各个分段的作用

4.进行细跟踪
 搞定算法


实例讲解:

输入试练码:
ID:luying10
SN:9876543210abcdef

点击确定,程序被OD断下

注意观察代码窗口、信息提示窗口、(寄存器窗口只要注意字符串类型数据就好)

并随时记录有用信息

;====================================================================|
0045BDB0.6A FF      push    -1                              ;//断在这
0045BDB2.68 983A4700push    00473A98                        ;
0045BDB7.64:A1 0000000>mov    eax, dword ptr fs:
0045BDBD.50            push    eax
0045BDBE.64:8925 00000>mov    dword ptr fs:, esp
0045BDC5.51            push    ecx
0045BDC6.53            push    ebx
0045BDC7.56            push    esi
0045BDC8.8BF1          mov    esi, ecx
0045BDCA.57            push    edi
0045BDCB.8D4C24 0C    lea    ecx, dword ptr
0045BDCF.E8 D4BE0000call    <jmp.&MFC42.#540>
0045BDD4.6A 01      push    1
0045BDD6.8BCE          mov    ecx, esi
0045BDD8.C74424 1C 000>mov    dword ptr , 0
0045BDE0.E8 43C30000call    <jmp.&MFC42.#6334>
0045BDE5.E8 6CC20000call    <jmp.&MFC42.#1168>
0045BDEA.8B48 04      mov    ecx, dword ptr
0045BDED.E8 78C30000call    <jmp.&MFC42.#1669>
;
0045BDF2.68 E8030000push    3E8                              ; /Timeout = 1000. ms
0045BDF7.FF15 3C524700 call    dword ptr [<&KERNEL32.Sleep>]    ; \Sleep
0045BDFD.E8 54C20000call    <jmp.&MFC42.#1168>
0045BE02.8B48 04      mov    ecx, dword ptr
在经过地址0045BDF7的CALL时候,程序停顿了一小会儿
我们看英文注释也大概猜到这段代码的作用了----加入延时验证,防止暴力攻击
;--------------------------------------------------------------------|

0045BE05.E8 5AC30000call    <jmp.&MFC42.#2652>            ;
0045BE0A.8B46 64      mov    eax, dword ptr           ;
0045BE0D.8B4E 60      mov    ecx, dword ptr           ;
程序运行到0045BE0A这行时候
信息提示窗口提示dword ptr = "9876543210abcdef"
中保存的是序列号
这不正是我们最开始天的序列号嘛
运行本行代码后,发现EAX变成了序列号
同样,运行0045BE0D这行,ECX被赋予了用户名
;--------------------------------------------------------------------|
0045BE10.8D5E 64      lea    ebx, dword ptr           ;
0045BE13.8D7E 60      lea    edi, dword ptr           ;
这两行只有值,没有所谓的字符串的东东
我们学过LEA是装载有效地址的指令
中保存的是序列号
那么,执行完lea    ebx, dword ptr 后,EBX中保存的就应该是序列号的地址
同样,EDI中保存用户名的地址
;--------------------------------------------------------------------|
0045BE16.50            push    eax                              ;序列号压栈
0045BE17.51            push    ecx                              ;用户名压栈
压栈操作,不用多解释,下面是一个CALL,我们首次跟踪,先F8过
;---------------------------------------------------------------------
0045BE18.E8 83430000call    004601A0                        ;
0045BE1D.83C4 08      add    esp, 8
0045BE20.85C0          test    eax, eax
0045BE22.75 1F      jnz    short 0045BE43                  ;
运行了0045BE18这行的CALL后,EAX变为0
继续F8,来到跳转,跳转指向是绿色,跳转未实现
下面英文提示invalid username or registration code
显然再继续下去是提示注册失败咯
;--------------------------------------------------------------------|
0045BE24.6A 40      push    40
0045BE26.68 9C7C4900push    00497C9C                        ;sorry
0045BE2B.68 707C4900push    00497C70                        ;invalid username or registration code      sorry
0045BE30.8BCE          mov    ecx, esi
0045BE32.E8 EDC30000call    <jmp.&MFC42.#4224>            ;
提示注册失败,点击确定按钮,继续F8
;--------------------------------------------------------------------|
0045BE37.C705 082C4E00>mov    dword ptr , 0            ;
0045BE41.EB 76      jmp    short 0045BEB9                  ;//跳走
程序跳走

;跳到这里来
0045BEB9>8BCE          mov    ecx, esi                        ;->来到这
0045BEBB.E8 74C20000call    <jmp.&MFC42.#4853>
0045BEC0.8D4C24 0C    lea    ecx, dword ptr
0045BEC4.C74424 18 FFF>mov    dword ptr , -1
0045BECC.E8 8FBD0000call    <jmp.&MFC42.#800>
0045BED1.8B4C24 10    mov    ecx, dword ptr
0045BED5.5F            pop    edi
0045BED6.5E            pop    esi
0045BED7.5B            pop    ebx
0045BED8.64:890D 00000>mov    dword ptr fs:, ecx
0045BEDF.83C4 10      add    esp, 10
0045BEE2.C3            retn                                    ;//结束
;====================================================================|

首轮跟踪结束,总结下:

1.关键跳转0045BE22

2.关键CALL->004601A0

3.标志位EAX
EAX = 0 注册失败
EAX = 1注册成功

4.可疑处,地址:0045BE7mov dword ptr , 0


两个研究方向
1.关键CALL->004601A0
2.可疑的赋值


首先研究关键CALL

;====================================================================|
;在地址0045BE18处跟进关键CALL->004601A0
;--------------------------------------------------------------------|
004601A0/$53            push    ebx                              ;
OD下方显示:本地调用来自 00428B46, 00429225, 0044BB64, 0045120C, 00455368, 0045BE18, 0045E824
004601A1|.55            push    ebp
注意:从这里我们可疑看到,程序有多处代码调用了CALL=>004601A0
这也验证了为了程序只修改0014BE22处的关键跳转不能达到完全爆破的目的
;--------------------------------------------------------------------|
004601A2|.8B6C24 0C    mov    ebp, dword ptr           ;
EBP = = 用户名

004601A6|.56            push    esi
004601A7|.57            push    edi
004601A8|.BE 84014A00mov    esi, 004A0184                  ;ESI = 4A0184
004601AD|.8BC5          mov    eax, ebp                        ;EAX = 用户名
004601AF|>8A10          /mov    dl, byte ptr             ;用户名的第1个字符
004601B1|.8A1E          |mov    bl, byte ptr
004601B3|.8ACA          |mov    cl, dl
004601B5|.3AD3          |cmp    dl, bl
004601B7|.75 1E      |jnz    short 004601D7                  ;

跳转实现//跳出循环体(跳至EAX置零)
;----------------------------
004601B9|.84C9          |test    cl, cl
004601BB|.74 16      |je      short 004601D3                  ;
004601BD|.8A50 01      |mov    dl, byte ptr
004601C0|.8A5E 01      |mov    bl, byte ptr
004601C3|.8ACA          |mov    cl, dl
004601C5|.3AD3          |cmp    dl, bl
004601C7|.75 0E      |jnz    short 004601D7
004601C9|.83C0 02      |add    eax, 2
004601CC|.83C6 02      |add    esi, 2
004601CF|.84C9          |test    cl, cl
004601D1|.^ 75 DC      \jnz    short 004601AF                  ;//循环

004601D3|>33C0          xor    eax, eax                        ;
004601D5|.EB 05      jmp    short 004601DC                  ;
程序跳出了循环体,条件是用户名不为空

我们可以看看如果程序从004601D5这跳会怎样
首先跳至004601DC
因为EAX = 0,所以又跳至00460242
接着EAX置零后返回
而我们的主程序在EAX=0的时候是注册失败的

;----------------------------
004601D7|>1BC0          sbb    eax, eax                        ;
004601D9|.83D8 FF      sbb    eax, -1                        ;
004601DC|>85C0          test    eax, eax
004601DE|.74 62      je      short 00460242                  ;

EAX=1,跳转未实现

综上,我们可疑得出,上段代码是检查用户名是否为空
;--------------------------------------------------------------------|

004601E0|.8B7C24 18    mov    edi, dword ptr           ;
EDI = = 序列号
004601E4|.BE 84014A00mov    esi, 004A0184                  ;ESI = 4A0184
004601E9|.8BC7          mov    eax, edi                        ;
004601EB|>8A10          /mov    dl, byte ptr             ;序列号的第i个字符
004601ED|.8A1E          |mov    bl, byte ptr
004601EF|.8ACA          |mov    cl, dl
004601F1|.3AD3          |cmp    dl, bl
004601F3|.75 1E      |jnz    short 00460213                  ;
跳转实现//跳出循环体(跳至EAX置零)
004601F5|.84C9          |test    cl, cl
004601F7|.74 16      |je      short 0046020F                  ;
004601F9|.8A50 01      |mov    dl, byte ptr
004601FC|.8A5E 01      |mov    bl, byte ptr
004601FF|.8ACA          |mov    cl, dl
00460201|.3AD3          |cmp    dl, bl
00460203|.75 0E      |jnz    short 00460213
00460205|.83C0 02      |add    eax, 2
00460208|.83C6 02      |add    esi, 2
0046020B|.84C9          |test    cl, cl
0046020D|.^ 75 DC      \jnz    short 004601EB                  ;//循环

0046020F|>33C0          xor    eax, eax                        ;
00460211|.EB 05      jmp    short 00460218                  ;
程序跳出了循环体,条件是序列号不为空

我们可以看看如果程序从00460211这跳会怎样
首先跳至00460218
因为EAX = 0,所以又跳至00460242
接着EAX置零后返回
而我们的主程序在EAX=0的时候是注册失败的
00460213|>1BC0          sbb    eax, eax                        ;
00460215|.83D8 FF      sbb    eax, -1                        ;
00460218|>85C0          test    eax, eax                        ;
0046021A|.74 26      je      short 00460242                  ;

EAX=1,跳转未实现

综上,我们可疑得出,上段代码是检查序列号是否为空
;--------------------------------------------------------------------|
0046021C|.57            push    edi                              ;
0046021D|.55            push    ebp                              ;
0046021E|.E8 6DF9FFFFcall    0045FB90                        ;
00460223|.83C4 08      add    esp, 8
00460226|.85C0          test    eax, eax                        ;
00460228|.75 0E      jnz    short 00460238                  ;

EDI保存序列号
EBP保存用户名
EAX = 0
跳转未实现,继续

有一个CALL->0045FB90 ,下次跟入


假设跳转实现,程序跳至00460238
EAX = 1,返回-》注册成功
;--------------------------------------------------------------------|
0046022A|.57            push    edi                              ;
0046022B|.55            push    ebp                              ;
0046022C|.E8 8FFBFFFFcall    0045FDC0                        ;
00460231|.83C4 08      add    esp, 8
00460234|.85C0          test    eax, eax                        ;
00460236|.74 0A      je      short 00460242                  ;

EDI保存序列号
EBP保存用户名
EAX = 0
跳转实现

EAX = 0,返回,注册失败


假设跳转未实现
EAX = 1,返回,注册成功
;--------------------------------------------------------------------|
00460238|>5F            pop    edi
00460239|.5E            pop    esi
0046023A|.5D            pop    ebp
0046023B|.B8 01000000mov    eax, 1                        ;
00460240|.5B            pop    ebx
00460241|.C3            retn                                    ;
标志位赋值:EAX = 1 //返回
;--------------------------------------------------------------------|
00460242|>5F            pop    edi                              ;
跳转来自 004601DE, 0046021A, 00460236
00460243|.5E            pop    esi
00460244|.5D            pop    ebp
00460245|.33C0          xor    eax, eax                        ;
00460247|.5B            pop    ebx
00460248\.C3            retn                                    ;
EAX置零//返回
;====================================================================|


跟进call    0045FB90 ,只有一处调用

跟进call    0045FDC0,只有一处调用


我们可以爆破了
在第二层中想办法让EAX的返回值为1

思路.
1.修改关键跳转
地址00460236处jnz改为jmp

2.修改赋值语句
地址00460245处xor eax,eax修改为inc eax(nop填充)



哈哈,这么简单就搞定``````

还有个可疑点呢,别忘了噢~~~

我们在运行到0045BE20.85C0          test    eax, eax
这行时候,修改下EAX的值,将其由0置为1
使下面的跳转实现

;--------------------------------------------------------------------|
0045BE43>8B07          mov    eax, dword ptr             ;
0045BE45.8D4C24 0C    lea    ecx, dword ptr
0045BE49.50            push    eax                              ;
0045BE4A.68 547C4900push    00497C54                        ;license to:%s
0045BE4F.51            push    ecx
0045BE50.E8 07C20000call    <jmp.&MFC42.#2818>
0045BE55.8B5424 18    mov    edx, dword ptr           ;
提示窗口信息(ASCII "License To:luying10            ")
;---------------------------
0045BE59.83C4 0C      add    esp, 0C
0045BE5C.8BCE          mov    ecx, esi
0045BE5E.6A 40      push    40
0045BE60.68 487C4900push    00497C48                        ;thank you
0045BE65.52            push    edx                              ;
提示窗口信息(ASCII "License To:luying10            ")
0045BE66.E8 B9C30000call    <jmp.&MFC42.#4224>            ;

提示注册成功
;---------------------------
0045BE6B.57            push    edi
0045BE6C.B9 04034A00mov    ecx, 004A0304                  ;ASCII "萷?"
0045BE71.E8 3EBE0000call    <jmp.&MFC42.#858>
0045BE76.53            push    ebx
0045BE77.B9 08034A00mov    ecx, 004A0308                  ;ASCII "乇?"
0045BE7C.E8 33BE0000call    <jmp.&MFC42.#858>
0045BE81.C705 082C4E00>mov    dword ptr , 1            ;
这块很可疑噢~
mov    dword ptr , 1
;---------------------------
0045BE8B.8B3F          mov    edi, dword ptr             ;
0045BE8D.57            push    edi                              ;
0045BE8E.68 A0F24800push    0048F2A0                        ;username
0045BE93.68 94F24800push    0048F294                        ;register
0045BE98.B9 F4014A00mov    ecx, 004A01F4
0045BE9D.E8 5EC7FEFFcall    00448600                        ;
EDI为用户名
0045BEA2.8B1B          mov    ebx, dword ptr             ;
0045BEA4.B9 F4014A00mov    ecx, 004A01F4
0045BEA9.53            push    ebx                              ;
0045BEAA.68 84F24800push    0048F284                        ;registercode
0045BEAF.68 94F24800push    0048F294                        ;register
0045BEB4.E8 47C7FEFFcall    00448600                        ;
EBX为序列号
;---------------------------
0045BEB9>8BCE          mov    ecx, esi                        ;
0045BEBB.E8 74C20000call    <jmp.&MFC42.#4853>
0045BEC0.8D4C24 0C    lea    ecx, dword ptr
0045BEC4.C74424 18 FFF>mov    dword ptr , -1
0045BECC.E8 8FBD0000call    <jmp.&MFC42.#800>
0045BED1.8B4C24 10    mov    ecx, dword ptr
0045BED5.5F            pop    edi
0045BED6.5E            pop    esi
0045BED7.5B            pop    ebx
0045BED8.64:890D 00000>mov    dword ptr fs:, ecx
0045BEDF.83C4 10      add    esp, 10
0045BEE2.C3            retn                                    ;//结束

;====================================================================|


注册失败mov    dword ptr , 0
注册成功mov    dword ptr , 1

分析,将注册失败后也赋值为1
但在整个程序中,有很多地方调用关键CALL
并且两段是不相干的代码
修改地址赋值不能改变关键CALL
修改关键CALL后该地址赋值就相应随之发生变化

结论:修改此处,不能达到完美爆破的目的
      只修改关键CALL就可以达到完美爆破的目的


通过本文,主要是让兄弟们学会如何给程序加注释,和确定程序代码段的功能

两点说明:
1.恰当的书写注释
注释不是越多越好,太多的注释反而影响程序的阅读
用最少的文字表达概括语句含义

2.注册流程分析的意义
明确区段代码的作用,为以后定位注册关键代码段做铺垫,减少不必要的代码分析

Now,check it yourself```Come On,U'll be the best!!

[ 本帖最后由 zjid520 于 2007-12-18 19:54 编辑 ]

hackhd 发表于 2007-12-25 03:42:00

很适合我啊。这东西 .我慢慢的看。

larry_wang 发表于 2007-12-25 10:19:17

学了,谢谢!继续学习中

实现者 发表于 2007-12-25 15:49:12

先收下,慢慢学/:014 /:014

蒙文软件 发表于 2008-2-20 10:43:55

/:001好的谢谢

Aeddy 发表于 2008-8-27 08:52:16

好东西啊。谢谢了

hwmzly 发表于 2009-11-26 10:00:43

好东西啊。谢谢了

hk35544 发表于 2009-11-26 13:03:19

不错,看来论坛那里还有不少好东西等待发掘啊。

302 发表于 2009-11-27 22:19:30

好东西啊 慢慢学习了。。

tianfire 发表于 2009-11-29 07:50:43

谢谢,我需要
页: [1] 2
查看完整版本: 菜鸟学算法<二>注册流程篇