应要求,更新(找Delphi程序关键点),由于篇幅和图文关系,我打包于doc附件,请查看!
--------------------------------

前言:
  写这篇文章出于个人兴趣,要先声明的是本人能力是非常有限!其中参考了许多文章!由于无法提供更多的信息教程,所以文章题目才叫做"给新手引路 之 浓缩汇编基础"。主要讲的是破解中所要的一些汇编知识,方便新手们理解,我将用比较通俗的语言讲述!希望大家别说我"低级"   - -b
  写此文的理由: 
    1、出于兴趣
    2、助于自己学习巩固,利于新手
  注:
    本文并不教如何编写汇编程序,只想引新手们进Crack大门,一些迟迟无法Crack成功的人也就是这个原因了!
    最好是能抛砖引玉了,勾起新手们学习汇编的兴趣!
    重要一点是:不懂汇编是完全没办法搞Crack的,希望大家带着这点来学习!


一、汇编基础
-----------------------------------------------------------------------------------------------
1.0 关于汇编语言
  汇编语言是创造出来代替原始的只能由处理器理解的二进制代码的,也就是在OD中常见的机器码!用机器码来写程序,可以想象其难度吧,所以汇编语言就出现了,汇编代码是直接描述处理器可以执行的代码,也就是在OD中最常见的反汇编代码了!(当然,有点不一样),而汇编语言是和cpu相关的,和机器语言是一一对应的!

2.0 关于cpu
  CPU的任务就是执行存放在存储器里的指令序列。为此,除要完成算术逻辑操作外,还需要担负CPU和存储器以及I/O之间的数据传送任务。早期的CPU芯片只包括运算器和控制器两大部分。到了近几年,为了使存储器速度能更好地与运算器的速度相匹配,又在芯片中引入了高速缓冲存储器(知道为什么P4比P4赛扬贵那么多了吧?)。
  看主要的部件:
  1.算术逻辑部件ALU(arithmetic logic unit)用来进行算术和逻辑运算。这部分与我们的关系不太大,我们没必要管它。 
  2.控制逻辑。同样与我们的关系不大。 
  3.工作寄存器。意识了吧,寄存器呀!喂,,寄存器呀!~

3.0 寄存器
  所要了解的是8个32位的寄存器,分别是eax,ebx,ecx,edx,esp,ebp,edi,esi
  eax-edx这四个是通用寄存器,虽然各个都有各自的用途,不过你可以用它们来做任何事!是32位的,自然有低位和高位,我们又可以通过ax,bx,cx,dx来访问其低十六位,但高十六位是无法访问的!比如eax=12345678h,那么低十六位ax=5678h!而十六位的自然也有低位和高位,不过高八位是可以访问的,如ax可以分为ah和al,看字面就知道,ah(high)高八位,al就为低八位了!前面的例子,ax=5678h,那么ah=56h,al=78h!这四个寄存器主要是用来暂放计算结果或什么什么的!
  esp-esi这四个主要是寻址时用来存放偏移或指针,所以,也就称为指针寄存器或变址寄存器了~如在OD中看到的[eax],其实eax中存放的是一个内存地址,而实际要访问的是那个内存地址里的内容!
  esp(堆栈指针寄存器):
     很重要的一个概论,堆栈有着先进后出的特点,就好像有一个圆柱形的筒子,该直径刚好是一个乒乓球的直径,所以最先放进去的球当然会最后出来.而esp呢,永远是指着最顶的那个球的,也就是永远都指向栈顶!在od中也很常见了,比如push和pop就是对栈的操作,push把一个数据压入栈中,也就是把一个球放进去,再去调用push时就再放进一个,而esp则指向第二个放进去的那个球了!使用pop呢就从栈中弹出一个数据,前面说了,堆栈有着先进后出的特点,所以用pop呢就从最后放进去的那个球先出了(除非你破坏筒子(破坏堆栈?那是不可能的,程序马上死给你看))!而esp还是指向栈顶!
  取个代码例子:
  (1) mov ecx, 100<---------100传入ecx
  (2) mov eax, 200<---------200传入eax
  (3) push eax <------------eax先进了
  (4) push ecx<-------------再来是ecx
  (5) pop ebx<--------------从栈顶取出一个,也是最后进去的那一个,结果存到ebx
  (6) pop ecx<--------------从栈顶取出一个,也就是刚刚先进去的那个了,结果存到ecx
最后ebx=100,ecx=200
  到了win32的平台下,api大家都知道了吧!api的参数都是靠堆栈来传递的,比如说一个FindWindow,在C里我这样调用
->::FindWindow("a",0)->而反汇编之后在系统底层反汇编代码就象这个样子:
  push xxxxxxxx->xxxxxxxx为"a"的内存地址
  push yyyyyyyy
  call zzzzzzzz->调用FindWindow
  而在call里面先使用pop弹出先前压入栈的参数再使用

  ebp(基址指针寄存器):
  它称为基址指针寄存器,它们都可以与堆栈段寄存器SS(堆栈段)联用来确定堆栈中的某一存储单元的地址,ESP用来指示段顶的偏移地址,而EBP可作为堆栈区中的一个基地址以便访问堆栈中的信息。

  ESI(源变址寄存器)和EDI(目的变址寄存器)一般与数据段寄存器DS联用,用来确定数据段中某一存储单元的地址。这两个变址寄存器有自动增量和自动减量的功能,可以很方便地用于变址。

  还有两个专用寄存器,分别是eip和flags
  flags:
     这个是标志寄存器了,存放条件标志码、控制标志和系统标志的寄存器!在od中也见很多了,比如zf(零标志),用cmp比较时,把两个操作数相减,为0就置zf为1,否则zf为0。而jnz就是看zf是否为0,为0就跳!这样说起来似乎更乱了,建议大家去记那些大于就跳,小于就跳的,比较简单(jnz就是不相等就跳)``哦呵呵!!至于其它标志,这里不再阐述了,可以去参看汇编速查!
     cmp eax,ebx<-比较eax和ebx,两个相减,为0的话zf就为一,否则zf为0
     jnz xxxxxxx<-判断zf是否为0,为0就跳到xxxxxxx处,也就是所谓的不相等就跳

  eip(指令指针寄存器):
     这个很好理解,根据od来说,载入一个程序后,比如代码像这样:
     0043C412 >/$  55            push    ebp         <-载入后停在这,看寄存器窗口eip这时为43c412
     0043C413  |.  8BEC          mov     ebp, esp    <-f8运行一步之后,eip为43c413
     0043C415  |.  6A FF         push    -1          <-eip为43c415
     0043C417  |.  68 C8B64800   push    0048B6C8    <-eip为43c417
     有人会说"原来eip是指示当前执行到代码处的地址的!"  ,这不对!!因为f8运行还没通过那条代码,所以还不算已经执行了,没错,eip指向的就是下一条将要执行的指令的指针!

  段寄存器:
    cs代码段,ds数据段,ss堆栈段,es附加段
    在Win32编程中段的概念已经不重要了!而在Crack时你总不会是在调是dos时代的程序吧!-!

4.0 常用汇编指令
  mov ax,cx  <-很常用了,把cx的值送入ax中,cx值保持不变
  cmp eax,ecx<-很常见了吧,比较eax和ecx,置标志位!方法前面说过了
  xor eax,eax<-看这个,eax与自己异或,是清零的操作!
  lea eax,str<-并不传送数据,只传送该数据的地址,将str字符串的地址传到eax
  push eax   <-进栈操作,前面说过了,eax进栈
  pop  ebx   <-出栈操作,前面也说了,弹出位于栈顶的数据存入ebx
  ADD 加法指令 格式:ADD DST,SRC 执行的操作:(DST)<-(SRC)+(DST) 
  SUB 减法指令 格式:SUB DST,SRC 执行的操作:(DST)<-(DST)-(SRC) 
  MUL 无符号乘法指令 格式: MUL SRC  执行的操作:字节操作(AX)<-(AL)*(SRC);字操作(DX,AX)<-(AX)*(SRC);双字操作:(EDX,EAX)<-       (EAX)*(SRC) 
  DIV 无符号除法指令 格式:DIV SRC  执行的操作:字节操作:16们被除数在AX中,8位除数为源操作数,结果的8位商在AL中,8位余数在AH中       。表示为: 
       (AL)<-(AX)/(SRC)的商,(AH)<-(AX)/(SRC)的余数。字操作:32位被除数在DX,AX中。其中DX为高位字,16位除数为源操作数,结果的16       位商  在AX中,16位余数在DX中。表示为:(AX)<-(DX,AX)/(SRC)的商,(DX)<-(DX,AX)/(SRC)的余数。
  nop        <-  无操作,去掉指令用的吧!去掉一个跳转,让程序直接往下走,就到注册成功处啦(扯远了````)
  call       <- 调用子程序或函数用的

  关于跳转指令,可以查看汇编速查手册,别强迫自己把所有的都一下记住,浪费精力,不懂时再查一下,久了就记住了!
   
5.0 高级语言程序的汇编浅解析
  汇编语言要和硬件直接打交道,写病毒是方便点啦!!而在高级语言中,如C中我们要面向的是问题的解决,对于硬件资源操作,编译器搞定了!在这里稍微讲一下高级语言中与反汇编代码相应的一些地方:
  1、定义变量
    int a;
    一个变量其实是存放在一个内存地址里,如果对a进行赋值"a=10",在反汇编中就有可能表现为:
    mov word ptr[007e58c2],A
    像这个样子,而a所对应的内存地址就是0x007e58c2了,当然是乱写的一个地址而已,系统怎样分配?(天知道...)

 2、比如一个数组
    char str[]="hello";
    占用了6个字节,最后一个是以0结尾的空字节,数组名可以当做数组的指针!str[0]='h',str[0]相应一个变量地址,比如为[0040e123],那么[0040e124]就为'e',[0040e125]就为'l'....了`

 3、指针
   char *p;
   指针也是一个变量,所以它也对应一个内存地址!但访问时应该是访问其指向的内存地址的内容,而不是这个指针变量的内容,其内容只是一个地址而已!假如该指针变量地址为007e1000,那么语句p=a,这句在高级语言里是让指针p指向a这个内存单元!p里的内容是a的地址,*p实际上是a的内容了!而反汇编有可能表现成这样:
  mov [007e1000],007e2000<-假如007e2000为变量a的地址,那么就是把a的地址传到007e1000这个内容里了!

4、函数调用
  sub(a,b);
  假如sub是自定义的一个减法函数,作用为参数一减去参数二,上面语句为在C中调中时传递参数!前面有说过了,Win32平台下函数调用的参数是通过堆栈来传递的,那么反汇编就是:
  (假如a=2,b=1)
  mov eax,2
  mov ebx,1
  push eax
  push ebx
  call 取地址(sub)
  .......


二、Crack断点初步
-----------------------------------------------------------------------------------------------
  啊``我不是高手`也不好多说什么!一些新手们总是说断下不来`说到断点,在破解是是至关重要的,一个正确的断点可以使你大大减少工作量!这里就要涉及到一些方法了,其实论坛上有许多好的文章,多看一下也能总结出断点技巧了!在这里我只是讲讲本人的一些体会与方法~~

1.0  怎么样才能断在我们认为正确的地方?
  一些新手朋友们刚开始并不了解断点时会盲目的下断,搞得自己都分不清啥跟啥了!断点的话,就是在程序运行时让它中停止在一个位置,而这个位置就是断点的重要性了!一般在破解软件时这个位置都会是算法的代码!如果你断在算法代码之外的地方,可以想象了,一点用也没有!所以现在我们来讨论一下要怎么样才能断在在我们看来是正确的地方(算法部分)!

  断点的话,其实一个程序,比如说CrackMe,它一般是一个编辑框控件带一两个按钮!它本身就给我们提供了许多有用的信息了~~

  比如常见的字符串参考,你注册成功与不成功我想作者都会给你一个交代,比如"You did it"!!在OD中查找字符串我想大家都会!找到之后向上看,在OD中有个很方便的功能就是可以看到跳转的动向,点一下那个跳转就可以看到那个跳转跳到哪了!这样我们很容易可以看到是哪个跳转跳到或者说是跳过一个注册成功提示!这个跳转就是关键跳转了!而一个跳转的实现条件是在跳转前面实现的,比如一个jnz,不相等就跳,要置zf(0标志)的话就cmp(比较两个操作数),而cmp的两个操数,比如cmp eax,ebx! eax假设是你输入的假注册码,那么ebx是哪来的!所以根据这样的推断,你可以很容易的找到算法部分了!

  你注册错误没有提示,成功我想应该有提示了!这要根据自己去判断成功信息是什么,在OD字符串参考中找!
 
  如果找不到有用的字符串,呵呵``看看你点注册之后会出现什么,一个提示错误的对话框?,那么就是MessageBox这个函数,ctrl+n打开函数参考,找到Messagebox,在所有MessageBox上下断,点击注册后可能被Od断下了,不过这可不是关键的地方!我们是在一个消息框将要显示的时候断下的,而程序已经确定下来你注册是不成功的了,正想要提示你就被你断下了!呵呵``很明显,可以根据上面的推断,找出关键部分了!啥?OD提示说没有参考?呵``这可能是提示时用的消息框不是MessageBox这个函数吧,是作者自已画的也不一写,在系统底层要显示一个窗口都会调用ShowWindow,还是没有参考?~也要被破解的程序有提示窗口下能下这两个断哦~~程序可能不用消息框的形式提示我们,它可能是在窗口的什么显示一些文字来提示,而显示文字我们可以"SetWindowText"!断看看~~~~也不行~~~ - -b ~~唉```哇靠,NND!!这个程序总该有地方让我们输入注册码吧,就是那个编辑框控件呀,总得先取得控件的内容才开始运算吧!!我们就GetWindowText或者对话框的GetDlgItemText!再试试```失败!!!!->  - -!!  怎么搞的~~总该有一个按钮让我们启动程序的算法部分吧,那就断在那个按钮按下时的那个位置,就用消息断点!!哦呵呵呵(自谓天才$.$)``OD载入后运行程序,输入好假信息,OD中查看窗口,找到按钮文本,在上面消息断点....(具体方法大家可以去看版主的献给初学者那个OD入门书,,很棒)。唉,本来想放弃了`,突然想到moodsky说的OD万能断点(98以后的平台,具体方法参看《看雪论坛精华8》),再试试,(被破解的程序说:"你不行的啦!"),大概吧!好,我们用peid看看他是什么程序,如果是delphi就用dede找关键,是vb就用VBExplorer!
  peid大哥告诉我们"FSG 1.33 -> dulek/xt",原..原来有壳!
  (嘿嘿,不是我的错,OD这个家伙载入程序居然没报加壳(fsg的壳``OD不报的))
  惊讶1秒后,吐血(必杀-番茄星)倒地!
  <擦擦身上的番茄酱,继读讲..>
  ...好好,用dede,VBExplorer找delphi和vb程序的关键点这里不阐述了,有需要的朋友可以跟贴说一声!


三、浓缩汇编的巩固与Crack接触
------------------------------------------------------------------------------------------------
1.0  keyfile保护的简介
  给大家的crack初接触就找个keyfile的,实在抱歉!因为讲到的问题似乎多了点,所以找了个综合性强点代码量又少点的程序!
  keyfile呢,看名字我想大家都知道,钥匙文件嘛,这么讲可以低级了点!不过就是这么个意思。写出来的程序用keyfile保护的话,程序就会去检测这个keyfile,而一般的注册流程就是通过一些方式给软件作者汇钱,然后得到的不是一个注册号,而是一个注册文件!也就是你必须要拥有这个文件,且文件内容正确的条件下就行了!
  好,我想大家都了解keyfile是怎样的一个东西了,下面讲讲拦截程序读取这个key!


2.0  crack之前要了解的一些东东
  如果是懂win32编程的,这节就跳过吧!主要是写这个Crack用到的一些函数,当然这是必须要了解的!
  keyfile呢是程序靠读取文件来进行注册验证的!
  所以就用到函数了
  readfile->读取文件内容
  createfile->打开现有的或创建一个新文件
  openfile->找开文件
  要读取当然得在内存中先打开了,所以这三个函数我想就够了吧,现在我们重点看看createfile这个函数,因为要先打开嘛,f8一直走的话当然程序到时候也会读取文件了!哦呵呵``
  在msdn中CreateFile函数的定义是这样的:

  HANDLE CreateFile(
  LPCTSTR lpFileName,                         // file name(要打开的文件名)
  DWORD dwDesiredAccess,                      // access mode(打开的方式,比如只读打开,只写打开,或可读也可写)
  DWORD dwShareMode,                          // share mode(共享方式,不必管了,我不是在讲编程- -b)
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD(安全性,没啥路用)
  DWORD dwCreationDisposition,                // how to create(要怎么样打开,比如打开现有的,或创建新的)
  DWORD dwFlagsAndAttributes,                 // file attributes(文件的属性,与crack无啥关)
  HANDLE hTemplateFile                        // handle to template file(模板文件句柄,不管他)
);

  我们要破解keyfile保护,所以必须要知道他读的是哪个文件!也就是文件名了。嘿嘿``我们关注一下CreateFile函数的第一个参数,就是文件名! 可以照这样一个思路来搞:只要有了这个文件的文件名,我们就可以做一个假的文件,文件名当然是真的,内容当然是注册不成功的!而程序是死的,但只要找到一个这样的文件,就会去读取,检测,从而算法也暴露出来了!
  如果知道了文件名,在程序读取的时候断下来,嘿嘿,这根普通的序列号算法有啥区别!

3.0  Start..
  od载入,ctrl+n打开本程序的函数表,找到CreateFile这个函数,右键,在每个函数上下断点,f9运行就被断下了,证明程序用了这个函数去打开文件!
  004010A8  |.  6A 00         push    0                                ; /hTemplateFile = NULL
  004010AA  |.  68 80000000   push    80                               ; |Attributes = NORMAL
  004010AF  |.  6A 03         push    3                                ; |Mode = OPEN_EXISTING
  004010B1  |.  6A 00         push    0                                ; |pSecurity = NULL
  004010B3  |.  6A 01         push    1                                ; |ShareMode = FILE_SHARE_READ
  004010B5  |.  68 00000080   push    80000000                         ; |Access = GENERIC_READ
  004010BA  |.  68 3B304000   push    0040303B                         ; |FileName = "test.txt"      <-文件名
  004010BF  |.  E8 9C000000   call    <jmp.&KERNEL32.CreateFileA>      ; \CreateFileA                <-断在这里
  004010C4  |.  83F8 FF       cmp     eax, -1
  004010C7  \.  C3            retn

 这样就得到文件名了,Createfile的第一个参数,哦呵呵~~文件名就是"test.txt"

 接下来做个假的,新建一个文本文件,文件名当然是"test.txt"了!写入"12345678"
 ctrl+f2重新载入,再运行就又断在原地了!f8的时候会跳到系统底层了,因为我们在设断的时候设了两个,所以在那里f2取消断点后alt+f9返回到调用的下一条语句就行了!然后eax会和-1比较,eax是这个函数的返回值!我们看一下在msdn中这个函数返回值的说明:
 "If the function succeeds, the return value is an open handle to the specified file"
 "如果函数成功,返回值是所打开的文件的句柄"
 而eax与-1比较,-1显然不是句柄了,所以这里就是看看CreateFile函数是否打开成功,cmp->这个指令大家没忘吧,两个操作数相减,为0的话就置zf(0标志)为1,否则置为0!
 好,我们f8通过那个retn语句会返回到:
00401018  |. /74 32         je      short 0040104C                   <-返回到这里,就要看刚才置的zf是0还是1了,为1就跳,也就是两个操作数相等就跳,也就是打开文件失败就跳!
0040101A  |. |A3 44304000   mov     dword ptr [403044], eax          <-刚才说过打开功的话eax是句柄了,别现忘了,以后不说了!这里把句柄传到内存地址403044处
0040101F  |. |8005 50304000>add     byte ptr [403050], 1             <-403050处的内容加1,等下就会知道403050里是什么东西了
00401026  |. |E8 9D000000   call    004010C8                         <-这个要跟进去,大家又会问,为啥这个要跟进呀!因为它后面跟了一个跳转,啥?那反汇编所有的代码里也有很多这样类型的call啊,为啥要跟进这个?嘿嘿``因为我们现在在分析的这堆代码是关键处!这样说大家没疑问了吧,通常破解书上都会说一个call后面跟一个跳转就是关键的之类的话,把新手们搞糊涂了,因为漏了这么一句"这种类型的call要出现在程序算法关键处时对我们才是有用的!",当然刚开始我也不知道这个call是不是有用的!所以要跟进去看一下嘛,没用的话现出来不就好了!如果你这么懒,回家play games去嘛!这个call呢,先把它叫做call1!


   call1:
   跟进call1之后就到这里:
   004010C8  /$  6A 00         push    0                                ; /pFileSizeHigh = NULL
   004010CA  |.  FF35 44304000 push    dword ptr [403044]               ; |hFile = FFFFFFFF     <-Createfile函数返回的句柄
   004010D0  |.  E8 97000000   call    <jmp.&KERNEL32.GetFileSize>      ; \GetFileSize          <-看名字就知道是取文件大小
   004010D5  |.  83F8 0A       cmp     eax, 0A                                                  <-函数返回值与0a比较
   004010D8  \.  C3            retn
   这个函数的返回值是文件的大小,下面和0a比较!并置0标志



0040102B  |. |75 1F         jnz     short 0040104C                 <-又检测一下0标志,0就跳,也就是文件大小不等于0a(十个字节)就跳,而跳,是跳到出错的地方,所以就可以说是"文件大小不等于十字节的话就出错",现在可以理解破文的写法了吧!
0040102D  |. |A3 72304000   mov     dword ptr [403072], eax        <-别忘了eax是返回值,也是文件大小,文件大小传到403072里
00401032  |. |E8 A2000000   call    004010D9                     <-又是一个call,大家说说看,这个call应不应该跟进?当然(call2)

   call2跟进:
   004010D9  /$  6A 00         push    0                                ; /pOverlapped = NULL
   004010DB  |.  68 88304000   push    00403088                         ; |pBytesRead = Cme.00403088
   004010E0  |.  6A 04         push    4                                ; |BytesToRead = 4
   004010E2  |.  68 7E304000   push    0040307E                         ; |Buffer = Cme.0040307E
   004010E7  |.  FF35 44304000 push    dword ptr [403044]               ; |hFile = FFFFFFFF
   004010ED  |.  E8 86000000   call    <jmp.&KERNEL32.ReadFile>         ; \ReadFile          <-妈呀,没跟进就吃亏哇!
   004010F2  |.  803D 7E304000>cmp     byte ptr [40307E], 2D            <-上面已经读入文件了,这里是文件是第一个字节和2d比较
   004010F9  |.  74 04         je      short 004010FF                   <-文件的第一个字符不等于2d就要出错啦

又有人要问,你前面说的函数返回值不都在eax里的么?怎么现在你说40307e里是返回值!!呵呵``你想想,一个寄存器可以放得下一串字符么?顶多是一个指向字符串的指针啦@!哦呵呵``这么讲大家可能还有疑惑!
嗯``在调试程序时候别当OD是透明的,od那五个窗口都是很有用的,这里涉及到信息窗口,别告诉我你不知道哪个是信息窗口!我马上昏死过去1秒给你看!而在该窗口是总是会自动指出当前到达的该条语句中内存地址里的内容,就比如说我们f8停在那个比较上:
cmp     byte ptr [40307E], 2D
而信息窗口是就会提示我们40307e中的内容是什么?我们前面输入的是"12345678",所以这时信息窗口的内容就是:
ds:[40307e]=31('1')
先指出所在的段,再指出内存地址,再给出内存地址里的内容,连asc字符都给出了,哦,,OD太伟大了!
顺便提一下,40307f里是'2',403080里是'3'....这没问题吧,大家都了解吧,要不偶"浓缩汇编基础"就白写了!

还有要知道的就是,一个字符占一个字节,所以到分析到这里就可以得出文件内容要十个字符,而第一个就是0x2d,对应的asc是'-'负号

好,现在我们是错误的了,看看它会跳到哪里,在那些代码上左键一下就可以顺着红线找到你的你老婆,,啊,不对不对,又不是牵红线,就可以找到跳转的位置了!
004010FF  |>  803D 7F304000>cmp     byte ptr [40307F], 30<-会跳到这句
但是它没跳!可以没跳就错了哇,不管他了,让程序停在哪里,我们变成静态分析程序了!

004010FB  |.  33C0          xor     eax, eax               <-没跳,eax清零
004010FD  |.  EB 13         jmp     short 00401112         <-一定跳的跳转


00401112  \>  C3            retn<-上面会跳到这里,也就跳说不等于'-'的话eax就是0,还会跳到这里返回


004010FF  |>  803D 7F304000>cmp     byte ptr [40307F], 30  <-这里,我们现在是到达不了这里的,因为我们的第一个字符不是'-',40307f这个大家都知道了吧,是第二个字符,在这里又和30比较了,信息窗口大家可以看一下
00401106  |.  74 04         je      short 0040110C         <-检测一下0标志,也就是上面相等就跳
00401108  |.  33C0          xor     eax, eax               <-不相等eax就清零
0040110A  |.  EB 06         jmp     short 00401112         <-又跳到第二个返回处
0040110C  |>  B8 01000000   mov     eax, 1                 <-看这句,把1赋给eax,上面我们可知道,如果前两个字符不为"-0"的话就不会执行到这条语也,也就是eax是0而不是1,程序返回的话也是最后那句的返回而不是下面那句的返回!
00401111  |.  C3            retn
00401112  \>  C3            retn                          

现在我们还不能确定eax是1好还是是0好?不过想也知道程序在返回后一定会有这样的一个判断

00401037  |. |83F8 01       cmp     eax, 1                  <-想这样,真有一个判断,啊我真是天才(昏倒读者一堆),eax是刚才的..这里和1`比较
0040103A  |. |74 09         je      short 00401045          <-相等就跳

现在我们还是不能确定eax是1好还是是0好?因为我们不知道je会跳到成功处还是失败处,我们现顺着红线找一下(不是女友哦..^-^)

0040103C  |. |8005 50304000>add     byte ptr [403050], 1
00401043  |. |EB 07         jmp     short 0040104C
00401045  |> |E8 C9000000   call    00401113                <-eax为1的话会跳到这里,这个call是干嘛滴呢?想也知道我们要跟


   call3跟进:
   00401113  /$  8D3D 00304000 lea     edi, dword ptr [403000]          <-403000里的指针传到edi,为啥不是内容传到edi,因为lea这个指令,我汇编基础中有说的!况且前面也说了,寄存器怎么可以放得下一个字符串哦,呀 ,不知不觉透露出403000处是字符串了,呵呵,大家可以在命令行上写"d  403000",在内存窗口中就可以看到里面的内容了,(这谁不知道呀,当..一拳过来)
   00401119  |.  E8 1F000000   call    0040113D                         <-上面传给edi有啥有咧,这个call我们也跟,算了,你们自己跟吧,里面是有关字符串解密算法了,一点都不难,当作是练习吧!如果程序可以执行到这里,呵呵``字符串就解密成成功字符串啦!
   0040111E  |.  8D3D 0F304000 lea     edi, dword ptr [40300F]
   00401124  |.  E8 14000000   call    0040113D
   00401129  |.  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL
   0040112B  |.  68 00304000   push    00403000                         ; |Title = "Xnt.L`cd.Hs-.."
                     <-成功对话框标题的密文
   00401130  |.  68 0F304000   push    0040300F                         ; |Text = "Fqd`s.Vnqj-..Mns.sg`s.g`qc.h.ftdrr-."
                     <-成功对话框内容的密文
   00401135  |.  6A 00         push    0                                ; |hOwner = NULL
   00401137  |.  E8 1E000000   call    <jmp.&USER32.MessageBoxA>        ; \MessageBoxA
   0040113C  \.  C3            retn

    成功的字符串是什么呢,你自己找一下吧!

0040104A  |. |EB 4A         jmp     short 00401096
0040104C  |> \8D3D 34304000 lea     edi, dword ptr [403034]
00401052  |.  E8 E6000000   call    0040113D
00401057  |.  8D3D 51304000 lea     edi, dword ptr [403051]
0040105D  |.  E8 DB000000   call    0040113D
00401062  |.  6A 00         push    0
00401064  |.  A0 50304000   mov     al, byte ptr [403050]
00401069  |.  66:0FB6C0     movzx   ax, al
0040106D  |.  66:50         push    ax
0040106F  |.  68 51304000   push    00403051                         ; /Format = "Dqqnq.hm.sdrs.$kt"
00401074  |.  68 63304000   push    00403063                         ; |s = Cme.00403063
00401079  |.  E8 D6000000   call    <jmp.&USER32.wsprintfA>          ; \wsprintfA
0040107E  |.  83C4 0C       add     esp, 0C
00401081  |.  6A 10         push    10                               ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
00401083  |.  68 34304000   push    00403034                         ; |Title = "DQQNQ "
00401088  |.  68 63304000   push    00403063                         ; |Text = ""
0040108D  |.  6A 00         push    0                                ; |hOwner = NULL
0040108F  |.  E8 C6000000   call    <jmp.&USER32.MessageBoxA>        ; \MessageBoxA
00401094  |.  EB 00         jmp     short 00401096
00401096  |>  6A 00         push    0                                ; /ExitCode = 0
00401098  \.  E8 C9000000   call    <jmp.&KERNEL32.ExitProcess>      ; \ExitProcess

好了`好了``太累了!后面的代码不说`1`不说``不说``

唉!到最后,你也能看出程序执行的流程了不是么?呵呵``Crack也不难嘛,至少这个是这样!^-^


4.0  end..
   不知道经过这篇文章之后大家对从反汇编代码中看程序是否有了一定的了解了呢!当然,这只是很小的一个程序!
   
   还有就是建议一下新手:
      最好找Crack来练习,在论坛上总是会看到新手问这样的问题"什么什么软件怎么破?",我说呀,一个软件作者辛苦完成的软件会那么容易那
端了才怪!还有哇"这个壳怎么脱",,我想壳的问题不应该在新手区中出现吧,对于遇到的壳,可以尝试去找找脱壳机,没有的话我想你也可以放弃了!不然就算你进行抓破头皮式调试也是没用!


 
  好了,最后感谢大家能看完这篇了了草草的东西,我也回顾了不少知识!在本人能力范围内的事不知道对新手们有没有帮助,还有一点要提醒各位新手,如果真想学好Crack学通Crack这门知识的话,不完全了解汇编是不行的!所以在看了我这篇之后如果能激起你学汇编的信心是最好的!要不等到Crack遇到什么问题时才再想回过头学汇编,那很难!~

  注:本文有讲错的地方请批评指正(偶大菜一个),另外我参考了很多的文章,因为比较懒,所以``有的都是直接复制过来了~不过也根据我自已的理解努力为新手们讲解了!!
 


嘿嘿``全文完了! 
                                                                                           -=>大菜一号<=-
                                                                                     全完文 于07.06.17  8:56
                                                                                     整理   于07.06.15  10:24