逆向笔记(新人发帖,大佬轻点喷)

查看 13|回复 2
作者:awsbaiek   
逆向笔记(1)———寄存器与汇编指令
寄存器
定义:CPU内部的储存元件,包括通用寄存器,专用寄存器和控制寄存器(每个CPU内核都有对应的一套寄存器)
CPU缓存:寄存器,一级缓存,二级缓存(寄存器用来暂时储存数据而缓存用来储存CPU常用指令)
速度:寄存器>一>二     大小:寄存器
PS:这里来科普一下,一个字节(Byte)由8个二进制位(bit)组成,每个八进制数字可以表示3个二进制位,一个十进制数字(0-9)需要用 4个二进制位 来表示,每个十六进制数表示4个二进制位,即一个字节可以表示两个十六进制数(4个二进制位可以表示的范围是 2**4(16) 个值,但2的3次方不够表示十进制数字,所以十进制也是4个)
寄存器类别
在CPU中至少要有六类寄存器:指令寄存器(IR)程序计数器(PC)地址寄存器(AR)数据寄存器(DR)累加寄存器(AC)程序状态字寄存器(PSW)。这些寄存器用来暂存一个计算机字,其数目可以根据需要进行扩充。
x86寄存器一般以E开头
x64寄存器一般以R开头
通用寄存器
EAX:累加器,用于加乘
ECX:循环计数
EDX:存放整数除法的余数
EBX:在内存寻址时存放基地址
ESP:保存当前栈顶的下一个地址(但通常被说指向栈顶)
EBP:保存当前栈底的地址,还可以加上一个偏移量从而访问栈内数据(如数组)
ESI:在字符串操作时指向源字符串
EDI:在字符串操作时指向目标字符串
(在储存偏移量时优先这两个寄存器)
EIP:存放下一条要执行的指令地址
主要用途
1. EAX
  • 主要用途
  • 函数返回值:通常用于存储函数返回的值(如int或指针)。
  • 算术运算:参与乘法(MUL)、除法(DIV)等操作,保存结果。
  • 系统调用号:在Linux系统调用中,EAX存储调用号(如sys_write)。


    2. EBX
  • 主要用途
  • 基址寄存器:在内存寻址中作为基地址(如[ebx + offset])。
  • Linux系统调用:存储系统调用的第一个参数(如文件描述符)。
  • 全局数据指针:某些编译约定用它保存全局变量地址。


    3. ECX
  • 主要用途
  • 循环计数器:在LOOP指令或显式循环中存储计数(如for (int i=0; ...))。
  • this指针:在C++成员函数中,存储指向当前对象的指针(this)。
  • 字符串/内存操作:与REP前缀指令配合(如REP MOVSB)。


    4. EDX
  • 主要用途
  • 辅助算术寄存器:在乘除法中配合EAX(如MUL结果的高32位存EDX)。
  • 系统调用参数:Linux系统调用中存储第二个参数(如write的缓冲区地址)。
  • I/O端口操作:与IN/OUT指令配合使用。


    5. ESI
  • 主要用途
  • 源数据指针:在字符串/内存操作中(如MOVSB)指向源地址。
  • 数组遍历:作为源数组的索引。


    6. EDI
  • 主要用途
  • 目标数据指针:在字符串/内存操作中(如MOVSB)指向目标地址。
  • 数组/结构体操作:存储写入的目标地址。


    7. ESP
  • 主要用途
  • 栈顶指针:始终指向当前栈的顶部,用于PUSH/POP操作。
  • 函数调用:保存返回地址、局部变量和参数。


    8. EBP
  • 主要用途
  • 栈帧基址:在函数中标记栈帧的起始位置(如[ebp + 8]访问参数)。
  • 调试支持:方便回溯调用链(编译器优化时可能被省略)。


    特殊指令依赖:如CPUID依赖EAX,RDRAND结果存EAX等
    AX: | 15 - 8  | 7 - 0  |
           AH       AL
    •   AX(16 位):EAX 的低 16 位。
    •   AH(8 位):AX 的高 8 位。
    •   AL(8 位):AX 的低 8 位。
    EAX:  | 31 - 16 | 15 - 8 | 7 - 0 |
              AH       AL  
             (AX)
    •   EAX(全 32 位):完整寄存器,包含所有 32 位。
    •   AX(低 16 位):EAX 的低 16 位,包含 AH 和 AL。
    •   AH(高 8 位):AX 的高 8 位。
    •   AL(低 8 位):AX 的低 8 位。
    RAX: | 63 - 32 | 31 - 16 | 15 - 8 | 7 - 0 |
                       EAX       AH      AL
                        |         |
                       AX (16位)
    •    RAX(64 位):完整寄存器,包含所有 64 位数据。
    •    EAX(32 位):RAX 的低 32 位。
    •    AX(16 位):EAX 的低 16 位。
    •    AH(8 位):AX 的高 8 位。
    •    AL(8 位):AX 的低 8 位。

    ![](D:\ctf笔记\图片\屏幕截图 2025-03-14 205407.png)
    高八位与低八位:高八位和低八位是指在计算机中,一个16位的数或值用8位表示高位,8位表示低位。例如,对于一个16位的内存地址,前8位是高八位,后8位是低八位(在平时使用时一般都是低位用起来方便,因为有专门的名称,如果想要使用高八位则要使用移位的方式)(high与low)
    大端规则与小端规则:大端规则就是把数值的高位字节放在内存的低位地址上,把数值的地位字节放在内存的高位地址上,而小端规则把数值的高位字节放在内存的低位地址上,把数值的地位字节放在内存的高位地址上。(不管是大端法还是小端法存储,计算机在内存中存放数据的顺序都是从低地址到高地址,所不同的是首先取低字节的数据存放在低地址还是取高字节数据存放在低地址,也就是说大端法或小端法是数据在存储时的表现,而不是在寄存器中参与运算时的表现)
    打个比方:
    假设我们要存储数字 0x12345678,这个数字是 4 个字节的(不够四个则自动补0),分别是:
  • 12(最高字节)
  • 34
  • 56
  • 78(最低字节)

    在小端法下存储(低字节在前):
    复制代码地址: 0x100   0x101   0x102   0x103
    数据:  78      56      34      12
    在大端法下存储(高字节在前):
    复制代码地址: 0x100   0x101   0x102   0x103
    数据:  12      34      56      78
    标志寄存器(EFLAG)
    大小:标志寄存器占两个字节,x64虽然是64位的,但也实际只占两个字节
    作用:
    •用来储存相关指令的某些执行结果
    •用来控制CPU的相关工作方式
    •用来为CPU执行相关指令提供行为依据
    种类:
    状态标志(CF/ZF/SF/PF/OF/AF):记录程序运行结果
    控制标志(DF/IF/TF):控制执行指令的方式
    ==每一个寄存器都有全部的标志寄存器==
    CF:进位标志(有进位或借位时为1)(例如1111 1111 + 1 = 1 0000 0000 但是会发生数据截断所以这个1存储到CF中,数据变为0000 0000,没有截断/溢出CF不会发生改变)(WORD操作时好像不会触发)
    ZF:零标志(结果为0时为1)
    SF:结果最高位为1,SF为1(二进制数据的符号位)
    PF:奇偶标志(结果的最低有效字节(十六进制数的后两位)中1的个数为偶数时为1)
    OF:溢出标志(结果有溢出时为1)(CF针对无符号整数,OF针对有符号整数)(OF = CF XOR MSB[最高有效位])
    (正数+正数=负数 -> 发生溢出 ; 负数+负数=正数 -> 发生溢出 ; 正数+负数=任意 -> 不会发生溢出)
    AF:辅助进位标志(WORD操作时是低字节向高字节进/借位为1,DWORD操作时低八位向高八位进/借位为1)
    ![](D:\ctf笔记\图片\屏幕截图 2025-03-17 203645.png)
    图中三种情况时第一个与第二个数相加然后得出第三个数时AF的反应:
    反映了一个事情WORD与DWORD的判断逻辑有相同的地方,如果在低八位的低字节的两个十六进制数(前四个bit与后四个bit)没有发生进位,那么AF不会发生变化
    DF:方向标志(DF=1,地址由高向低,DF=0,地址由低向高)
    IF:中断允许标志(IF=1,CPU可以响应中断申请,IF=0则不能)
    TF:TF=1,CPU每执行一条指令产生一个单步中断请求

    ![](D:\ctf笔记\图片\屏幕截图 2025-03-17 200101.png)            
    翻译:
  • ID 标志(ID)
  • 虚拟中断挂起(VIP)
  • 虚拟中断标志(VIF)
  • 对齐检查 / 访问控制(AC)
  • 虚拟 8086 模式(VM) or  保护模式
  • 恢复标志(RF)
  • 嵌套任务(NT)
  • I/O 特权级(IOPL)
  • 溢出标志(OF)
  • 方向标志(DF)
  • 中断使能标志(IF)
  • 陷阱标志(TF)
  • 符号标志(SF)
  • 零标志(ZF)
  • 辅助进位标志(AF)
  • 奇偶标志(PF)
  • 进位标志(CF)

  • S 表示状态标志
  • C 表示控制标志
  • X 表示系统标志
  • 保留的位位置。不要使用。
  • 始终设置为先前读取的值。


    转换时十六进制数可以直接写成二进制数(就是把二进制数每四个隔一段就是一个十六进制数字)
    指令指针寄存器
    指令指针寄存器  ----  EIP
    EIP存放下一条要执行的指令地址
    段寄存器
    段:内存中一段连续区域用于储存特定的数据或代码
    CS:代码段寄存器
    DS:数据段寄存器
    ES:附加段寄存器
    FS:FS寄存器主要用于指向当前活动线程的TEB结构(线程环境块),在内核态(R0)和用户态(R3)下的指向有所不同(线程进入内核之后就会指向KPCR)
    SS,SP,BP:栈段寄存器(SS储存基地址,SP储存及地址相对于栈顶的偏移量,BP与SP类似,访问时SS+SP(BP)即可得到目标地址)
    ==通常默认的数据段寄存器为DS,但在进行字符串操作时变为ES==
    段选择子与段描述符
    段基地址:是在计算机的内存管理体系中,表示一个段(Segment)在内存中的起始位置。它是由段寄存器(如 CS, DS, SS 等)提供的值,定义了该段的开始位置。段基地址是整个段的起始地址,从这个地址开始,偏移地址(Offset)可以用来访问该段内的具体数据。(但是在 x86 架构中的实模式下,段寄存器存储的是段基地址的“段基值”,它表示的是段在内存中的位置,单位是 16 字节。因此,我们需要将段寄存器中的值乘以 16(物理地址的计算公式为:物理地址=段地址×16+偏移地址物理地址=段地址×16+偏移地址)(别问,问就是历史遗留问题),才能得到段的实际物理起始地址,但是的但是在64位计算机中已经采用虚拟内存不需要将段寄存器的值乘以 16 来计算物理地址)(这些都是段模式的问题,在页模式并没有这个东西)
    举例说明
    假设 DS 寄存器的值为 0x1000,而要访问数据的偏移地址为 0x200。则物理地址为:
    物理地址 = DS × 16 + 偏移地址
             = 0x1000 × 16 + 0x200
             = 0x10000 + 0x200
             = 0x10200
    使用示例
    MOV AX, DS:[0x200]  ; 从 DS 指向的段的偏移地址 0x200 处读取数据到 AX
    MOV DS:[0x200], AX  ; 将 AX 寄存器的值写入 DS 指向的段的偏移地址 0x200
    MOV DS:[eax],AL     ; 将eax的值作为一个偏移地址
    CS:IP寄存器
    CS:IP 两个寄存器指示了 CPU 当前将要读取的指令的地址,其中 CS 为代码段寄存器,而  IP 为指令指针寄存器
    什么叫做指示了 CPU 当前将要读取的指令呢?在 8086 CPU 中,为什么 CPU 会自动的执行指令呢?
    这些指令肯定是存放在内存中的,但是 CPU 怎么知道这些指令存放在内存的那个位置呢?
    比如,我有下面的两条指令要执行:
        MOV AX,1234H
        MOV BX,AX
    而假设这两条指令在内存中存放为:

    很显然, 1000H:0000H 指向的是 MOV AX,1234H 的首地址,
    如果 CPU 要读取到我的指令的话,很显然,必须要知道地址 1000H:0000H ,
    然后 CPU 就可以根据这个首地址,将汇编指令 MOV AX,1234H 所对应的机器码读入到 CPU 的指令寄存器中
    最后便可以在 CPU 中进行处理了。
    但关键是  CPU 如何知道我的 1000H:0000H 这个首地址?其实这就需要使用到 CS:IP 这个寄存器组了 。
    当我们运行一个可执行文件时,很明显,我们需要另外一个程序来将这个可执行文件加载到内存当中,
    关于这个加载可执行文件的程序,我们在这里不管他,点一下即可,
    一般是通过操作系统的外壳程序(也就是传说中的 Shell 程序),
    Shell 将可执行文件加载到内存中以后,就会设置 CPU 中的两个寄存器,**即设置 CS:IP 两个寄存器指向可执行文件的起始地址,此后 CPU 便从这个起始地址开始读取内存中的指令,并且执行。**
    比如我们在写汇编程序时,通常会使用 START 标记,其实这个标记就是用来标记起始地址的,
    当将一个汇编程序编译,连接成可执行文件以后,再通过操作系统的 Shell 程序将可执行文件加载到内存中以后,
    这个 START 所标记处的地址就是整个可执行文件的起始地址了 。
    也就是说,当一个可执行文件加载到内存中以后,CS:IP 两个寄存器便指向了这个可执行文件的起始地址,
    然后 CPU 就可以从这个起始地址开始往下读取指令,
    当读取完指令后,CS:IP 将会自动的改变,基本上是改变 IP ,从而指向下一条要读取的指令,这样就可以执行这个可执行文件了 。
    最后再对 CS:IP 总结一下
    [ol]
  • 你想让 CPU 执行哪行指令,你就让 CS:IP 指向保存有指令的那块内存即可。
  • 任何时候,CS:IP 指向的地址中的内容都是 CPU 当前执行的指令。
    [/ol]
    指令
    指令=操作码+操作数
    指令和数据的寻址方式
    寻址方式:确定本条指令的数据地址或下一条要执行的指令地址的方法。

    汇编代码":"的含义:
    DS:偏移地址  
    数据寻址方式说明
    目的操作数和源操作数均可采用不同的寻址方式(你可以让源和目的分别来自不同的地方源可以是内存、寄存器、立即数……目的可以是寄存器、内存……)
    两个操作数的类型必须一致。
    AX表示16位寄存器,AH、AL表示其高低字节,AX 可以拆分成两个 8 位寄存器 AH 和 AL 单独使用。
    立即寻址
    操作数就在指令中,紧跟在操作码后面,作为指令一部分存放在内存的代码段中,该操作数为立即数,这种寻址方式称为立即寻址方式。数据通常采用补码的形式存放。常用于给寄存器赋初值(作用);
    注意
    ①立即数可以送到寄存器、一个存储单元(8位)、两个连续的存储单元(16位)中去;
    ②立即数只能作源操作数,不能作目的操作数;
    ③以A~F打头的数字,前面必须加数字0。
    如下指令
    MOV AL,3400H ×,前后格式不匹配
    MOV AX,34H √,等价于 MOV AX,0034H
    MOV 87H,BX ×,立即数不能作为目的操作数
    MOV AX,F98AH ×,应为 MOV AX,0F98AH
    设16位计算机中,存储器宽度为16位,一条指令占据一个机器字。
    已知操作码为6位,目的操作数为寄存器编号,占4位;
    源操作数寻址方式为 立即数寻址 ,以补码整数形式给出,
    则该指令立即数寻址方式的数值范围是多少?

    操作码占6位,目的操作数据占4位,那么立即数(源操作数)占6位,按补码的形式,第一位为符号位,所以数值范围为 -32~31 。
    立即寻址方式的
    优点:指令已经提供操作数,无需再次访问存储器。提供操作数最快。
    缺点
    ①操作数为指令一部分,不能修改,适用于给某一寄存器或存储单元赋初值等操作。
    ②指令中A的位数限制了这类指令所表述的立即数的范围。、
    直接寻址
    存储单元有效地址EA(即:操作数的有效地址)直接由指令给出。
    作用:实现对存储单元的读/写操作。
    特点:
    访问存储器次数较少;
    操作数地址在指令中,灵活性较差。
    操作数A的位数限制了操作数的寻址范围
    (存储器)间接寻址
    操作数所在内存单元的地址通过存储器间接给出。
    优点:实现简便,对编程带来较大的灵活性,当操作数地址改变时,只需修改间接地址指示器的单元内容,而不必修改指令,原指令的功能照样实现。这给编程带来很大方便。
    多次访问内存,增加了指令的执行时间;占用主存储器单元多。
    寄存器寻址
    操作数包含在寄存器中,寄存器的名称由指令指定。
    常用于寄存器之间传递数据。优势是速度快。注意源操作数的长度必须与目的操作数一致
    寄存器间接寻址
    操作数所在内存单元的地址通过寄存器间接给出。
    作用:有效地址可以存放在寄存器中。
    相对地址
    操作数的有效地址是程序计数器PC的内容与n位位移量
    位移量的概念:在寄存器间接寻址给出的偏移地址上,加上一个相对偏移量。位移量是一个带符号的补码机器数。
    可有效缩短地址的长度 。
    兼具灵活性,只与PC相对位置有关,与绝对地址无关。
    相对基址寻址
    这种寻址方式下,EA是由两部分组成的,基址寄存器BX或BP的内容加上偏移量DISP。(EA=BX(BP)+DISP(偏移量))
    相对变址寻址
    这种寻址方式下,EA是由两部分组成的,变址寄存器的内容加上偏移量DISP。
    与基址寻址类似。基址寻址,往往变化的是形式地址;变址寻址,往往形式地址不变,变化的是变址寄存器。(EA=SI(DI)+DISP(偏移量))
    相对基址变址寻址
    这种寻址方式下,EA是由三部分组成的,基址寄存器BX或BP的内容加上变址寄存器的内容,以及位移量的和。(EA=BX(BP)+SI(DI)+DISP(偏移量))
    常用操作指令
    数据传输指令
    mov
    mov指令:最常见的数据传输指令,相当于赋值操作(当操作数中有内存地址时需要加[])(AX ,CX ,DX ,SP不能用来指定内存地址)
    mov  目的操作数,源操作数
    mov可以实现寄存器与寄存器之间,寄存器与内存之间,寄存器与立即数之间,内存立即数之间的内存传递,但内存与内存之间是无法直接传输数据的,目的操作数不能为立即数,且两个操作数的宽度一致
    在对于地址赋值时可以通过BYTE,WORD,DWORD来控制取多少字节(例如MOV EAX,DWORD PTR DS:[0x12345678] 就是从12345678开始到1234567B的数据存储到EAX)

    movzx
    较小数据单位的数据移动到较大数据单位的寄存器中,并且移动的时候将高位设置为0,但是它只适用于无符号整数
    大致有以下三种格式
    movzx 32位通用寄存器, 8位通用寄存器/内存单元
    movzx 32位通用寄存器, 16位通用寄存器/内存单元
    movzx 16位通用寄存器, 8位通用寄存器/内存单元
    xchg
    xchg指令:交换两个操作数据
    xchg  目的操作数,源操作数
    同样不允许内存与内存交换数据
    lea
    LEA指令用于计算地址,将有效地址加载到寄存器中,不会访问内存。
    LEA 目标寄存器, [源操作数]
    目标寄存器:存放计算出的地址。
    源操作数:通常是一个内存地址表达式(如偏移量或基址+索引)
    标志位操作指令(CLC,STC,CLD,STD,CLI,STI,CMC)
    [table]
    [tr]
    指令[/td]
    全称[/td]
    功能[/td]
    [/tr]
    [tr]
    [td]CLC

    寄存器, 指令

  • 一场荒唐半生梦   

    你小子能成事 ,关注了。到时候记得分享些实用软件类
    zhangzhibo139   

    支持,支持。
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部