V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Aruforce
V2EX  ›  程序员

问个汇编的问题?原谅我半路出家

  •  
  •   Aruforce · 2019-06-10 18:55:38 +08:00 · 2453 次点击
    这是一个创建于 1995 天前的主题,其中的信息可能已经有所发展或是发生改变。

    32 bit

    _load_gdtr:		; void load_gdtr(int limit, int addr);
    	MOV		AX,[ESP+4]		; limit,
    	MOV		[ESP+6], AX		;
    	LGDT	[ESP+6]
    	RET
    

    load_gdtr ( 0xffff,0x00270000 )

    压栈后 字节序:FFFF 0000 0000 2700

    经过 两次 MOV 操作后 字节序是 FFFF 00FF FF00 2700 ?

    但是 别人告诉我是 FFFF FFFF 0000 2700

    我哪里理解错了么? 我应该补充哪面的知识?

    16 条回复    2019-06-11 16:11:09 +08:00
    goodleixiao
        1
    goodleixiao  
       2019-06-10 19:38:33 +08:00
    你去看看 gdt 内存分配,他的地址是非连续的
    Aruforce
        2
    Aruforce  
    OP
       2019-06-10 22:00:30 +08:00
    @goodleixiao 我不是要从 GDT 里面读取数据。。而是用 LGDT 来生成内存分段表...
    lcdtyph
        3
    lcdtyph  
       2019-06-10 22:19:56 +08:00
    假设是小端机器

    进入函数之后 ESP 指向返回地址,从 ESP+4 开始栈上数据分布如下:
    +4 +5 +6
    ESP+4: FF FF 00 00 00 00 27 00
    ESP+C: .........

    MOV AX, [ESP+4] 之后 AX=FFFF
    MOV [ESP+6], AX 之后即是从上面 ESP+6 开始写入 16bit 数据,结果就是
    +4 +5 +6
    ESP+4: FF FF FF FF 00 00 27 00
    lcdtyph
        4
    lcdtyph  
       2019-06-10 22:23:13 +08:00 via iPhone
    前导空格被吃了…凑合看吧……
    co3site
        5
    co3site  
       2019-06-10 22:46:21 +08:00 via Android
    细心点,一个字节 8 位,2 个十六进制位,挨个挨个数就不会错了
    Aruforce
        6
    Aruforce  
    OP
       2019-06-11 09:30:11 +08:00
    @lcdtyph 你好,请问能个给个图片么? 我还是没看明白 ESP 到底开始指向了哪里? 谢谢
    Aruforce
        7
    Aruforce  
    OP
       2019-06-11 09:38:46 +08:00
    SakuraKuma
        8
    SakuraKuma  
       2019-06-11 09:50:19 +08:00
    题目的话,重点其实就是,细心点。

    esp 的话可以看一下函数调用约定。
    x1314aq
        9
    x1314aq  
       2019-06-11 10:38:48 +08:00
    @Aruforce esp 最开始指向 caller 的返回地址;这是 x86 的函数调用约定,函数参数从右往左依次入栈,如果是用 call 指令进行调用的话,还会把返回地址入栈;因此当_load_gdtr()开始执行的时候,栈是这样一个排布:
    (小端序)
    +0 +4 +5 +6 +7
    ret_addr 0xff 0xff 0x00 0x00 0x00 0x00 0x27 0x00
    ^
    |
    esp
    Aruforce
        10
    Aruforce  
    OP
       2019-06-11 11:00:09 +08:00
    我大概明白了...在函数入栈的时候
    ```asm
    push ebp ;32 位 CPU...占用 4 个字节
    mov ebp,esp ; ebp 存储 调用方的栈顶
    ...
    mov esp, ebp ;恢复调用方栈顶
    pop ebp ;从 stack 顶端 读取 4 个 byte 到 EBP,也就是调用方的元 EBP
    ret

    ```

    也就是说 ESP+4 只是跳过了调用方的 EBP 的值,而如果是 16 或者 32 的 CPU 的话就是 ESP+2 或者 ESP+8 了对么?

    @lcdtyph @x1314aq
    Aruforce
        11
    Aruforce  
    OP
       2019-06-11 11:01:24 +08:00
    拼写错误 fix: 16 或者 64 CPU 的时候 是 ESP+2 或者 ESP+8
    lcdtyph
        12
    lcdtyph  
       2019-06-11 11:33:08 +08:00 via iPhone
    @Aruforce
    你可能还是没太明白,在题目里这个例子里没有 ebp 的事情。这个函数太简单了甚至都没有开辟栈帧。

    1. esp 永远指向栈顶的第一个元素
    2. 指令 call _load_gdtr 可以理解成两步:先 push eip,再 jmp _load_gdtr。push eip 之后栈顶就是返回地址了
    3. 进入函数后 esp+4 是为了跳过返回地址。

    如果开辟了栈帧那么有两种本地变量的寻址方式,分别是 esp 寻址和 ebp 寻址。其中比较简单的就是 ebp 寻址,在你 10 层的回复中 push ebp; mov ebp,esp 之后 ebp 指向栈上的那个旧的 ebp,此时 ebp+4 指向返回地址,esp+8 才指向第一个参数。你自己把栈图画出来就明白了
    Aruforce
        13
    Aruforce  
    OP
       2019-06-11 14:35:25 +08:00
    @lcdtyph

    ! [1] ( https://imgur.com/WJWbglI)

    你好这样理解 正确么?
    lcdtyph
        14
    lcdtyph  
       2019-06-11 14:58:31 +08:00
    @Aruforce #13
    栈里数据的图是对的,但是“ call 指令”那里的操作不太对
    1. 函数参数的压栈不是“ call 指令”来做的,需要 caller 自己做。
    2. EIP 入栈之后还有一步跳转操作,根据不同类型的 call 会有不同的动作,不过结果都是使 EIP 指向函数的入口
    3. push ebp 以及之后的操作都不是“ call 指令”的行为,是 callee 自己的行为
    Aruforce
        15
    Aruforce  
    OP
       2019-06-11 15:14:04 +08:00
    @lcdtyph 意思是 CALL 只是执行了 修改 EIP,EIP 入栈,还有 JMP 这一组操作?
    lcdtyph
        16
    lcdtyph  
       2019-06-11 16:11:09 +08:00 via iPhone   ❤️ 1
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1023 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:45 · PVG 04:45 · LAX 12:45 · JFK 15:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.