• 反汇编分析寄存器状态


    一直对反汇编分析代码有兴趣,但是一直没静下心来研究一下,昨天腾讯笔试,有一题就是考察大端小端模式(见下面C代码段),涉及到函数调用,大端小端模式前些时间复习过,但是即便如此,做的时候还是感觉思路不清,回头想想,一是对函数调用中栈的变化过程不甚了解,一是对long long结构不了解,而如果我知道如何反汇编的话,可以直接在调试过程中解开疑虑,这样就不用盲目地在网上搜集了。因此我决定这些天弄一弄反汇编。

    在VS2008中反汇编很容易,进入debug模式之后,在代码区域“右键->反汇编” 或者“调试->窗口->反汇编” 就可以看到程序对应的汇编代码,同样的方式“调试->窗口->寄存器” 就可以查看寄存器的状态。

    /*-----------------lp横刀立马------------------------------
    -----------------腾讯2013春季实习生笔试4.13---------------------
    题目描述:代码如下,win32 环境,CPU小端模式,参数用栈来传递,请问输出--
    
    */
    #include <stdio.h>
    
    int main()
    {
        long long a=1;
        long long b=2;
        long long c=3;
    
        printf("%d%d%d",a,b,c);
    }

    VS2008反汇编代码如下:

    #include <stdio.h>
    
    int main()
    {
    00D813B0  push        ebp  
    00D813B1  mov         ebp,esp 
    00D813B3  sub         esp,0F0h 
    00D813B9  push        ebx  
    00D813BA  push        esi  
    00D813BB  push        edi  
    00D813BC  lea         edi,[ebp-0F0h] 
    00D813C2  mov         ecx,3Ch 
    00D813C7  mov         eax,0CCCCCCCCh 
    00D813CC  rep stos    dword ptr es:[edi] 
    /* 说明: rep 表示重复,重复的次数由ecx决定,在执行这一句话的过程中,ecx自动减小直至为零,这也是为什么ecx叫做计数器
    另外,stos表示将edi指向的内容写成eax中的值,rep过程中edi自动增加,从这个行为也可以感受到为什么edi叫做目标变址寄存器*/
    long long a=
    1; 00D813CE mov dword ptr [a],1 00D813D5 mov dword ptr [ebp-8],0
    /* long long 类型占8个字节,而双字是4个字节,所以要分两次写入,可以看出,栈顶在低地址,栈底在高地址,push操作对应着esp的减小 */
    long long b=
    2; 00D813DC mov dword ptr [b],2 00D813E3 mov dword ptr [ebp-18h],0
    /*为啥这里是ebp-18h?不是只需要8个字节就够了吗?*/ long long c=
    3; 00D813EA mov dword ptr [c],3 00D813F1 mov dword ptr [ebp-28h],0 printf("%d%d%d",a,b,c); 00D813F8 mov esi,esp 00D813FA mov eax,dword ptr [ebp-28h] 00D813FD push eax 00D813FE mov ecx,dword ptr [c] 00D81401 push ecx 00D81402 mov edx,dword ptr [ebp-18h] 00D81405 push edx 00D81406 mov eax,dword ptr [b] 00D81409 push eax 00D8140A mov ecx,dword ptr [ebp-8] 00D8140D push ecx 00D8140E mov edx,dword ptr [a] 00D81411 push edx 00D81412 push offset string "%d%d%d" (0D8573Ch) 00D81417 call dword ptr [__imp__printf (0D882BCh)] 00D8141D add esp,1Ch 00D81420 cmp esi,esp 00D81422 call @ILT+310(__RTC_CheckEsp) (0D8113Bh) } 00D81427 xor eax,eax 00D81429 pop edi 00D8142A pop esi 00D8142B pop ebx 00D8142C add esp,0F0h 00D81432 cmp ebp,esp 00D81434 call @ILT+310(__RTC_CheckEsp) (0D8113Bh) 00D81439 mov esp,ebp 00D8143B pop ebp 00D8143C ret

    反汇编的过程如果不懂每个寄存器分别是干啥的,无异于没学单词就开始念英文,那么今天就存寄存器开始吧。先进行一个简要的概述,然后挨个分析。

    1.寄存器组概述

    数据寄存器:保存操作数和运算结果等信息。
        EAX:Accumulator。函数返回值。取低16位为AX,分割为8位寄存器AH-AL。指令ret返回用到。
        EBX:Base Register。
        ECX:Count Register。
        EDX:Data Register。

    指针寄存器:EBP,ESP可作为通用寄存器,即可存储算术逻辑运算的操作数和运算结果。
        EBP:Base Pointer,基指针寄存器,直接访问栈中的数据。
        ESP:Stack Pointer,栈指针寄存器,只可访问栈顶。指令pop/push时自动变。
        EIP:Instruction Pointer,指令指针寄存器,存放下次将要执行的指令在代码段中的偏移量。每走一条指令自动变一次,如果希望跳转后能返回继续就需要跳转前把它放入栈中,返回时出栈。

    变址寄存器:主要用于存放存储单元在段内的偏移量。
        ESI: Source Index,源变址寄存器。EDS:ESI即源串段寄存器:源串变址,ESI在串操作中自动增减。
        EDI:Destination Index,目标变址寄存器。EES:EDI即目标串段寄存器:目标串变址,EDI在串操作中自动增减。

    段寄存器:内存分段。这里最多为6个内存段,不同的内存段放入不同的东西。
        ECS:Code Segment Register,代码段寄存器。
        EDS:Data Segment Register,数据段寄存器。
        EES:Extra Segment Register,附加段寄存器。
        ESS:Stack Segment Register,栈段寄存器。
        EFS:Extra Segment Register,附加段寄存器。
        EGS:Extra Segment Register,附加段寄存器。

    2.EBP 和 ESP

    最简单的说法是:EBP 指向栈底 ,ESP指向栈顶。

    分析一段简短的c代码

    void Layer02()
    {
           int b = 2;
    }
     
    void Layer01()
    {
           int a = 1;
           Layer02();
    }

    其反汇编代码如下:

    void Layer02()
    {
    00413700 push        ebp 
    00413701 mov         ebp,esp
    00413703 sub         esp,0CCh
    00413709 push        ebx 
    0041370A push        esi 
    0041370B push        edi 
    0041370C lea         edi,[ebp-0CCh]
    00413712 mov         ecx,33h
    00413717 mov         eax,0CCCCCCCCh
    0041371C rep stos    dword ptr es:[edi]
           int b = 2;
    0041371E mov         dword ptr [b],2
    }
    00413725 pop         edi 
    00413726 pop         esi 
    00413727 pop         ebx 
    00413728 mov         esp,ebp
    0041372A pop         ebp 
    0041372B ret

    如上,可以看到,这几段函数调用反汇编代码的头两句都是一样的,调用时先保存栈底指针,在将栈底指针设为栈顶指针,此时的栈底就为被调用函数的栈的栈底。

    push ebp
    mov ebp ,esp

    结尾也都是一样的,一旦调用完毕,栈底栈顶都恢复到调用之前的状态。

    mov esp,ebp
    pop ebp
    ret

    上面代码是参考solidMango的博客 ,里面的配图简单形象,可以参考。

    3.EDI ,ESI 

    ESI/EDI 分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.

    4.EAX,EBX,ECX,EDX

    EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。

    EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。

    ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。

    EDX 则总是被用来放整数除法产生的余数。

     经过这么一分析,可以知道,这一题的结果实际为102

    参考网址:博客园--石头儿   C++内嵌汇编 

  • 相关阅读:
    Nginx教程(三) Nginx日志管理
    Nginx教程(二) Nginx虚拟主机配置
    官方解析Cookies和Session的区别
    J2EE十三个技术规范
    J2EE十三个规范小结
    tomcat -web.xml里的内容
    tcp协议和udp协议的使用场景
    IntelliJ IDEA创建maven web项目(IDEA新手适用)
    Maven安装与配置
    X86、X64和X86_64区别
  • 原文地址:https://www.cnblogs.com/obama/p/3020507.html
Copyright © 2020-2023  润新知