千江月 发表于 2009-4-6 12:33:08

算法分析的教程

从一蓑烟雨看到的,也就转过来了,很不错的。。





前言:论坛上有关算法分析的文章不少,也不少精品
但对于刚学CARACK来说,只是叹为观止
原因如下:
1.论坛高手如云,菜鸟也不占少数,有些甚至对汇编指令还不是很熟悉
2.不少算法分析文章分析的是相当完美了,但是作者仅仅展示给的是最终的成果,而没有把分析的思路,分析的过程展示给我们

相信更多的兄弟需要的是一种分析思想,分析理念!
本文(本系列)将对这些问题展开逐一讨论
第一篇:必要基础知识
第二篇:软件注册流程
第三篇:算法分析实例


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


==文章结构==

1.ASCii表(基础)

2.对特殊汇编指令讲解(推荐)

3.定位程序注册代码段


一、ASCII表

◇数 字类:
数    字0123456789
十六进制30 31 32 33 34 35 36 37 38 39
十进制48 49 50 51 52 53 54 55 56 57

◇大写字母:
字    母ABCDEFGHIJKLMNOPQRSTUVWXYZ
十六进制41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A
十进制65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

◇小写字母:
小写字母abcdefghijklmnopqrstuvwxyz
十六进制61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A
十进制97 98 99 100101102103104105106107108109110111112113114115116117118119120121122

◇特殊字符:
字    符   !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
十六进制20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 3A 3B 3C 3D 3E 3F 40 5B 5C 5D 5E 5F 60 7B 7C 7D 7E
十进制32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 58 59 60 61 62 63 64 91 92 93 94 95 96123124125126


=>记忆技巧<=

数    字:'0'~'9' -> 30~39
大写字母:'A'~'Z' -> 41~5A
小写字母:'a'~'z' -> 61~7A

特别字符:空格' '-> 20
         连字符'-'-> 2D
备注:连字符在序列号中经常用到,应熟记


■对字符的ASCII值一定要熟悉,深入分析非密码学算法的关键
很多算法是在字符的ASCII值上"做文章"的

#举个典型的例子
算法描述如下:
->取用户名每个字符的ASCII值,累加以后做为序列号
示例代码
004B32BB|>8B4D F0       /mov   ecx, dword ptr          ;ASCII "[email protected]"
004B32BE|.8A0C01      |mov   cl, byte ptr           ;字符串的第i个字符
004B32C1|.81E1 FF000000 |and   ecx, 0FF                        ;取cl
004B32C7|.0FAFC8      |imul    ecx, eax                        ;ECX = ECX * EAX
004B32CA|.03F9          |add   edi, ecx                        ;EDI = EDI + ECX
004B32CC|.40            |inc   eax                           ;EAX++
004B32CD|.4A            |dec   edx                           ;EDX--
004B32CE|.^ 75 EB         \jnz   short 004B32BB                  ;循环length(string)次


■大写小写转化

大写 -> 小写: 加20H
小写 -> 大写: 减20H


■另外,还要对一下知识有一定了解

1.数值的十进制和16进制转化
2.字符直接变数字
3.数字直接变字符
4....



二、汇编指令

1.数据传送(Data transfer)
2.算术运算(Arithmetic)
3.逻辑运算和移位指令(Logic& Shift)
4.串操作(String manipulation)
5.控制转移(Control Transfer)
6.处理器控制(Processor Control)

具体查阅8086系统指令手册


常用指令特别讲解

●指令MOV作用:
1.完成寄存器与寄存器、寄存器与内存之间数据传递
2.完成标志位、密码表等的初始化
其中密码表一般为内存地址
      标志位一般为寄存器,少量是内存地址

●关于堆栈的指令
1.压栈
ESP的值要减少
2.出栈
ESP的值要增加
跟踪堆栈的时候,不同的ESP格式的内存地址指向同一内存地址

建议尽量不要在内存地址是ESP格式的情况下锁定跟踪


●目的地址传送指令LEA
作用主要有两个
1.装载有效地址
指令示例:
      mov   eax, dword ptr           ;
      lea   ebx, dword ptr           ;
经过跟踪发现存放的是某字符串
 那么EBX保存的是该字符串的地址

2.完成算术运算
      lea   eax, dword ptr           ;
      等价于:EAX = EDI + XX;

●算术运算指令
求反:NEC
主要理解其用法:以 0 减之
往往和OR一起结合,完成对字符串长度的取值

乘法MUL
MUL ECX
运算:EAX = EAX * ECX

除法DIV
DIV ECX
运算:EAX = EAX / ECX
   EDX = EAX % ECX

●逻辑运算指令

"与"运算&

"或"运算^

"异或"运算|

以上三条指令常用作算法运算
做注册机时候分析起来比较头痛

"测试"test
操作,进行异或运算
特点:
(1).不保存异或结果
(2).常和条件跳转指令结合在一起

测试一般有三个方向
1.对标志位值进行测试
格式如下
test EAX,EAX
je/jne xxxxxxxx

2.对某位进行测试
test EAX,4
je/jne xxxxxxxx

对倒数第三位进行测试

3.进行奇偶测试
test EAX,80000001
je/jne xxxxxxxx


●移位指令
左移->乘
SHLEAX,2
等价于EAX = EAX * (2^2)

右移->除
SHREAX,3
等价于EAX = EAX / (2^3)




三、定位程序注册代码段

1.试注册,判断保护方式 
序列号(变相的序列号)
所谓变相的序列号就是程序没有输入注册码的地方,但会以文本文件方式通过读取特定的文件
如keyfile等
这类仍可以归类为序列号保护方式


2.查壳/脱壳
有壳脱之
无壳进行下一步


3.定位程序注册代码段

(1)超级字符串定位
A.有错误提示"invalid regcode!"

B.没有错误提示,但有正确提示"Thank you,..."

C.


(2)通过API断点

A.有对话框
   bp MessageBoxA(W)

B.没有对话框
   bp GetWindowTextA(W)
   bp GetDlgItemTextA(W)

C.重启验证
bp RegCreateKeyA(W)
bp RegDeleteKeyA(W)
bp RegQueryValueA(W)
bp RegCloseKey
bp RegOpenKeyA(W)

(3)其它
对付不同语言的工具选择
DEDE:确定delphi的按钮动作对应的代码

VB:SmartCheck
注意:必须是明码比较的VB程序

4.方法的选择
选择流程如下
字符串->对话框->文本->其它

调试还很依赖经验和运气,暂时无思路的程序可以先放放
有时候可以根据软件注册对话框样式猜出大致用什么思路定位,甚至可以猜到它的加密算法


5.AntiDbg
"高级话题",暂时回避{偶暂时对这方面涉猎太少}o(n_n)o


暂时先告一段落,希望能对论坛的兄弟们有所帮助~~


                                 鹭影依凌



菜鸟学算法<二>注册流程篇
声明:
1.以下内容都是个人在学习中的一些心得体会,写给新手的,高手飘过
2.文章难免有疏漏之处,欢迎各位兄弟批评指正
3.本文原创于UnPacKcN,如转载,请保持文章完整性


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


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


一、培养良好的破解习惯

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

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

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

这样的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 983A4700   push    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 D4BE0000   call    <jmp.&MFC42.#540>
0045BDD4   .6A 01         push    1
0045BDD6   .8BCE          mov   ecx, esi
0045BDD8   .C74424 1C 000>mov   dword ptr , 0
0045BDE0   .E8 43C30000   call    <jmp.&MFC42.#6334>
0045BDE5   .E8 6CC20000   call    <jmp.&MFC42.#1168>
0045BDEA   .8B48 04       mov   ecx, dword ptr
0045BDED   .E8 78C30000   call    <jmp.&MFC42.#1669>
;
0045BDF2   .68 E8030000   push    3E8                              ; /Timeout = 1000. ms
0045BDF7   .FF15 3C524700 call    dword ptr [<&KERNEL32.Sleep>]    ; \Sleep
0045BDFD   .E8 54C20000   call    <jmp.&MFC42.#1168>
0045BE02   .8B48 04       mov   ecx, dword ptr
在经过地址0045BDF7的CALL时候,程序停顿了一小会儿
我们看英文注释也大概猜到这段代码的作用了----加入延时验证,防止暴力攻击
;--------------------------------------------------------------------|

0045BE05   .E8 5AC30000   call    <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 83430000   call    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 9C7C4900   push    00497C9C                         ;sorry
0045BE2B   .68 707C4900   push    00497C70                         ;invalid username or registration code      sorry
0045BE30   .8BCE          mov   ecx, esi
0045BE32   .E8 EDC30000   call    <jmp.&MFC42.#4224>               ;
提示注册失败,点击确定按钮,继续F8
;--------------------------------------------------------------------|
0045BE37   .C705 082C4E00>mov   dword ptr , 0            ;
0045BE41   .EB 76         jmp   short 0045BEB9                   ;//跳走
程序跳走

;跳到这里来
0045BEB9   >8BCE          mov   ecx, esi                         ;->来到这
0045BEBB   .E8 74C20000   call    <jmp.&MFC42.#4853>
0045BEC0   .8D4C24 0C   lea   ecx, dword ptr
0045BEC4   .C74424 18 FFF>mov   dword ptr , -1
0045BECC   .E8 8FBD0000   call    <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.可疑处,地址:0045BE7   mov 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 84014A00   mov   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 84014A00   mov   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 6DF9FFFF   call    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 8FFBFFFF   call    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 01000000   mov   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 547C4900   push    00497C54                         ;license to:%s
0045BE4F   .51            push    ecx
0045BE50   .E8 07C20000   call    <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 487C4900   push    00497C48                         ;thank you
0045BE65   .52            push    edx                              ;
提示窗口信息(ASCII "License To:luying10             ")
0045BE66   .E8 B9C30000   call    <jmp.&MFC42.#4224>               ;

提示注册成功
;---------------------------
0045BE6B   .57            push    edi
0045BE6C   .B9 04034A00   mov   ecx, 004A0304                  ;ASCII "萷?"
0045BE71   .E8 3EBE0000   call    <jmp.&MFC42.#858>
0045BE76   .53            push    ebx
0045BE77   .B9 08034A00   mov   ecx, 004A0308                  ;ASCII "乇?"
0045BE7C   .E8 33BE0000   call    <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 A0F24800   push    0048F2A0                         ;username
0045BE93   .68 94F24800   push    0048F294                         ;register
0045BE98   .B9 F4014A00   mov   ecx, 004A01F4
0045BE9D   .E8 5EC7FEFF   call    00448600                         ;
EDI为用户名
0045BEA2   .8B1B          mov   ebx, dword ptr              ;
0045BEA4   .B9 F4014A00   mov   ecx, 004A01F4
0045BEA9   .53            push    ebx                              ;
0045BEAA   .68 84F24800   push    0048F284                         ;registercode
0045BEAF   .68 94F24800   push    0048F294                         ;register
0045BEB4   .E8 47C7FEFF   call    00448600                         ;
EBX为序列号
;---------------------------
0045BEB9   >8BCE          mov   ecx, esi                         ;
0045BEBB   .E8 74C20000   call    <jmp.&MFC42.#4853>
0045BEC0   .8D4C24 0C   lea   ecx, dword ptr
0045BEC4   .C74424 18 FFF>mov   dword ptr , -1
0045BECC   .E8 8FBD0000   call    <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!!

zhjiqi168 发表于 2009-4-6 15:30:07

顶一下。。。。。。。。。。

火凤凰 发表于 2009-4-6 15:52:51

好文章,值得一读再读

kcr 发表于 2009-4-6 16:54:46

学习算法分析。

bobby 发表于 2009-4-6 23:42:12

好复杂。。。。。。。/:002

neinei 发表于 2009-4-7 11:14:16

循序渐进,好啊

冬天的雷雨 发表于 2009-4-7 14:54:42

真的是很好的文章啊!谢谢

buhaixin 发表于 2009-4-8 15:58:11

复杂了点,收下慢慢读。

天天在唱歌 发表于 2009-4-8 16:12:21

不错的文章 谢谢楼主分享 辛苦了

yongbinxp 发表于 2009-4-8 17:39:02

先顶在慢慢看,,学习了
页: [1] 2
查看完整版本: 算法分析的教程