学习笔记(逆向汇编)Day1-Day5

Day 1 《进制》

#

(一)进制意义:

1.定义:
  二进制:0 1                 逢2进1
  八进制:0 1 2 3 4 5 6 7            逢8进1
  十进制:0 1 2 3 4 5 6 7 8 9         逢10进1
  十六进制:0 1 2 3 4 5 6 7 8 9 A B C D E F   逢16进1

 当然,除了常规的,还能3进制、4进制…..甚至,进制的符号不一定是常规的123的顺序;
 我们可以这样定义一个5进制: 9 7 5 3 1 逢5进1; 则该进制的79 = 6(10进制),57 = 11(10进制),39 = 15(10进制);
 (如果将上面的符号用于加密,将会给解密者带来极大的麻烦!!!)
  

2.二进制与十六进制:
  计算机中的任何信息都是以二进制的形式存储的。
  由于二进制书写复杂,大部分软件都将计算机中的数据以16进制展示。
  任何 1 个十六进制的符号都有一个对应的 4 个二进制符号。
  且16进制是2进制的简写,1个16进制数 == 4个2进制数:
  二进制:  0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111
  十六进制: 0 1 2  3  4  5  6  7  8   9   A   B  C   D   E   F
  

3.练习:
  a. 2+3 = 1,成立吗?说明理由:
    答:成立,假如定义一个4进制数 4 2 3 1 5 ,逢5进1,则4 = 0(10进制),2 = 1(10进制),3 = 2(10进制),1 = 3(10进制);
      那么2+3=1 => 1+2=3;

  b. 将下面的二进制数用16进制表示:
        1100 1011 0101 0100 1110 1011 0101 0111 1011 0100 1010 1011
    答:   C   B  5   4   E  B   5   7  B   8   A  B 

  c. 将下面的十六进制数用2进制数表示:
       4  8  7  F  D  C  1  2  0  A  C  E  6  9  B  9  5  3  F  E
    答:0100 1000 0111 1111 1101 1100  0001 0010 0000 1010 1100 1110 0110 1001 1011 1001 0101 0011 1111 1110

  d.二进制从0写到100(是100个数,每行写10个)
    答: 1、 10、 11、 100、 101、 110、 111、 1000、 1001、 1010
      1011、 1100、 1101、 1110、 1111、 10000、 10001、10010、10011、10100
      10101、10110、10111、11000、11001、11010、11011、11100、11101、11110
      11111、100000、100001、100010、100011、100100、100101、100110、100111、101000
      101001、101010、101011、101100、101101、101110、101111、110000、110001、110010
      110011、110100、110101、110110、110111、111000、111001、111010、111011、111100
      111101、111110、111111、1000000、1000001、1000010、1000011、1000100、1000101、1000110
      1000111、1001000、1001001、1001010、1001011、1001100、1001101、1001110、1001111、1010000
      1010001、1010010、1010011、1010100、1010101、1010110、1010111、1011000、1011001、1011010
      1011011、1011100、1011101、1011110、1011111、1100000、1100001、1100010、1100011、1100100

(二)进制运算法:

1.八进制:
  加法表:
   1+1=2 1+2=3 1+3=4 1+4=5 1+5=6   1+6=7   1+7=10
       2+2=4 2+3=5 2+4=6 2+5=7   2+6=10  2+7=11
           3+3=6 3+4=7 3+5=10 3+6=11  3+7=12
                4+4=10 4+5=11 4+6=12 4+7=13
                   5+5=12 5+6=13 5+7=14
                         6+6=14 6+7=15
                             7+7=16
  乘法表:
   1*1=1  1*2=2  1*3=3  1*4=4  1*5=5  1*6=6  1*7=7
         2*2=4  2*3=6  2*4=10   2*5=12   2*6=14  2*7=16
            3*3=11   3*4=14   3*5=17   3*6=22  3*7=25
                   4*4=20   4*5=24   4*6=30  4*7=34
                        5*5=31   5*6=36  5*7=43
                             6*6=44 6*7=52
                                   7*7=61
2.加减乘除法则:



3.二进制:

4.练习:
  1. 9进制定义:由9个符号组成,分别是2、9、1、7、6、5、4、8、3,逢9进1;
   计算: 123+234 = 725;
  2. 10进制定义:由10个符号组成,分别是:! @    @ $B + %AC& = &!@%;
  3. 3进制定义:由3个符号组成,分别是2、0、1,逢3进1;
   2  0  1
   02  00   01
   12  10   11
   022
   计算:12 + 02 = 022;
  4. 7进制的定义:由7个符号组成,分别是:8、3、4、2、9、5、6,逢7进1;
   8  3  4  2  9  5  6
   38  33  34  32  39   35   36
   48  43  44  42  49   45   46
   28  23  24  22  29   25   26
   98  93  94  92  99   95   96
   58  53  54  52  59   55   56
   388 383
   计算:92 + 39 = 68;


2017/5/10 17:37:35

Day 2  《数据宽度&逻辑运算》

#

(一)数据宽度:

1.数据宽度意义:

  • a.计算机中,受制于硬件条件,数据都是有长度的,超过最大宽度的数据会被丢失;

  • b.其中重要的有这几种数据宽度,类型为:
                         BYTE  字节   8bit 1字节
                         WORD  字   16bit 2字节
                         DWORD 双字 32bit 4字节

  • c.数据类型图解:
      

2.逻辑运算:
  

  • a.或运算(or |)  只要有一个为1,则为1; 0101 | 0001 = 0101;

  • b.与运算(and &) 必须要两个为1,才为1; 0101 & 0001 = 0001;

  • c.异或(xor ^)    两者互为不同,才为1; 0101 ^ 0001 = 0100;

  • d.非(not !)    1是0,0是1; !0101 = 1010; !0001 = 1110;

3.cpu的加法计算:

4.简单加密与解密:

5.本节作业:

  1. 八进制数2-5在计算器中的结果是:177777777777777777777777775,为什么?
       答:

  2. 使用异或对87AD6进行加密后再进行解密,加密密钥:5;
       答:

  3. 只用逻辑运算计算 2-3 = ?(涉及内容:逻辑运算、移位、数据宽度)
       答:

6.寄存器


2017/5/11 16:11:16

Day 3  《寄存器》

(一)通用寄存器

1.含义:
  通用寄存器是寄存器中的一种,有些寄存器是专用的,而没有指定专门用途的寄存器,就是通用寄存器;
  汇编就是:不停的移动数据,在内存和寄存器之间,寄存器和寄存器之间,移动得越复杂,程序越复杂。如果弄清楚这些数据的流动,就可以知道程序的流程,逆向就是要知道数据如何移动的;
2.32位寄存器分类:

(二)寄存器操作语法

1.MOV 目标操作数, 源操作数
作用:拷贝源操作数到目标操作数

#include <stdio.h>

int main()
{
   unsigned char m8 = 0;
   short m16 = 0;
   int m32 = 0;

  //r:通用寄存器    m:内存    imm:立即数    r8:8位通用寄存器    m8:8位内存    imm8:8位立即数
   __asm {
      mov ah, 0x12                              //MOV r8, imm8
      mov ax, 0xabcf                            //MOV r16, imm16
      mov eax, 0x13579BDF                       //MOV r32, imm32

      mov al,ah                                 //MOV r/m8, r8
      mov m8,ah
      mov bx,ax                                 //MOV r/m16, r16
      mov m16,ax
      mov ebx,eax                               //MOV r/m32, r32
      mov m32,eax

      mov bl,al                                 //MOV r8, r/m8
      mov al,m8
      mov bx,ax                                 //MOV r16, r/m16
      mov ax,m16
      mov ebx,eax                               //MOV r32, r/m32
      mov eax,m32
   }
   return 0;
}  

1.源操作数可以是立即数、通用寄存器、段寄存器、内存单元
2.目标操作数可以是通用寄存器、段寄存器或者内存单元
3.操作数的宽度必须一样
4.源操作数和目标操作数不能同时为内存单元

2.ADD 目标操作数, 源操作数
作用:将源操作数值加到目标操作数上

#include <stdio.h>

int main()
{
   unsigned char m8 = 5;
   short m16 = 4;
   int m32 = 3;

   //r:通用寄存器    m:内存    imm:立即数    r8:8位通用寄存器    m8:8位内存    imm8:8位立即数
   __asm {
      add al, 0x10                          //ADD r/m8, imm8
      add m8, al
      add ax, 0x1000                        //ADD r/m16, imm16
      add m16, ax
      add eax, 0x10000000                   //ADD r/m32, imm32
      add m32, eax

      add ax, 0x10                          //ADD r/m16, imm8
      add m16, 0x10
      add eax, 0x10                         //ADD r/m32, imm8
      add m32, 0x10

      add ah, al                            //ADD r/m8, r8
      add m8, al
      add bx, ax                            //ADD r/m16, r16
      add m16, ax
      add ebx, eax                          //ADD r/m32, r32
      add m32, eax

      add bh, m8                            //ADD r8, r/m8
      add bx, m16                           //ADD r16, r/m16
      add ebx, m32                          //ADD r32, r/m32
   }
   return 0;
}

1.目标操作数的宽度和源操作数的宽度必须一样,当源操作数为立即数时,可不一致。
2.立即数作为源操作数,可以比目标数小,也可以比目标数大。超出的位丢失,缺少的位补0。

3.SUB 目标操作数, 源操作数
作用:目标操作数减去源操作数

#include <stdio.h>

int main()
{
   unsigned char m8 = 5;
   short m16 = 4;
   int m32 = 3;

   //r:通用寄存器    m:内存    imm:立即数    r8:8位通用寄存器    m8:8位内存    imm8:8位立即数
   __asm {
      sub al, 0x10                          //SUB r/m8, imm8
      sub m8, al
      sub ax, 0x1000                        //SUB r/m16, imm16
      sub m16, ax
      sub eax, 0x10000000                   //SUB r/m32, imm32
      sub m32, eax

      sub ax, 0x10                          //SUB r/m16, imm8
      sub m16, 0x10
      sub eax, 0x10                         //SUB r/m32, imm8
      sub m32, 0x10

      sub ah, al                            //SUB r/m8, r8
      sub m8, al
      sub bx, ax                            //SUB r/m16, r16
      sub m16, ax
      sub ebx, eax                          //SUB r/m32, r32
      sub m32, eax

      sub bh, m8                            //SUB r8, r/m8
      sub bx, m16                           //SUB r16, r/m16
      sub ebx, m32                          //SUB r32, r/m32
       }
   return 0;
}

1.目标操作数与源操作数的宽度一定要一致,立即数除外;
2.立即数作为源操作数,可以比目标数小,也可以比目标数大。超出的位丢失,缺少的位补0;

4.AND 目标操作数, 源操作数
作用:位运算&,将目标操作数和源操作数进行&运算,并将值存给目标操作数;

#include <stdio.h>

int main()
{
   unsigned char m8 = 5;
   short m16 = 4;
   int m32 = 3;

   //r:通用寄存器    m:内存    imm:立即数    r8:8位通用寄存器    m8:8位内存    imm8:8位立即数
   __asm {
      mov eax,0xFFFFFFFF    

      and al, 0x11                          //AND r/m8, imm8
      and m8, al
      and ax, 0x1111                        //AND r/m16, imm16
      and m16, ax
      and eax, 0x11111111               //AND r/m32, imm32
      and m32, eax

      /*源操作数为立即数时,其宽度可大于可小于目标操作数的宽度*/
      and ax, 0x10                          //AND r/m16, imm8
      and m16, 0x10
      and eax, 0x10                         //AND r/m32, imm8
      and m32, 0x10

      and ah, al                                //AND r/m8, r8
      and m8, al
      and bx, ax                                //AND r/m16, r16
      and m16, ax
      and ebx, eax                          //AND r/m32, r32
      and m32, eax

      and bh, m8                            //AND r8, r/m8
      and bx, m16                           //AND r16, r/m16
      and ebx, m32                          //AND r32, r/m32
     }
   return 0;
}

5.OR 目标操作数, 源操作数
作用:位运算|,将目标操作数和源操作数进行 | 运算,并将值存给目标操作数;

#include <stdio.h>

int main()
{
   unsigned char m8 = 5;
   short m16 = 4;
   int m32 = 3;

   //r:通用寄存器    m:内存    imm:立即数    r8:8位通用寄存器    m8:8位内存    imm8:8位立即数
   __asm {
     mov eax, 0x00000000

     or al, 0x11                            //OR r/m8, imm8
     or m8, al
     or ax, 0x1111                      //OR r/m16, imm16
     or m16, ax
     or eax, 0x11111111         //OR r/m32, imm32
     or m32, eax

     /*源操作数为立即数时,其宽度可大于可小于目标操作数的宽度*/
     or ax, 0x10                            //OR r/m16, imm8
     or m16, 0x10
     or eax, 0x10                       //OR r/m32, imm8
     or m32, 0x10
     or ax, 0x1000
     or m8, 0x1000

     or ah, al                              //OR r/m8, r8
     or m8, al
     or bx, ax                          //OR r/m16, r16
     or m16, ax
     or ebx, eax                            //OR r/m32, r32
     or m32, eax

     or bh, m8                          //OR r8, r/m8
     or bx, m16                         //OR r16, r/m16
     or ebx, m32                        //OR r32, r/m32
   }
   return 0;
} 

6.XOR 目标操作数, 源操作数
作用:位运算^,将目标操作数和源操作数进行 ^ 运算,并将值存给目标操作数;

#include <stdio.h>

int main()
{
   unsigned char m8 = 5;
   short m16 = 4;
   int m32 = 3;

   //r:通用寄存器    m:内存    imm:立即数    r8:8位通用寄存器    m8:8位内存    imm8:8位立即数
   __asm {
     mov eax, 0x00000000

     xor al, 0x11                           //XOR r/m8, imm8
     xor m8, al
     xor ax, 0x1111                         //XOR r/m16, imm16
     xor m16, ax
     xor eax, 0x11111111                    //XOR r/m32, imm32
     xor m32, eax

     /*源操作数为立即数时,其宽度可大于可小于目标操作数的宽度*/
     xor ax, 0x10                           //XOR r/m16, imm8
     xor m16, 0x10
     xor eax, 0x10                          //XOR r/m32, imm8
     xor m32, 0x10
     xor ax, 0x1000
     xor m8, 0x1000

     xor ah, al                             //XOR r/m8, r8
     xor m8, al
     xor bx, ax                             //XOR r/m16, r16
     xor m16, ax
     xor ebx, eax                           //XOR r/m32, r32
     xor m32, eax

     xor bh, m8                             //XOR r8, r/m8
     xor bx, m16                            //XOR r16, r/m16
     xor ebx, m32                           //XOR r32, r/m32
   }
   return 0;
}

7.NOT 目标操作数
作用:对目标操作数进行取反

#include <stdio.h>

int main()
{
   unsigned char m8 = 5;
   short m16 = 4;
   int m32 = 3;

   __asm {
      mov eax, 0x00000000

      not al                //NOT r/m8
      not m8
      not ax                //NOT r/m16
      not m16
      not eax               //NOT r/m32
      not m32
   }
   return 0;
}
(三)寄存器的知识

1.寄存器与内存的区别

  1. 寄存器位于CPU内部,执行速度快,但成本高;
  2. 内存速度相对较慢,但成本低,可以大量使用;
  3. 寄存器和内存并无本质区别,都是用于存储数据的容器,都是定宽的;
  4. 基层其常用的有8个:EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI;
  5. 计算机中的几个常用计量单位:BYTE、WORD、DWORD;

2.内存(这里并不是指磁盘/硬盘的存储大小)

  1. 计算机说的32位指的是寻址编号,即寻址宽度。每一个编号表示一个字节。0xFFFFFFFF+1=100000000,这是计算机存储的最大的量,其10进制数:4294967296字节,即4G。(32位系统可以打补丁编程,64位的识别更多内存);
  2. 内存的数量特别庞大,无法为每个单元都起一个名字,因此用编号来代替。说32位计算机是因为其寄存器是32位的,但并不准确,还有很多寄存器是大于32位的。

3.内存格式:


1. 每一块内存单元都有一个唯一的编号,每个内存单元的宽度为8bit,即1个字节;
2. [编号]称为地址;
3. 地址的作用:当我们想从内存中读取数据或者想向内存中写入数据时需要用到;

4.从指定内存中写入/读取数据:

#include <stdio.h>

int main()
{   
   __asm {
      mov eax,esp
      mov dword ptr ds:[eax],0xabcdabcd             //使用esp的值作为地址
      mov eax,dword ptr ds:[eax]
   }

   __asm {
      mov dword ptr ds:[0x012FF9AC],0xabcdabcd      //此处报错,无法写入
      mov eax,dword ptr ds:[0x012FF9AC]             
   }
   return 0;
}

dword: 表示要读/写多少宽度,这里是4字节;
ptr: 表示后面是一个指针(即里面存的是一个地址,不是一个普通的数值);
ds: 数据段寄存器;
0x012FF9AC: 内存编号(必须是32位的),前面0可以省略;
注意:地址编号不要随便写,因为内存是有保护的,并不是所有内存都可以直接读写(需特别处理),可以使用esp中的数据作内存使用。


2017/5/12 19:01:39

Day 4  《内存地址和堆栈》

(一)数据存储与寻址

(1)数据窗口与堆栈窗口
  

上图为数据窗口,在vs中看不到堆栈窗口。
0x006FF890内存单元对应的是20这块内存,从左往右依次是0x006FF891、0x006FF892…0x006FF89F;
堆栈窗口可从VTDeBug软件中查看,其对应的内存单元正好是相反的,要从右往左看;

(2)寻址公式

#include<stdio.h>

int main()
{
   unsigned char m8 = 8;
   short m16 = 16;
   int m32 = 32;

   __asm {

      mov ebx, 0x10

      //读取内存的值
      //mov eax, dword ptr ds : [0xA7777]                 //[立即数]
      mov eax, dword ptr ds : [esp]                       //[reg]
      mov eax, dword ptr ds : [esp + 0x10]                //[reg + 立即数]
      mov eax, dword ptr ds : [esp + ebx*2]               //[reg + reg*{1,2,3,8}]
      mov eax, dword ptr ds : [esp + ebx*2 + 0x10]        //[reg + reg*{1,2,3,8} + 立即数]

      //向内存中写入数据
      //mov dword ptr ds : [0xA7777], 0x11111111          //[立即数]
      mov dword ptr ds : [esp], 0x12345678                //[reg]
      mov dword ptr ds : [esp + 0x10], 0x87654321         //[reg + 立即数]
      mov dword ptr ds : [esp + ebx*2], 0x99999999        //[reg + reg*{1,2,3,8}]
      mov dword ptr ds : [esp + ebx*2 + 0x10], 0x22222222 //[reg + reg*{1,2,3,8} + 立即数]

      //获取内存编号
      lea eax, dword ptr ds : [esp]
      lea eax, dword ptr ds : [esp + 10]
      lea eax, dword ptr ds : [esp + eax * 2]

   }
}

lea 和 mov的区别:lea是将源操作数的地址传递给目标操作数;mov是将源操作数的值传递给目标操作数;

(3)模拟堆栈

#include<stdio.h>

int main()
{
   unsigned char m8 = 8;
   short m16 = 16;
   int m32 = 32;

   __asm { 

      mov ebx, esp                              //BASE
      mov edx, esp                              //TOP

      //压入数据
      //方式1:
      mov dword ptr ds : [edx - 4], 0xaaaaaaaa  //压入数据,栈顶上移
      sub edx, 4

      //方式2:
      //sub edx,4
      //mov dword ptr ds:[edx],0xbbbbbbbb

      //方式3:
      //mov dword ptr ds:[edx-4],0xdddddddd
      // lea edx,dword ptr ds:[edx-4]

      //方式4:
      //lea edx,dword ptr ds:[edx-4]
      //mov dword ptr ds:[edx],0xeeeeeeee

      //读取数据
      mov esi, dword ptr ds : [ebx - 8]         //通过栈底偏移
      mov edi, dword ptr ds : [edx + 4]         //通过栈顶偏移

      //弹出数据
      //方式1:
      mov ecx, dword ptr ds : [edx]             //弹出数据,栈顶下移
      lea edx, dword ptr ds : [edx + 4]

      //方式2:
      //mov esi, dword ptr ds : [edx]
      //add edx, 4

      //方式3:
      //lea edx, dword ptr ds : [edx + 4]
      //mov edi, dword ptr ds : [edx - 4]


      push eax                                 //使用push,esp当栈顶,ebp当栈底
      push ax                                  //不能是8位寄存器
   }
}

(4)PUSH指令与POP指令

#include<stdio.h>

int main()
{
   unsigned char m8 = 8;
   short m16 = 16;
   int m32 = 32;

   //PUSH指令 & POP指令
   __asm {

      //PUSH r16/r32/m16/m32
      //POP r16/r32/m16/m32

      push m16  
      push m32
      push ax
      push ebx

      POP m32       //这里获取的是原ebx的值
      POP m16       //这里获取的是原ax的值
      POP ebx       //这里获取的是原m32的值
      POP ax        //这里获取的是原m16的值
   }
}

PUSH与POP指令,都会改变esp的值,即系统将esp当作栈顶;
PUSH与POP指令,都不能使用8位寄存器;
并不是每次使用都减4,而是根据压入和弹出的数据类型决定;

(5)PUSHAD指令预POPAD指令

#include<stdio.h>

int main()
{
   unsigned char m8 = 8;
   short m16 = 16;
   int m32 = 32;

   //PUSH指令 & POP指令
   __asm {

      pushad          //将8个通用寄存器的值存入栈中
      mov eax,0x0     
      mov ebx,0x0 
      popad           //复原8个通用寄存器的值

   }
}  

这里的pushad指令与popad指令都会改变栈顶esp的值;

(6)练习

int main()
{
   __asm {
      mov ebx, esp
      mov edx, esp
      //1.使用ebx存储栈底地址,edx存储栈顶地址,并连续存储5个不同的数
      lea edx, byte ptr ds : [edx - 1]
      mov byte ptr ds : [edx], 0x11
      lea edx, word ptr ds : [edx - 2]
      mov word ptr ds : [edx], 0x2222
      lea edx, dword ptr ds : [edx - 4]
      mov dword ptr ds : [edx], 0x33333333
      lea edx, dword ptr ds : [edx - 4]
      mov dword ptr ds : [edx], 0x44444444
      lea edx, dword ptr ds : [edx - 4]
      mov dword ptr ds : [edx], 0x55555555

      //2.分别使用栈底加偏移、栈顶加偏移的方式读取这5个数,并存到寄存器中  
      mov eax, dword ptr ds : [edx]                                   //eax:55555555
      mov eax, dword ptr ds : [edx + 4]                               //eax:44444444
      mov eax, dword ptr ds : [edx + 8]                               //eax:33333333
      mov al, byte ptr ds : [ebx - 1]                                 //eax:33333311
      mov ax, word ptr ds : [ebx - 3]                                 //eax:33332222

      //3.弹出这5个数,回复栈顶到原来的位置  
      mov eax, dword ptr ds : [edx]
      lea edx, dword ptr ds : [edx + 4]
      mov eax, dword ptr ds : [edx]
      lea edx, dword ptr ds : [edx + 4]
      mov eax, dword ptr ds : [edx]
      lea edx, dword ptr ds : [edx + 4]
      mov ax, word ptr ds : [edx]
      lea edx, word ptr ds : [edx + 2]
      mov al, byte ptr ds : [edx]
      lea edx, byte ptr ds : [edx + 1]

      //4.使用2种方式实现:push ecx
      lea esp, dword ptr ds : [esp - 4]
      mov dword ptr ds : [esp], ecx
      lea esp, [esp - 4]
      mov[esp], ecx

      //5.使用2种方式实现:pop ecx
      mov ecx, dword ptr ds : [esp]
      lea esp, dword ptr ds : [esp + 4]
      mov ecx, [esp]
      lea esp, [esp + 4]

      //6.使用2种不同方式实现:push esp             后面这两题不太懂!!!!
      lea esp, dword ptr ds : [esp - 4]
      mov dword ptr ds : [esp], esp
      lea esp, [esp - 4]
      mov[esp], esp

      //7.使用2种不同方式实现:pop esp  
      mov esp, dword ptr ds : [esp]
      lea esp, dword ptr ds : [esp + 4]
      mov esp, [esp]
      lea esp, [esp + 4]
   }
}

2017/5/14 3:09:55

Day 5  《标志寄存器》

(一)32位标志寄存器EFLAGS

  1. 标志寄存器用来保存在一条指令执行完成后,CPU所处状态的信息及运算结果的特征。
  2. 标志寄存器是有顺序的,必须记住。
(二)运算结果标志位

1.进位标志CF(CarryFlag):第0位
  如果运算结果的最高位产生了一个进位或者借位,那么其值为1,否则为0;

2.奇偶标志PF(ParityFlag):第2位
  用于反映运算结果中“1”的个数的奇偶性。如果“1”的个数为偶数,则PF=1,否则为0;

3.辅助进位标志AF(AuxiliaryCarryFlag):第4位
  在发生下列情况时,辅助位标识AF的值被置为1,否则其值为0:
  (1)在字操作时,发生低位字节向高位字节进位或借位时;
  (2)在字节操作时,发生低4位向高4位进位或借位时;

4.零标志ZF(ZeroFlag):第6位
  如果运算结果为0,则其值为1,否则为0;在判断运算结果是否为0时,可使用此标志位;

5.符号标志SF(SignFlag):第7位
  反映运算结果的符号位,它与运算结果的最高位相同;

__asm {
    xor eax, eax        //SF=0
    sub eax, 0x1        //SF=1
    add eax, 0x1        //SF=1
}

6.溢出标志OF(OverflowFlag):第11位
  反映有符号数加减运算所得结果是否溢出,如果运算结果超过当前运算位数所能表示范围,则称为溢出,OF=1,否则OF=0;

这里要注意: 与进位标志的区别;
最高位进位与溢出的区别:
 1.进位标志表示无符号数运算结果是否超出范围;溢出标志表示有符号数运算结果是否超出范围;
 2.正+正=正,如果结果是负数,则说明有溢出;负+负=负,如果结果是正数,说明有溢出;
 3.正+负永远不会有溢出;

CF位是我们做无符号运算时候需要关注的。有符号运算时我们要关注OF位;

unsigned short um16 = 0x8;
short m16 = 0x8;
__asm {
    //无符号不进位,有符号不溢出
    add um16, 8                     //cf=0
    add m16, 8                      //of=0

    //无符号进位、有符号不溢出
    mov um16, 0Xffff
    mov m16, 0xffff
    add um16, 2                     //cf=1
    add m16, 2                      //of=0

    //无符号不进位,有符号溢出
    mov um16,0x7FFF
    mov m16, 0x7FFF
    add um16, 2                     //cf=0
    add m16, 2                      //of=1

    //无符号进位、有符号溢出
    mov um16,0xFFFE
    mov m16,0xFFFE
    add um16,0x8000                 //cf=1
    add m16,0x8000                  //of=1

}  
(三)相关指令

1.ADC指令:带进位加法
格式: ADC R/M,R/M/IMM  两边不能同时为内存,宽度要一致

与ADD唯一的不同是,ADC执行加法运算时,会将CF位的值一起加到目标操作数中。
所以ADC常用于处理以下情况:
  如果必须处理非常大的、不能存放到双字数据长度(ADD可以使用的最大长度)中的整数,可以把值分割为多个双字数据元素,并且对每个元素执行独立的加法操作。
  为了正确完成这个操作,必须检测每个加法操作的进位标志,如果进位标志被设置为1,就必须进位到下一对相加的数据元素。

    short um16 = 1;
   __asm{
      mov al,1
      mov cl,2
      add um16,0xffff
      adc al,cl                 //al=4
   }  

2.SBB指令:带借位的减法
格式:SBB R/M,R/M/IMM 两边不能同时为内存,宽度要一致

unsigned short m16 = 0xFFFF;
   __asm {
      mov al,4
      mov cl,2
      add m16,1
      sbb al,cl         //al=1
   }

操作对象1=操作对象1-操作对象2-CF 其中CF为进位的值

3.XCHG指令:交换数据
格式:XCHG R/M,R/M 两边不能同时为内存,宽度要一致

__asm {
    xchg al,cl

    xchg dword ptr ds:[esp-4],eax
    xchg byte ptr ds:[esp-4-1],al
}  

4.MOVS指令:移动数据 内存-内存
注意:MOVS R/M,R/M 这个指令两边都可以是内存
MOVS指令是用来复制一个数据项(字节,字或双字)从源字符串到目标字符串。源字符串指出由DS:SI和ES:DI指向目标字符串。

movs 指令(串传送)(字符串操作指令)( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
这里MOVS拷贝4个字节的内容,即DWORD,另外一种书写形式为:MOVSD。
与之对应的还有拷贝两个字节的MOVSW指令和拷贝一个字节的MOVSB指令。
请注意ESI、EDI拷贝的方向,拷贝的方向取决于方向标志位D。

5.STOS指令:将AL/AX/EAX的值储存到[EDI]指定的内存单元

__asm {
      mov eax,0x12345678
      stos dword ptr es:[edi]   //edi 0x00DDF898  78 56 34 12 00 00 00 00
      stosw                     //edi 0x00DDF898  78 56 34 12 78 56 00 00
      stosb                     //edi 0x00DDF898  78 56 34 12 78 56 78 00
   }

我们可以使用REP前缀,还有每次操作两个字节的STOSW指令和每次操作一个字节的STOSB指令。

6.REF指令:按计数寄存器(ECX)中指定的次数重复执行字符串命令
__asm {
mov eax, 0x12345678

      mov ecx, 5
      //edi 0x008FFBF4  08 fc 8f 00 7e 1d b3 00
      rep stosb   //edi 0x008FFBF4  78 78 78 78 78 1d b3 00

      mov ecx,5 
      //esi 0x00E11046  e9 45 0d 00 00 e9 52 3a 00 00 
      //edi 0x00F1F769  1d e1 00 01 00 00 00 90 3e 1a
      rep movsw   //edi 0x00F1F769  e9 45 0d 00 00 e9 52 3a 00 00 
}

这里要注意,每次ref执行之后,ecx都会清零;

(四)练习
    char m8 = 8;
    unsigned char um8 = 88;
    short m16 = 16;
    unsigned short um16 = 1616;
    int m32 = 32;
    unsigned int um32 = 3232;
    __asm {
      //1.熟练记住CF/PF/AF/ZF/SF/OF  
      //2.写汇编指令,只影响CF位的值 (不改变其他标志位的值) 
      mov um8, 0xf0
      add um8, 0x11                                //EFL=203    = 0010 0000 0011
      mov um16, 0xf000
      add um16, 0x1001                             //EFL=203    = 0010 0000 0011
      mov um32, 0xf0000000
      add um32, 0x10000001                         //EFL=203    = 0010 0000 0011

      //3.写汇编指令,只影响PF位的值  
      mov um8, 0x01
      add um8, 0x1                                 //EFL=202 = 0010 0000 0010
      add um8, 0x1                                 //EFL=206 = 0010 0000 0110

      //4.写汇编指令,只影响AF位的值
      mov um8, 0x01
      add um8, 0x0f                                //EFL=212 = 0010 0001 0010 低4位向高4位进位,AF=1
      mov um16, 0xccfe
      add um16, 0x1110                             //EFL=280 = 0010 1000 0000   低字节向高字节进位,AF=0,这里我也不太明白,字操作时,明明第字节向高字节进位
      mov um32, 0xeeeeffee
      add um32, 0x00001001                         //EFL=282 = 0010 1000 0010 低字向高字进位,AF=0

      //5.写汇编指令,只影响SF位的值
      mov um8, 0x7F
      add um8, 0x10                                //EFL=A82 = 1001 1000 0010 最高为1,与SF位保持一致  

      //6.写汇编指令,只影响OF的值
      mov m8, 0x7F
      add m8, 0x10                                 //EFL=A82 = 1001 1000 0010 溢出了,OF=1

      //7.用MOVS指令分别移动5个字节、5个字、5个双字
      movs byte ptr es : [edi], byte ptr ds : [esi]
      movs word ptr es : [edi], word ptr ds : [esi]
      movs dword ptr es : [edi], dword ptr ds : [esi]
      movsb
      movsw
      movsd

      //8.用STOS指令分别存储5个字节、5个字、5个双字
      stos byte ptr es : [edi]
      stos word ptr es : [edi]
      stos dword ptr es : [edi]
      stosb
      stosw
      stosd

      //9.使用REP指令重写第7,8题  
      mov ecx,5
      rep movsb
      mov ecx,5
      rep movsw
      mov ecx,5
      rep movsd

      mov ecx,5
      rep stosb
      mov ecx,5
      rep stosw
      mov ecx,5
      rep stosd
   }

2017/5/14 12:17:24

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值