zoukankan      html  css  js  c++  java
  • understand 破jie,Google Analytics的原理

     关键call 在此,剩下的自己搞定。

    参考:https://www.52pojie.cn/thread-1451831-1-1.html

    话不多说先上代码:
    <ignore_js_op>


    接下来就是话要多说的环节了



    堆栈是一块儿连续的内存区域,通常情况下,由栈顶向栈底压入内容
    可以将堆栈抽象的理解为一个放在地上的袋子
    在没有东西的时候,袋子是瘪的,袋子的上面与下面是紧挨着的
    但是每次压栈都会向袋子里放入一些东西,
    袋子里面装了东西就鼓起来了,袋子变高了,也就是栈顶也就向上走了,但是栈底还是在原来的地方
    从袋子里面把东西拿出来,袋子也就变瘪了,栈顶也就向下走了




    push指令就是向袋子(堆栈)里面放东西,放的东西就是push后面的内容,
    也就是说每次push时,会向堆栈压入一个双字,然后栈顶会自动-4

    为什么是-4而不是+4呢,因为堆栈是高地址在下,低地址在上,通俗来讲堆栈的内存地址编号数字大的下面,数字小的在上面
    至于为什么是4呢,因为"16/4=4"


    pop指令刚好与push相反,是从袋子(堆栈)里面拿东西,每次加四


    Call指令是用来调用程序段中的子程序的,使用Call的时候回自动记录下当前指令的下一条指令的地址,并push到堆栈中
    retn指令学过编程的可以将其理解为return指令,不过这个retn可不是会返回什么结果给其他程序调用,在汇编中,retn命令的作用是返回到调用Call指令时所记录的call指令的下一条指令的位置.


    mov 指令和 add指令百度就有很多成熟的讲解,我就不再多做赘述了.


    概念讲的差不多了,下面来解析代码:

    图中的代码,主要目的是带参数call地址为00598310的"子函数"
    提供参数的方法简单粗暴,就是把参数压入堆栈中.
    两次push(传参)以后,进入子函数程序段,第一步就push了ebp,
    为什么要push ebp呢


    注意了,这里就又蹦出来两个概念,我们可以很明显的从代码中看到这个子程序的开头和结尾是相互对应的
    push ebp以后把esp的值给了ebp,在子程序retn之前呢又pop了ebp,其实这个pop ebp就是为了将原本的ebp值还回去

    这么做的原因就是因为堆栈自身只是一块内存空间,它的结构过于简单,简单到需要人去手动将堆栈划分成某一个程序或者某一个子函数的堆栈空间
    esp寄存器存放的的是堆栈的栈顶地址,ebp则是存放的栈底地址,这就印证了为什么ebp叫基址指针寄存器,你知道了最下面的地址是什么,想要找上面的地址,只要用基址向上加就可以了


    言归正传,将栈顶的给栈底,就形成了如图所示的情况
    <ignore_js_op>

    这样,原有的下方堆栈空间就一定程度上得到了保护,整个堆栈也就大致的划分成程序堆栈段,以及被esp赋值后的ebp所形成的新的"函数堆栈段"(我自己瞎起的名字)

    现在命令继续向下执行,完成这个call的功能,将两个"参数"相加,最后得出的结果赋给eax
    首先将第一个参数放到eax中,这里的dword实际上是double word也就是双字的缩写

    prt则是临时的类型转换符  学过C语言的,你可以把他同以下代码进行比较来相互理解

    [C] 纯文本查看 复制代码
    1
    2
    3
    4
    5
    int a;
    char b='A';
    a=int(b);
    这里的a就是eax而b就是那个需要被ptr的内容,int()则充当了ptr
    这样是不是好理解多了


    ss代表的是堆栈段 (汇编基础知识,段寄存器都有啥)
    []是间接寻址的意思(这里我记忆模糊了,好像是叫间接寻址)
    []中的内容就是基址加上偏移地址(ebp是基址0xc是偏移地址),如果你比较细心的话,就会发现mov和add两条指令一个是0xc一个是0x8也就是说这两个地址相差4  实际上堆栈中每两个地址之间都是相差4的,在进入call之前是连续push两次,所以两个"参数"在堆栈中也是挨着存放的/

    这两行主要功能代码结束后,就是前面提到的相互对应的地方了


    在完成子程序的功能以后需要将ebp复原,也就是把分割出来的函数堆栈段还给程序堆栈段,我自我感觉这个堆栈有点儿套娃,刚开机什么都不干的时候,就是系统堆栈段,运行程序后会划分出来程序的堆栈段,然后程序运行子函数又划分函数堆栈段


    在将函数的堆栈段还回去以后,函数就要执行最后一条指令retn到之前call指令的下一条指令
    而为什么retn后面要加上一个0x8呢,这里本文的重点就来了,这就是为了堆栈平衡

    在一开始要"传参"给call的时候,我们的做法是将参数push进堆栈,现在需要传参的call已经完成了它的功能,这两个参数已经没有用了,如果此时直接retn的话,这两个值还会留存于堆栈中,白白占用着空间


    我们知道,堆栈的空间并不是无限大的,这只是一个需要传两个参数的call,如果每个call都这样的话,程序运行时的可用空间就会越来越小直到没有,最后程序崩溃.(整不好可能不止程序崩溃)


    这时候在retn后面加上0x8的作用就展现出来了,加上以后,代表栈顶的esp就会自动加上0x8,这样就将之前两次push留下的空间给空了出来.而在retn后面加上要"释放的堆栈空间的大小"的这种方法,就叫做堆栈平衡里的内平衡.


    如果不想在retn后面加上要释放的空间的话,也可以在call后面的指令手动给esp加4而这种在子程序外去平衡堆栈的方法也叫做外平衡

    有的人用od试验后可能发现,就算堆栈平衡后esp已经调正确了,但是之前push的两个值却还在堆栈里面
    其实这里已经没有什么太大的必要担心了,再下一次push或者call的时候,会自动将垃圾值覆盖掉

    写到这里,正文内容已经差不多了,不得不吐槽一下汇编确实是挺抽象的
    还是那句话,开贴只是为了记录下所学内容,大佬笑笑就好,
    但是万一有人恰好在学这里,然后恰好看到这里的一些内容或者概念想明白了呢
    当然如果我的上述内容有问题还请指正

    EAX寄存器(关键跳,关键CALL)

    OD动态调试过程中,寄存器窗口是需要时常关注的!

    EAX寄存器:累加器,在乘法和除法指令中被自动调用;在win32中,一般用在函数的返回值中。

    CALL:过程调用指令 格式: CALL OPRD 功能: 过程调用指令(说明: 1. 其中OPRD为过程的目的地址. 2. 过程调用可分为段内调用和段间调用两种.寻址方式也可以分为直接寻址和间接寻址两种. 3. 本指令不影响标志位.)

    32位 16位 高8位 低8位
    EAX AX AH AL
    今天用到得示例程序还是《Shark恒零基础百集VIP破解教程》中的示例程序:

    先查壳,发现无壳,下面打开软件,发现要注册,输入假码,发现提示关键字“注册”:

    下面载入OD,搜索字符串,转到相应地址 :

    向上溯源,发现关键跳(刚好跳过这一段代码得跳转),与je跳转相近的call就可能是关键call,先下断:

    关键跳:程序中某一个或某几个跳转刚好就跳过了我们期望他运行的代码段;关键call:关键跳是否实现取决于关键call返回的值,一个关键call可能对应很多关键跳,但一个关键跳只对应一个关键call。

    下面运行程序,至call处单步调试,观察寄存器窗口:

    发现call语句之后EAX寄存器al是00,test(测试指令 TEST 格式: TEST OPRD1,OPRD2 功能: 其中OPRD1、OPRD2的含义同AND指令一样,也是对两个操作数进行按位的'与'运算,唯一不同之处是不将'与'的结.说明: TEST与AND指令的关系,有点类似于CMP与SUB指令之间的关系.)指令后没变,然后执行至je指令后跳转实现,说明call调用的过程里面判断返回的值为假。那下面进入F7进入call调用的过程,让其返回为真。

    进入call调用的过程后做如下修改:

    "mov eax,1"指令的作用是将eax的al赋值为01,retn则是跳出call,程序接着执行call后面的test指令(相当于c++中的return)。

    修改后保存修改,程序破解完成!

    下面在OD中继续运行程序,发现程序成功破解,关闭OD后打开保存的程序,也到达了破解的目的:

    软件的注测一项已经不能点击,说明已经破解。

    就这个程序而言,也可以通过修改je来是实现破解:

    nop填充后保存,成功破解:

    在此示例软件上用nop填充可以达到破解的目的,但如果遇到程序有多个关键跳,那么用nop填充的方法就有点费时费力了。通过对寄存器的修改(修改call的返回值)能够起到事半功倍的效果!


    https://blog.csdn.net/song_10/article/details/83720673

    如果你没听说过,那看介绍好了
    【软件名称】WinZIP
    【软件版本】8.1 Beta 2
    【文件大小】1757KB
    【适用平台】Win9x/Me/NT/2000
    【软件简介】一个强大并且易用的压缩实用程序,支持ZIP、CAB、TAR、GZIP、MIME,以及更多格式的压缩文件。其特点是紧密地与Windows资源管理器拖放集成,不用离开资源管理器而进行压缩、解压缩。
    不用我说了吧,出处仍旧是电脑报2001年合订本的配套光盘
    我之所以先择它,是因为觉得它的关键CALL没有前边那两个那样好找(其实也就那样了^_^)极具代表性,而且通过它可以让你感受一下Ollydbg这个魅力比你家的荼几还大的调试器
    这里之所以提到Ollydbg,是觉的它真是一个非常非常棒的调试器...强烈建议你多玩几次...(MP3好听吗?^_^)
    我们来吧,首先当然还是要装上它(左闪术,右闪术),然后用Ollydbg来载入,此时界面会被分成四个部分,左上方是软件反汇编后的代码,右上方是寄存器开会的地方,左下方是内存区,右下方显示的则是堆栈的信息。
    我们来下断点,按Alt+F4,之后选USER32,然后再鼠标右键-->搜索-->当前模块中的名称,然后在那一大堆函数中找到GetDlgItemTextA,按F2来下断点,它会提示你错误,并说无法设置中断点,是不是很过瘾?(呜呜呜...大哥,我错了,再也不敢了...)
    呵呵,这个我也不知道什么原因,明明是用了这个函数嘛,就是不让断,其实我对Ollydbg也不是太那个(关键是讨厌它的下断方式)看来还是用我们的万能断点吧,输入注册名Suunb[CCG],输入注册码19870219,然后用TRW2000下断bpxhmemcpy,断到之后,pmodule返回领空后一次F12就会出错,看来所有的东东就在这里了...
    我们用TRW2000再断一下,返回领空之后记着第一条指令的地址0040bd5f,呜呜呜...上条指令明明是调用GetDlgItemTextA,为什么在Ollydbg中不让下呢?
    没关系,我们记下这个地址后仍旧用Ollydbg来加载程序,之后在反汇编窗口中找到0040bd5f处,然后按下F2来下断(会变为红色),下断之后便按F9来运行程序,接着输入注册名Suunb[CCG],注册码19870219后按确定,程序会被Ollydbg给断到:
    0040BD5F |. 57     PUSHEDI
    0040BD60 |. E8 F34A0500  CALLWINZIP32.00460858
    0040BD65 |. 57     PUSHEDI            ; /Arg1
    0040BD66 |. E8 164B0500  CALLWINZIP32.00460881       ;\WINZIP32.00460881
    0040BD6B |. 59     POPECX
    0040BD6C |. BE 1CCA4C00  MOVESI,WINZIP32.004CCA1C
    0040BD71 |. 59     POPECX
    0040BD72 |. 6A 0B    PUSH0B            ; /Count = B(11.)
    0040BD74 |. 56     PUSHESI            ; |Buffer=> WINZIP32.004CCA1C
    0040BD75 |. 68 810C0000  PUSH0C81           ;|ControlID = C81 (3201.)
    0040BD7A |. 53     PUSHEBX            ; |hWnd
    0040BD7B |. FF15 F4C54A00 CALL DWORD PTRDS:[<&USER32.GetDlgItemTe>; \GetDlgItemTextA
    0040BD81 |. 56     PUSHESI
    0040BD82 |. E8 D14A0500  CALLWINZIP32.00460858
    0040BD87 |. 56     PUSHESI
    0040BD88 |. E8 F44A0500  CALLWINZIP32.00460881
    0040BD8D |. 803D F0C94C00 >CMP BYTE PTR DS:[4CC9F0],0
    0040BD94 |. 59     POPECX
    0040BD95 |. 59     POPECX
    0040BD96 |. 74 5F    JE SHORTWINZIP32.0040BDF7
    0040BD98 |. 803D 1CCA4C00 >CMP BYTE PTR DS:[4CCA1C],0
    0040BD9F |. 74 56    JE SHORTWINZIP32.0040BDF7
    0040BDA1 |. E8 31F9FFFF  CALLWINZIP32.0040B6D7      <--关键CALL,等会儿进去玩玩
    0040BDA6 |. 84C0    TESTAL,AL          <--根据关键CALL中比较的结果来做相应的测试
    0040BDA8 |. 74 4D    JE SHORTWINZIP32.0040BDF7      <--跳走就没戏!
    0040BDAA |. 57     PUSHEDI
    0040BDAB |. 68 08DE4B00  PUSHWINZIP32.004BDE08       ; ASCII"Name"
    0040BDB0 |. FF35 1CC74A00 PUSH DWORD PTRDS:[4AC71C]      ;WINZIP32.004BDDEC
    0040BDB6 |. E8 8AFA0400  CALLWINZIP32.0045B845
    0040BDBB |. 56     PUSHESI
    0040BDBC |. 68 C8EB4B00  PUSHWINZIP32.004BEBC8       ; ASCII"SN"
    0040BDC1 |. FF35 1CC74A00 PUSH DWORD PTRDS:[4AC71C]      ;WINZIP32.004BDDEC
    0040BDC7 |. E8 79FA0400  CALLWINZIP32.0045B845
    0040BDCC |. FF35 18C74A00 PUSH DWORD PTRDS:[4AC718]      ; |Arg4 =004BDDF4 ASCII "winzip32.ini"
    0040BDD2 |. 6A 00    PUSH0            ; |Arg3 =00000000
    0040BDD4 |. 6A 00    PUSH0            ; |Arg2 =00000000
    0040BDD6 |. 68 14DE4B00  PUSHWINZIP32.004BDE14       ; |Arg1 =004BDE14 ASCII "rrs"
    0040BDDB |. E8 4CFA0400  CALLWINZIP32.0045B82C       ;\WINZIP32.0045B82C
    0040BDE0 |. A1 A8914C00  MOVEAX,DWORD PTR DS:[4C91A8]
    0040BDE5 |. 83C4 28   ADDESP,28
    0040BDE8 |. 85C0    TESTEAX,EAX
    0040BDEA |. 74 07    JE SHORTWINZIP32.0040BDF3
    0040BDEC |. 50     PUSHEAX            ; /hObject=> 000013F4 (font)
    0040BDED |. FF15 80C04A00 CALL DWORD PTRDS:[<&GDI32.DeleteObject>>; \DeleteObject
    0040BDF3 |> 6A 01    PUSH 1
    0040BDF5 |. EB 30    JMP SHORTWINZIP32.0040BE27
    0040BDF7 |> E8 C3020000  CALLWINZIP32.0040C0BF
    0040BDFC |. 68 8E020000  PUSH28E
    0040BE01 |. E8 61470500  CALLWINZIP32.00460567
    0040BE06 |. 50     PUSHEAX            ; |Arg3
    0040BE07 |. 53     PUSHEBX            ; |Arg2
    0040BE08 |. 6A 3D    PUSH3D            ; |Arg1 =0000003D
    0040BE0A |. E8 C8050400  CALLWINZIP32.0044C3D7       ;\WINZIP32.0044C3D7
    
    我们用Ollydbg断到之后,可以像在TRW2000中一样通过F8(这个调试器跟我一样,也不喜欢F4^_^)来单步执行程序,我们按32下F8后程序就会出错,那我们在第二遍载入时按F8按到20多下时就仔细看看有没有可疑的地方,你一眼就可以看到0040BDA1处的这个关键CALL,我们只要追到这里时追进去就有可能看到软件正确的注册码
    那还等什么呢?我们就进去吧...
    按F7跟进后你会看的眼花眼花缭乱,到处都是PUSH跟POP,到底哪个才是呢?现在知道我为什么让你用Ollydbg了吧(偶起初也是要用TRW2000的,但临时改变主意^_^)用Ollydbg的一个最大好处就是可以真接看到寄存器中的值,特别是你通过F8来单步执行的时候,在反汇编代码的下边,会有一个小窗体,在那里可以显示相关指令中所使用的寄存器的值,爽吧!
    我们按76下F8之后,在0040B803处就可以第一次看到正确的注册码了,呵呵,我这边儿是71C20EDC,然后你还会再陆续看到几次,爽?
    另外我还发现一个有趣的事情,在WinZIP8.1中,一个注册名可以有两个注册码,呵呵,不知道是不是还有为特别用户准备的特别注册码以用来和普通的做区别当程序通过比较,发现你输入的注册码不正确后竟然会再次算出另一个注册码来再比较一次,嘿嘿,我的第二个注册码是25170288
    
    
    
    追入关键CALL里的代码:
    0040B6D7 /$ 55     PUSHEBP
    0040B6D8 |. 8BEC    MOVEBP,ESP
    0040B6DA |. 81EC 0C020000 SUB ESP,20C
    0040B6E0 |. 8065 FF 00  AND BYTEPTR SS:[EBP-1],0
    0040B6E4 |. 803D F0C94C00 >CMP BYTE PTR DS:[4CC9F0],0
    0040B6EB |. 53     PUSHEBX
    0040B6EC |. 56     PUSHESI
    0040B6ED |. 57     PUSHEDI
    0040B6EE |. 0F84 FB000000 JE WINZIP32.0040B7EF
    0040B6F4 |. 8D45 E8   LEAEAX,DWORD PTR SS:[EBP-18]
    0040B6F7 |. 50     PUSHEAX
    0040B6F8 |. 68 C0E84B00  PUSHWINZIP32.004BE8C0
    0040B6FD |. E8 DE61FFFF  CALLWINZIP32.004018E0
    0040B702 |. 8D45 E8   LEAEAX,DWORD PTR SS:[EBP-18]
    0040B705 |. 50     PUSHEAX
    0040B706 |. E8 F57C0800  CALLWINZIP32.00493400
    0040B70B |. 83C4 0C   ADDESP,0C
    0040B70E |. 83F8 14   CMPEAX,14
    0040B711 |. 72 11    JB SHORTWINZIP32.0040B724
    0040B713 |. BF 20C74A00  MOVEDI,WINZIP32.004AC720      ; ASCII"auth.c"
    0040B718 |. 6A 21    PUSH 21
    0040B71A |. 57     PUSHEDI
    0040B71B |. E8 86F60000  CALLWINZIP32.0041ADA6
    0040B720 |. 59     POPECX
    0040B721 |. 59     POPECX
    0040B722 |. EB 05    JMP SHORTWINZIP32.0040B729
    0040B724 |> BF 20C74A00  MOVEDI,WINZIP32.004AC720      ; ASCII"auth.c"
    0040B729 |> 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
    0040B72F |. BB F0C94C00  MOVEBX,WINZIP32.004CC9F0      ; ASCII"Suunb[CCG]"
    0040B734 |. 50     PUSHEAX
    0040B735 |. 53     PUSHEBX
    0040B736 |. E8 50030000  CALLWINZIP32.0040BA8B
    0040B73B |. 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
    0040B741 |. 50     PUSHEAX
    0040B742 |. E8 B97C0800  CALLWINZIP32.00493400
    0040B747 |. BE C8000000  MOVESI,0C8
    0040B74C |. 83C4 0C   ADDESP,0C
    0040B74F |. 3BC6    CMPEAX,ESI
    0040B751 |. 72 0A    JB SHORTWINZIP32.0040B75D
    0040B753 |. 6A 23    PUSH 23
    0040B755 |. 57     PUSHEDI
    0040B756 |. E8 4BF60000  CALLWINZIP32.0041ADA6
    0040B75B |. 59     POPECX
    0040B75C |. 59     POPECX
    0040B75D |> 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
    0040B763 |. 50     PUSHEAX
    0040B764 |. 8D45 E8   LEAEAX,DWORD PTR SS:[EBP-18]
    0040B767 |. 50     PUSHEAX
    0040B768 |. E8 03300900  CALLWINZIP32.0049E770
    0040B76D |. 59     POPECX
    0040B76E |. 85C0    TESTEAX,EAX
    0040B770 |. 59     POPECX
    0040B771 |. 75 04    JNZ SHORTWINZIP32.0040B777
    0040B773 |. C645 FF 01  MOV BYTEPTR SS:[EBP-1],1
    0040B777 |> 8D45 E8   LEAEAX,DWORD PTR SS:[EBP-18]
    0040B77A |. 50     PUSHEAX
    0040B77B |. 68 D0E84B00  PUSHWINZIP32.004BE8D0
    0040B780 |. E8 5B61FFFF  CALLWINZIP32.004018E0
    0040B785 |. 8D45 E8   LEAEAX,DWORD PTR SS:[EBP-18]
    0040B788 |. 50     PUSHEAX
    0040B789 |. E8 727C0800  CALLWINZIP32.00493400
    0040B78E |. 83C4 0C   ADDESP,0C
    0040B791 |. 83F8 14   CMPEAX,14
    0040B794 |. 72 0A    JB SHORTWINZIP32.0040B7A0
    0040B796 |. 6A 27    PUSH 27
    0040B798 |. 57     PUSHEDI
    0040B799 |. E8 08F60000  CALLWINZIP32.0041ADA6
    0040B79E |. 59     POPECX
    0040B79F |. 59     POPECX
    0040B7A0 |> 8D45 E8   LEAEAX,DWORD PTR SS:[EBP-18]
    0040B7A3 |. 50     PUSHEAX
    0040B7A4 |. 53     PUSHEBX
    0040B7A5 |. E8 C62F0900  CALLWINZIP32.0049E770
    0040B7AA |. 59     POPECX
    0040B7AB |. 85C0    TESTEAX,EAX
    0040B7AD |. 59     POPECX
    0040B7AE |. 75 0E    JNZ SHORTWINZIP32.0040B7BE
    0040B7B0 |. FF15 F0C14A00 CALL DWORD PTRDS:[<&KERNEL32.GetTickCou>; [GetTickCount
    0040B7B6 |. A8 01    TESTAL,1
    0040B7B8 |. 74 04    JE SHORTWINZIP32.0040B7BE
    0040B7BA |. C645 FF 01  MOV BYTEPTR SS:[EBP-1],1
    0040B7BE |> 6A 14    PUSH 14
    0040B7C0 |. 8D45 E8   LEAEAX,DWORD PTR SS:[EBP-18]
    0040B7C3 |. 6A 00    PUSH 0
    0040B7C5 |. 50     PUSHEAX
    0040B7C6 |. E8 75820800  CALLWINZIP32.00493A40
    0040B7CB |. 56     PUSHESI
    0040B7CC |. 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
    0040B7D2 |. 6A 00    PUSH 0
    0040B7D4 |. 50     PUSHEAX
    0040B7D5 |. E8 66820800  CALLWINZIP32.00493A40
    0040B7DA |. 83C4 18   ADDESP,18
    0040B7DD |. 807D FF 00  CMP BYTEPTR SS:[EBP-1],0
    0040B7E1 |. 74 13    JE SHORTWINZIP32.0040B7F6
    0040B7E3 |. E8 D7080000  CALLWINZIP32.0040C0BF
    0040B7E8 |. 8025 EDBF4C00 >AND BYTE PTR DS:[4CBFED],0
    0040B7EF |> 32C0    XORAL,AL
    0040B7F1 |. E9 F5000000  JMPWINZIP32.0040B8EB
    0040B7F6 |> 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
    0040B7FC |. 50     PUSHEAX
    0040B7FD |. 53     PUSHEBX
    0040B7FE |. E8 ED000000  CALLWINZIP32.0040B8F0      <--参与计算软正确的注册码
    0040B803 |. 8D85 BCFEFFFF LEA EAX,DWORD PTRSS:[EBP-144]    <--在这里第一次发现软件正确的注册码
    0040B809 |. 50     PUSHEAX            
    0040B80A |. E8 F17B0800  CALLWINZIP32.00493400
    0040B80F |. BE 2C010000  MOVESI,12C
    0040B814 |. 83C4 0C   ADDESP,0C
    0040B817 |. 3BC6    CMPEAX,ESI
    0040B819 |. 72 0A    JB SHORTWINZIP32.0040B825
    0040B81B |. 6A 39    PUSH 39
    0040B81D |. 57     PUSHEDI
    0040B81E |. E8 83F50000  CALLWINZIP32.0041ADA6
    0040B823 |. 59     POPECX
    0040B824 |. 59     POPECX
    0040B825 |> BF 1CCA4C00  MOVEDI,WINZIP32.004CCA1C      ; ASCII"19870219"    <--将刚才输入的错误的注册码放入EDI
    0040B82A |. 8D85 BCFEFFFF LEA EAX,DWORD PTRSS:[EBP-144]    <--EAX中装入正确的注册码所在的地址
    0040B830 |. 57     PUSHEDI            <--用户输入的注册码入栈
    0040B831 |. 50     PUSHEAX            <--软件计算出的正确的注册码入栈
    0040B832 |. E8 392F0900  CALLWINZIP32.0049E770      <--关键CALL,用于比较用户输入的注册码
    0040B837 |. F7D8    NEGEAX
    0040B839 |. 1AC0    SBBAL,AL
    0040B83B |. 59     POPECX
    0040B83C |. FEC0    INC AL
    0040B83E |. 59     POPECX
    0040B83F |. A2 EDBF4C00  MOV BYTEPTR DS:[4CBFED],AL
    0040B844 |. 0F85 8A000000 JNZ WINZIP32.0040B8D4
    0040B84A |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
    0040B850 |. 50     PUSHEAX
    0040B851 |. 53     PUSHEBX
    0040B852 |. E8 33010000  CALLWINZIP32.0040B98A      <--参与计算软件的第二个注册码
    0040B857 |. 8D85 BCFEFFFF LEA EAX,DWORD PTRSS:[EBP-144]    <--此时软件会再算出另外一个注册码
    0040B85D |. 50     PUSHEAX            
    0040B85E |. E8 9D7B0800  CALLWINZIP32.00493400
    0040B863 |. 83C4 0C   ADDESP,0C
    0040B866 |. 3BC6    CMPEAX,ESI
    0040B868 |. 72 0E    JB SHORTWINZIP32.0040B878
    0040B86A |. 6A 3E    PUSH 3E
    0040B86C |. 68 20C74A00  PUSHWINZIP32.004AC720       ; ASCII"auth.c"
    0040B871 |. E8 30F50000  CALLWINZIP32.0041ADA6
    0040B876 |. 59     POPECX
    0040B877 |. 59     POPECX
    0040B878 |> 8D85 BCFEFFFF LEA EAX,DWORD PTRSS:[EBP-144]    <--软件计算出的第二个注册码装入EAX中
    0040B87E |. 57     PUSHEDI            <--用户输入的注册码入栈
    0040B87F |. 50     PUSHEAX            <--软件计算出的第二个注册码入栈
    0040B880 |. E8 EB2E0900  CALLWINZIP32.0049E770      <--另一个关键CALL,用于比较第二次生成的注册码
    0040B885 |. F7D8    NEGEAX
    0040B887 |. 1AC0    SBBAL,AL
    0040B889 |. 59     POPECX
    0040B88A |. FEC0    INC AL
    0040B88C |. 59     POPECX
    0040B88D |. A2 EDBF4C00  MOV BYTEPTR DS:[4CBFED],AL
    0040B892 |. 75 40    JNZ SHORTWINZIP32.0040B8D4
    0040B894 |. 8D85 C0FEFFFF LEA EAX,DWORD PTR SS:[EBP-140]
    0040B89A |. 6A 04    PUSH 4
    0040B89C |. 50     PUSHEAX
    0040B89D |. 57     PUSHEDI
    0040B89E |. E8 DD690900  CALLWINZIP32.004A2280
    0040B8A3 |. 83C4 0C   ADDESP,0C
    0040B8A6 |. 85C0    TESTEAX,EAX
    0040B8A8 |. 75 23    JNZ SHORTWINZIP32.0040B8CD
    0040B8AA |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
    0040B8B0 |. 6A 04    PUSH 4
    0040B8B2 |. 50     PUSHEAX
    0040B8B3 |. 68 20CA4C00  PUSHWINZIP32.004CCA20       ; ASCII"0219"
    0040B8B8 |. E8 C3690900  CALLWINZIP32.004A2280
    0040B8BD |. 83C4 0C   ADDESP,0C
    0040B8C0 |. 85C0    TESTEAX,EAX
    0040B8C2 |. 75 09    JNZ SHORTWINZIP32.0040B8CD
    0040B8C4 |. C605 EDBF4C00 >MOV BYTE PTR DS:[4CBFED],1
    0040B8CB |. EB 07    JMP SHORTWINZIP32.0040B8D4
    0040B8CD |> 8025 EDBF4C00 >AND BYTE PTR DS:[4CBFED],0
    0040B8D4 |> 56     PUSHESI
    0040B8D5 |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
    0040B8DB |. 6A 00    PUSH 0
    0040B8DD |. 50     PUSHEAX
    0040B8DE |. E8 5D810800  CALLWINZIP32.00493A40
    0040B8E3 |. A0 EDBF4C00  MOVAL,BYTE PTR DS:[4CBFED]
    0040B8E8 |. 83C4 0C   ADDESP,0C
    0040B8EB |> 5F     POPEDI
    0040B8EC |. 5E     POPESI
    0040B8ED |. 5B     POPEBX
    0040B8EE |. C9     LEAVE
    0040B8EF \. C3     RETN
    
    整理一下:
    注册名:Suunb[CCG]
    注册码:71C20EDC or 25170288
    其实如果你坐在那里肯花上一杯茶的功夫来仔细想一下,就会知道,其实一点儿也不难,只是有一点点麻烦而以
    这一章也就到这里吧,我现在巨困无比...
    最后说一下的是,现在有仍有N多的软件用的是明码的比较方法,所以,要想找一两个软件练练手还是挺容易的
    这一章本来还打算讲一下那些非明码比较的软件的,但忽然发现,如果通过非明码比较的软件能找到注册码的话,那应该也就把它的算法给搞的差不多了,所以,到下一章,分析软件的注册算法时再讲吧...

    https://www.52pojie.cn/forum.php?mod=viewthread&tid=276120

    今天测试
    mov al ,10
    mov ah ,20
    add al , ah
    mov byte ptr[a] ,al //字节计算
    sub al ,ah
    cmp zf ,0
    mov byte ptr [ 判断结果 ] , zf

    mov bl ,zf //暂存 判断结果 if (al==ah)

    |63...................................................|31..........................|15............|7....0|
    |AH...........|AL....|
    |AX.....................|
    |EAX..................................................|
    |RAX...........................................................................................................|
    ————————————————
    版权声明:本文为CSDN博主「极简完美之道~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/q158805972/article/details/100152946

    第一部分:基础知识

    汇编语言是一切程序的起点和终点,毕竟所有的高级语言都是建立在汇编基础之上的。在许多高级语言中我们都需要相对明确的语法,但是在汇编中,我们会使用一些单词缩写和数字来表达程序。

    I. 单元、位和字节

    1. ·BIT(位) - 电脑数据量的最小单元,可以是0或者1。
    2. 例:00000001 = 1;00000010 = 2;00000011 = 3     
    3. ·BYTE(字节) - 一个字节包含8个位,所以一个字节最大值是255(0-255)。为了方便阅读,我们通常使用16进制来表示。
    4. ·WORD(字) - 一个字由两个字节组成,共有16位。一个字的最大值是0FFFFh (或者是 65535d) (h代表16进制,d代表10进制)。
    5. ·DOUBLE WORD(双字DWORD) - 一个双字包含两个字,共有32位。最大值为0FFFFFFFF (或者是 4294967295d)。
    6. ·KILOBYTE(千字) - 千字节并不是1000个字节,而是1024 (32*32) 个字节。
    7. ·MEGABYTE - 兆字节同样也不是一兆个字节,而是1024*1024=1,048,576 个字节

    II. 寄存器

    寄存器是计算机储存数据的“特别地方”。你可以把寄存器看作一个小盒子,我们可以在里面放很多东西:比如名字、数字、一段话……

    如今Win+Intel CPU组成的计算机通常有9个32位寄存器 (w/o 标志寄存器)。它们是:

    1. EAX: 累加器
    2. EBX: 基址寄存器
    3. ECX: 计数器
    4. EDX: 数据寄存器
    5. ESI: 源变址寄存器
    6. EDI: 目的变址寄存器
    7. EBP: 扩展基址指针寄存器
    8. ESP: 栈指针寄存器
    9. EIP: 指令指针寄存器

    通常来说寄存器大小都是32位 (四个字节) 。它们可以储存值为从0-FFFFFFFF (无符号)的数据。起初大部分寄存器的名字都暗示了它们的功能,比如ECX=计数,但是现在你可以使用任意寄存器进行计数 (只有在一些自定义的部分,计数才必须用到ECX)。当我用到EAX、EBX、ECX、EDX、ESI和EDI这些寄存器时我才会详细解释其功能,所以我们先讲EBP、ESP、EIP。 

    1. EBP: EBP在栈中运用最广,刚开始没有什么需要特别注意的 ;) 
    2. ESP: ESP指向栈区域的栈顶位置。栈是一个存放即将会被用到的数据的地方,你可以去搜索一下push/pop 指令了解更多栈知识。 
    3. EIP: EIP指向下一个将会被执行的指令。

    还有一件值得注意的事:有一些寄存器是16位甚至8位的,它们是不能直接寻址的。

    通用寄存器包括: 

    8个32位:EAX ECX EDX EBX ESI EDI EBP ESP

    8个16位:AX CX DX BX SI DI BP SP

    8个8位: AH AL CH CL DH DL BH HL

    16位段寄存器:CS DS ES GS SS

    由CPU 控制改变的 32位EIP / 16位 IP

    32位EFLAGS / 16位FLAGS

    比如,EAX是这个32位寄存器的名字,EAX的低16位部分被称作AX,AX又分为高8位的AH和低8位的AL两个独立寄存器。 

    注意:即使不怎么重要,你至少也要知道以下的寄存器

    这些寄存器可以帮助我们区分大小: 

    i. 单字节(8位)寄存器: 顾名思义,这些寄存器都是一个字节 (8位) :

    1. AL and AH
    2. BL and BH
    3. CL and CH
    4. DL and DH

    ii. 单字(16位)寄存器: 这些寄存器大小为一个字 (=2 字节 = 16 位)。一个单字寄存器包含两个单字节寄存器。我们通常根据它们的功能来区分它们。 

    1. 通用寄存器:

    1. AX-> 'accumulator'(累加器):用于进行数学运算。 (单字=16位) = AH + AL -> 其中‘+’号并不代表把它们代数相加。AH和AL寄存器是相互独立的,只不过都是AX寄存器的一部分,所以你改变AH或AL (或者都改变) ,AX寄存器也会被改变。 
    2. BX -> 'base'(基址寄存器):用来连接栈(之后会说明)
    3. CX -> 'counter'(计数器):
    4. DX -> 'data'(数据寄存器):大多数情况下用来存放数据
    5. DI -> 'destination index'(目的变址寄存器): 例如将一个字符串拷贝到DI
    6. SI -> 'source index'(源变址寄存器): 例如将一个字符串从SI拷贝

    2. 索引寄存器(指针寄存器): 

    1. BP -> 'base pointer'(基址指针寄存器):表示栈区域的基地址
    2. SP -> 'stack pointer'(栈指针寄存器):表示栈区域的栈顶地址

    3. 段寄存器:

    1. CS -> 'code segment'(代码段寄存器):用于存放应用程序代码所在段的段基址(之后会说明)
    2. DS -> 'data segment'(数据段寄存器):用于存放数据段的段基址(以后会说明)
    3. ES -> 'extra segment'(附加段寄存器):用于存放程序使用的附加数据段的基地址
    4. SS -> 'stack segment'(栈段寄存器):用于存放栈段的段基址(以后会说明)

    4. 指令指针寄存器:

    IP -> 'instruction pointer'(指令指针寄存器):指向下一个指令 ;)

    iii. 双字(32位)寄存器:

    2 字= 4 字节= 32 位, EAX、EBX、ECX、EDX、EDI…… 

    如果16位寄存器前面加了‘E’,就代表它们是32位寄存器。例如,AX=16位,EAX=32位。 

    III. 标志寄存器

    标志寄存器代表某种状态。在32位CPU中有32个不同的标志寄存器,不过不用担心,我们只关心其中的3个:ZF、OF、CF。在逆向工程中,你了解了标志寄存器就能知道程序在这一步是否会跳转,标志寄存器就是一个标志,只能是0或者1,它们决定了是否要执行某个指令。

    Z-Flag(零标志):

    ZF是破解中用得最多的寄存器(通常情况下占了90%),它可以设成0或者1。若上一个运算结果为0,则其值为1,否则其值为0。(你可能会问为什么‘CMP’可以操作ZF寄存器,这是因为该指令在做比较操作(等于、不等于),那什么时候结果是0什么时候是1呢?待会再说) 

    The O-Flag(溢出标志):

    OF寄存器在逆向工程中大概占了4%,当上一步操作改变了某寄存器的最高有效位时,OF寄存器会被设置成1。例如:EAX的值为7FFFFFFFF,如果你此时再给EAX加1,OF寄存器就会被设置成1,因为此时EAX寄存器的最高有效位改变了(你可以使用电脑自带计算器将这个16进制转化成2进制看看)。还有当上一步操作产生溢出时(即算术运算超出了有符号数的表示范围),OF寄存器也会被设置成1。 

    The C-Flag(进位标志):

    进位寄存器的使用大概占了1%,如果产生了溢出,就会被设置成1。例,假如某寄存器值为FFFFFFFF,再加上1就会产生溢出,你可以用电脑自带的计算器尝试。 

    IV. 段偏移 

    内存中的一个段储存了指令(CS)、数据(DS)、堆栈(SS)或者其他段(ES)。每个段都有一个偏移量,在32位应用程序下,这些偏移量由 00000000 到 FFFFFFFF。段和偏移量的标准形式如下:

    段:偏移量 = 把它们放在一起就是内存中一个具体的地址。

    可以这样看:

    一个段是一本书的某一页:偏移量是一页的某一行

    V. 栈

    栈是内存里可以存放稍后会用到的东西的地方。可以把它看作一个箱子里的一摞书,最后一本放进去的永远是最先出来的。或者把栈看作一个放纸的盒子,盒子是栈,而每一张纸就代表了一个内存地址。总之记住这个规则:最后放的纸最先被拿出来。’push’命令就是向栈中压入数据,‘pop’命令就是从栈中取出最后放入的数据并且把它存进具体的寄存器中。

    VI. 指令 (字母表排序) 

    请注意,所有的值通常是以16进制形式储存的。

    大部分指令有两个操作符 (例如:add EAX, EBX),有些是一个操作符 (例如:not EAX),还有一些是三个操作符 (例如:IMUL EAX、EDX、64)。如果你使用 “DWORD PTR [XXX]”就表示使用了内存中偏移量为[XXX]的的数据。注意:字节在内存中储存方式是倒过来的(Win+Intel的电脑上大部分采用”小端法”, WORD PTR [XXX](双字节)和 BYTE PTR [XXX](单字节)也都遵循这一规定)。 

    大部分有两个操作符的指令都是以下这些形式(以add指令举例):

    add eax,ebx                          ;; 寄存器, 寄存器
    add eax,123                          ;; 寄存器, 数值
    add eax,dword ptr [404000]           ;; 寄存器, Dword  指针 [数值]
    add eax,dword ptr [eax]              ;; 寄存器, Dword  指针 [寄存器值]
    add eax,dword ptr [eax+00404000]     ;; 寄存器, Dword  指针 [寄存器值+数值]
    add dword ptr [404000],eax           ;; Dword 指针[数值], 寄存器
    add dword ptr [404000],123           ;; Dword 指针[数值], 数值
    add dword ptr [eax],eax              ;; Dword 指针[寄存器值], 寄存器
    add dword ptr [eax],123              ;; Dword 指针[寄存器值], 数值
    add dword ptr [eax+404000],eax       ;; Dword 指针[寄存器值+数值], 寄存器
    add dword ptr [eax+404000],123       ;; Dword 指针[寄存器值+数值], 数值

    ADD (加)

    语法: ADD 被加数, 加数

    加法指令将一个数值加在一个寄存器上或者一个内存地址上。

    add eax,123 = eax=eax+123;

    加法指令对ZF、OF、CF都会有影响。

    AND (逻辑与)

    语法: AND 目标数, 原数    

    AND运算对两个数进行逻辑与运算。

    AND指令会清空OF,CF标记,设置ZF标记。

    为了更好地理解AND,这里有两个二进制数:

    10010101100101001101

    如果对它们进行AND运算,结果是0001000100

    即同真为真(1),否则为假(0),你可以用计算器验证。

    CALL (调用)

    语法:CALL something

    CALL指令将当前的相对地址(IP)压入栈中,并且调用CALL 后的子程序

    CALL 可以这样使用:

    CALL 404000                ;; 最常见: CALL 地址CALL EAX                   ;; CALL 寄存器 - 如果寄存器存的值为404000,那就等同于第一种情况CALL DWORD PTR [EAX]       ;; CALL [EAX]偏移量所指向的地址CALL DWORD PTR [EAX+5]     ;; CALL [EAX+5]偏移量所指向的地址

    CDQ

    Syntax: CDQ

    CDQ指令第一次出现时通常不好理解。它通常出现在除法前面,作用是将EDX的所有位变成EAX最高位的值,

    比如当EAX>=80000000h时,其二进制最高位为1,则EDX被32位全赋值为1,即FFFFFFFF

    若EAX<80000000,则其二进制最高位为0,EDX为00000000。

    然后将EDX:EAX组成新数(64位):FFFFFFFF 80000000

    CMP (比较)

    语法: CMP 目标数, 原数

    CMP指令比较两个值并且标记CF、OF、ZF:

    CMP     EAX, EBX              ;; 比较eax和ebx是否相等,如果相等就设置ZF为1CMP     EAX,[404000]          ;; 比较eax和偏移量为[404000]的值是否相等CMP     [404000],EAX          ;; 比较[404000]是否与eax相等

    DEC (自减)

    语法: DEC something

    dec用来自减1,相当于c中的–

    dec可以有以下使用方式:

    dec eax                             ;; eax自减1dec [eax]                           ;; 偏移量为eax的值自减1dec [401000]                        ;; 偏移量为401000的值自减1dec [eax+401000]                    ;; 偏移量为eax+401000的值自减1

    dec指令可以标记ZF、OF

    DIV (除)

    语法: DIV 除数

    DIV指令用来将EAX除以除数(无符号除法),被除数通常是EAX,结果也储存在EAX中,而被除数对除数取的模存在除数中。

    例:

    mov eax,64                      ;; EAX = 64h = 100mov ecx,9                       ;; ECX = 9div ecx                         ;; EAX除以ECX

    在除法之后 EAX = 100/9 = 0B(十进制:11) 并且 ECX = 100 MOD 9 = 1

    div指令可以标记CF、OF、ZF

    IDIV (整除)

    语法: IDIV 除数

    IDIV执行方式同div一样,不过IDIV是有符号的除法

    idiv指令可以标记CF、OC、ZF

    IMUL (整乘)

    语法:IMUL 数值

    IMUL 目标寄存器、数值、数值

    IMUL 目标寄存器、数值

    IMUL指令可以把让EAX乘上一个数(INUL 数值)或者让两个数值相乘并把乘积放在目标寄存器中(IMUL 目标寄存器, 数值,数值)或者将目标寄存器乘上某数值(IMUL 目标寄存器, 数值)

    如果乘积太大目标寄存器装不下,那OF、CF都会被标记,ZF也会被标记

    INC (自加)

    语法: INC something

    INC同DEC相反,它是将值加1

    INC指令可以标记ZF、OF

    INT

    语法: int 目标数

    INT 的目标数必须是产生一个整数(例如:int 21h),类似于call调用函数,INT指令是调用程序对硬件控制,不同的值对应着不同的功能。

    具体参照硬件说明书。

    JUMPS

    这些都是最重要的跳转指令和触发条件(重要用*标记,最重要用**标记):

    指令                条件                    条件JA*          -    如果大于就跳转(无符号)      - CF=0 and ZF=0JAE          -    如果大于或等于就跳转(无符号)- CF=0JB*          -    如果小于就跳转(无符号)   - CF=1JBE          -    如果小于或等于就跳转(无符号)- CF=1 or ZF=1JC           -    如果CF被标记就了跳转       - CF=1JCXZ         -    如果CX等于0就跳转      - CX=0JE**         -    如果相等就跳转        - ZF=1JECXZ        -    如果ECX等于0就跳转       - ECX=0JG*          -    如果大于就跳转(有符号)   - ZF=0 and SF=OF (SF = Sign Flag)JGE*         -    如果大于或等于就跳转(有符号) - SF=OFJL*          -    如果小于就跳转(有符号)    - SF != OF (!= is not)JLE*         -    如果小于或等于就跳转(有符号 - ZF=1 and OF != OFJMP**        -    跳转             - 强制跳转JNA          -    如果不大于就跳转(无符号)   - CF=1 or ZF=1JNAE         -    如果不大于等于就跳转(无符号) - CF=1JNB          -    如果不小于就跳转(无符号)   - CF=0JNBE         -    如果不小于等于就跳转(无符号) - CF=0 and ZF=0JNC          -    如果CF未被标记就跳转     - CF=0JNE**        -    如果不等于就跳转       - ZF=0JNG          -    如果不大于就跳转(有符号)   - ZF=1 or SF!=OFJNGE         -    如果不大于等于就跳转(有符号) - SF!=OFJNL          -    如果不小于就跳转(有符号)   - SF=OFJNLE         -    如果不小于等于就跳转(有符号) - ZF=0 and SF=OFJNO          -    如果OF未被标记就跳转     - OF=0JNP          -    如果PF未被标记就跳转     - PF=0JNS          -    如果SF未被标记就跳转      - SF=0JNZ          -    如果不等于0就跳转      - ZF=0JO           -    如果OF被标记就跳转     - OF=1JP           -    如果PF被标记就跳转     - PF=1JPE          -    如果是偶数就跳转       - PF=1JPO          -    如果是奇数就跳转       - PF=0JS           -    如果SF被标记就跳转     - SF=1JZ           -    如果等于0就跳转      - ZF=1

    LEA (有效地址传送)

    语法:LEA 目的数、源数

    LEA可以看成和MOV差不多的指令LEA ,它本身的功能并没有被太广泛的使用,反而广泛运用在快速乘法中:

    lea eax,dword ptr [4*ecx+ebx]

    将eax赋值为 4*ecx+ebx

    MOV (传送)

    语法: MOV 目的数,源数

    这是一个很简单的指令,MOV指令将源数赋值给目的数,并且源数值保持不变

    这里有一些MOV的变形:

    MOVS/MOVSB/MOVSW/MOVSD EDI, ESI:这些变形能将ESI指向的内容传送到EDI指向的内容中去

    MOVSX:MOVSX指令将单字或者单字节扩展为双字或者双字节传送,原符号不变

    MOVZX:MOVZX扩展单字节或单字为双字节或双字并且用0填充剩余部分(通俗来说就是将源数取出置于目的数,其他位用0填充)

    MUL (乘法)

    语法:MUL 数值

    这个指令同IMUL一样,不过MUL可以乘无符号数。

    NOP (无操作)

    语法:NOP

    这个指令说明不做任何事

    所以它在逆向中运用范围最广

    OR (逻辑或)

    语法:OR 目的数,源数

    OR指令对两个值进行逻辑或运算

    这个指令会清空OF、CF标记,设置ZF标记

    为了更好的理解OR,思考下面二进制串:

    10010101100101001101

    如果对它们进行逻辑与运算,结果将是1101011111。

    只有当两边同为0时其结果为0,否则就为1。你可以用计算器尝试计算。希望你能理解为什么,最好自己动手算一算

    POP

    语法:POP 目的地址

    POP指令将栈顶第一个字传送到目的地址。 每次POP后,ESP(栈指针寄存器)都会增加以指向新栈顶

    PUSH

    语法:PUSH 值

    PUSH是POP的相反操作,它将一个值压入栈并且减小栈顶指针值以指向新栈顶。

    REP/REPE/REPZ/REPNE/REPNZ

    语法: REP/REPE/REPZ/REPNE/REPNZ ins

    重复上面的指令:直到CX=0。ins必须是一个操作符,比如CMPS、INS、LODS、MOVS、OUTS、SCAS 或 STOS

    RET (返回)

    语法:RET

    RET digit

    RET指令的功能是从一个代码区域中退出到调用CALL的指令处。

    RET digit在返回前会清理栈

    SUB (减)

    语法:SUB 目的数,源数

    SUB与ADD相反,它将源数减去目的数,并将结果储存在目的数中

    SUB可以标记ZF、OF、CF

    TEST

    语法:TEST 操作符、操作符

    这个指令99%都是用于”TEST EAX, EAX”,它执行与AND相同的功能,但是并不储存数据。如果EAX=0就会标记ZF,如果EAX不是0,就会清空ZF

    XOR

    语法:XOR 目的数,源数

    XOR指令对两个数进行异或操作

    这个指令清空OF、CF,但会标记ZF

    为了更好的理解,思考下面的二进制串:

    10010101100101001101

    如果异或它们,结果将是1100011011

    如果两个值相等,则结果为0,否则为1,你可以使用计算器验算。

    很多情况下我们会使用”XOR EAX, EAX”,这个操作是将EAX赋值为0,因为当一个值异或其自身,就过都是0。你最好自己动手尝试下,这样可以帮助你理解得更好。

    VII. 逻辑操作符  

    下面都是通常的逻辑操作符:

    好了,汇编基础已经梳理完毕!

    昨天发了个帖子https://www.xuepojie.com/thread-3844-1-1.html
    在昨天的破解分析中遇到了sete的 赋值,以前这样赋值的不多,一般用mov赋值。特查阅资料供大家参考!
    005622D7  |.  0F9403        sete byte ptr ds:[ebx]
    这里会让我们的断点数值变为0
    修改方案
    005622D7      C603 01       mov byte ptr ds:[ebx],0x1
    以下是我找的资料!不要忽视任何赋值的语句!才能使我们更快的破解
    SETBE/SETNA 小于等于/不大于 CF=1 或 ZF=1

    SETE/SETZ 等于/为零 ZF=1
    SETNE/SETNZ 不等于/不为零 ZF=0

    SETG/SETNLE 大于/不小于等于(带符号) SF=0 或 ZF=0
    SETGE/SETNL 大于等于/不小于(带符号) SF=0
    SETL/SETNGE 小于/不大于等于(带符号) SF=1
    SETLE/SETNG 小于等于/不大于(带符号) SF=1 或 ZF=1

    SETC 有进位或借位 CF=1
    SETNC 无进位或借位 CF=0

    SETO 有溢出 OF=1
    SETNO 无溢出 OF=0

    SETP/SETPE 低8位1的个数为偶数 PF=1
    SETNP/SETPO 低8位1的个数为奇数 PF=0

    SETS 结果为负 SF=1
    SETNS 结果为正 SF=0

    其中在CE中SETNC指令相当于SETNE,一般都会把SETNE写成SETNC。
    例如:
    CMP EAX,EBX
    SETNC CL //如果EAX不=EBX,那么CL=01,否则CL=00
    CMP EAX,ECX
    SETE [20401000] //如果EAX=ECX,那么0x20401000处=01,否则=00
    注意目标为地址时,不需要用 XXXX PTR 来区分数据的操作长度。

    同样在修改时,如果不知道前面判定的结果会是什么,那么可以直接把
    SETE AL改成MOV AL,01或者是MOV AL,00这两种指令,来看游戏中的实际影响。
    SETE AL指令占3字节,MOV AL,01指令占2字节,再补上一个NOP指令,正好可以替换

    Google Analytics的原理

    GA介绍
    Google Analytics(GA)是Google的一款免费的分析服务(网站+APP),自从其诞生以来,即广受好评。Google Analytics功能非常强大,只要在网站的页面上加入一段代码,就可以提供的丰富详尽的图表式报告。

    GA原理
    Google Analytics通过在网页中嵌入一段GA的JS代码,然后这段GA的JS代码会收集相关信息通过1像素的gif图片来发送相关的信息给Google的服务器,以完成数据统计。
    Google Analytics的原理
    一般来说,Google Analytics(分析)跟踪代码 (GATC/ga.js) 会在以下情况下提取网页数据:
    1.浏览器请求的网页包含跟踪代码。
    2.创建了一个名为 _gaq 的 JavaScript 数组,且跟踪命令被推送至该数组。
    3.创建并启用了一个 <script> 元素,以便进行异步载入(在后台载入)。
    4.获取了 ga.js 跟踪代码,且自动检测到了适当的协议。获取并载入代码之后,执行了针对 _gaq 数组的命令,且该数组被传输至跟踪对象。后续跟踪调用直接针对 Google Analytics(分析)进行。
    5.向 DOM 加载脚本元素。
    6.在跟踪代码收集数据之后,GIF 请求被发送至 Google Analytics(分析)数据库,以便进行记录和后处理。
    在Chrome打开任意部署了GA的站点,按F12,打开调试窗口,选择Network后按F5刷新,在找出向google发送数据的URL,如下图,URL后面的一大堆参数就是向谷歌服务器发送的数据,形式是1像素gif的形式。旧版版本的还可以在url上看到“_utf.gid”,新版的走MP(测量协议)格式协议,url上没有gif字段,有collect字段
    旧版本:
    Google Analytics的原理

    1. https://www.google-analytics.com/__utm.gif?utmwv=5.6.7&utms=2&utmn=594850897&utmhn=ichdata.com&utmcs=UTF-8&utmsr=320x568&utmvp=980x1739&utmsc=24-bit&utmul=en-us&utmje=0&utmfl=-&utmdt=Haran%20-%20ichdata&utmhid=825183211&utmr=-&utmp=%2F&utmht=1466060230740&utmac=UA-69988360-1&%3D252190481.1157261441.1464162601.1465975935.1466060219.9%3B%2B__utmz%3D252190481.1464162601.1.1.utmcsr%3Dgoogle%7Cutmccn%3D(organic)%7Cutmcmd%3Dorganic%7Cutmctr%3D(not%2520provided)%3B&utmjid=&utmu=qAAAAAAAAAAAAAAAAAAAAAAE~

    新版本:
    Google Analytics的原理

    1. https://www.google-analytics.com/collect?v=1&_v=j47&a=223781241&t=pageview&_s=1&dl=https%3A%2F%2Fwww.ppmoney.com%2F&ul=en-us&de=UTF-8&dt=PPmoney%E4%B8%87%E6%83%A0_%E4%B8%8A%E5%B8%82%E7%B3%BBA%2B%E7%BA%A7%E6%99%BA%E8%83%BD%E6%8A%95%E8%B5%84%E7%90%86%E8%B4%A2%E5%B9%B3%E5%8F%B0%20-%20PPmoney&sd=24-bit&sr=1680x1050&vp=1663x920&je=0&fl=23.0%20r0&_u=ACCAgEAB~&jid=&cid=386508397.1476667584&tid=UA-59232800-1&z=810184108

    通过 GIF 请求传递的许多参数的列表,如上面那段。每次执行跟踪代码时并非都会传递所有参数,因为某些参数仅适用于特定情况,如广告系列引荐或购物车,且每次打开时候传递的次数是不等的。下面是参数的最常用变量。
    Google Analytics的原理
    Google Analytics的原理

    GA能否正常传递数据:
    部分人认为GA是谷歌的产品,谷歌已经被屏蔽了,所以GA不能正常传递数据给谷歌,因为数据不具备参考性,事实上,GA接受数据的服务器是没有被屏蔽的,可以再cmd里面用ping http://www.google-analytics.com做个测试,完全可以ping通,部分人可能会通过一些测试站点测试说某些地区ping不同或严重超时,这个是取决于运营商或地区的,我现在有个美国的VPS,联通电信都秒开,但用某些小宽带就很难打开或打不开,所以屏蔽问题并不存在,只是要看GA数据的页面是在google.com下的子域名被屏蔽了,要看数据结果的时候需要FQ。

    =========================

    工作机会(内部推荐):发送邮件至gaoyabing@126.com,看到会帮转内部HR。

    邮件标题:X姓名X_X公司X_简历(如:张三_东方财富_简历),否则一律垃圾邮件!

    公司信息:

    1. 1.东方财富|上海徐汇、南京|微信客户端查看职位(可自助提交信息,微信打开);
  • 相关阅读:
    DataFrame转矩阵Np-Array
    十月15
    十月14
    十月14
    十月12
    十月10
    2016-02-22 有无网络 2
    2016-02-22 有无网络的提示 1 h m
    2016-02-20 web view
    20160220 下拉刷新 上拉加载
  • 原文地址:https://www.cnblogs.com/Chary/p/15609104.html
Copyright © 2011-2022 走看看