【问题标题】:db instruction it is executed after calldb 指令在调用后执行
【发布时间】:2017-02-17 07:24:08
【问题描述】:

我正在学习用汇编语言编程,我发现这段代码我无法理解指令是如何执行的

xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
jmp short string
code:
pop ecx 
mov bl,1
mov dl,13
mov al,4
int 0x80
dec bl
mov al,1
int 0x80
string: 
call code 
db 'hello, world!'

在调用代码之后,为什么要执行 db 指令?如果调用指令在它之前执行

【问题讨论】:

  • 没有db“指令”。 db 是对汇编器的一个指令,告诉它将一个或多个字节放入您的二进制文件(即您的可执行文件/目标文件)中。
  • 你认为它为什么会被执行? call 将 CPU 重定向到地址 code.. 从那里没有重定向回 call 之后的代码。 (提示:call用于将字符串的地址存储到栈顶,而不是“调用子程序”)
  • 指令为何运行?调用后不应该去代码
  • call 将返回地址存储在堆栈中,在这种情况下恰好是字符串'hello, world!' 的地址。然后将该地址弹出到ecx(这是系统调用4的字符串指针参数)。就像我在之前的评论中所说的那样,这是混淆代码,初学者尝试从这样的代码中学习是没有意义的。
  • 您知道您用于汇编程序的教程是针对 shellcode 和漏洞利用的吗?如果您不编写 shellcode 并进行漏洞利用,那么不同的教程可能会更好。如果你不做 shellcode,那么我的新标签应该从问题中删除。

标签: assembly x86 nasm shellcode


【解决方案1】:

只是为了指出我所说的“以其他方式定义字节值”的意思,您的代码的这个变体将做同样的事情,但它显示了如何通过指令定义字符串,以及如何通过db 指令定义指令...两者都使人类更难阅读源代码,但是对于汇编程序而言,差异可以忽略不计,它将产生相同的二进制机器代码,而对于 CPU,相同的机器代码是相同的机器代码,它不在乎您的来源确实看过。

我还尝试广泛地注释每一行、它的作用以及在代码中使用它的原因。

代码也是以这种非平凡的方式编写的,因为它是 shell-exploit 有效负载的示例,您的程序集不仅必须执行您想要的操作,而且其生成的机器代码还必须符合其他约束,例如不能包含任何零(使得在使用某些漏洞注入有效负载代码期间难以将其作为“字符串”传递),它必须是 PIC(与位置无关的代码),并且它不能使用任何绝对地址,或者在执行时担任任何特定位置等。

    ; sets basic registers eax,ebx,ecx,edx to zero (ecx not needed BTW)
    xor eax,eax
    db '1', 0xDB        ; xor ebx,ebx defined by "db" for fun
    db '1', 0xC9        ; xor ecx,ecx defined by "db" for fun
    xor edx,edx
    ; short-jump forward to make later "call code" to produce
    ; negative relative offset, so zero in "call" opcode is avoided
    ; "call code" from here would need zeroes in rel32 offset encoding
    jmp short string    ; the "jmp short string" is encoded as "EB 0F"
code:
    pop ecx             ; loads the address of string from the stack into ecx
    mov bl,1            ; ebx = 1 = STD_OUT stream, avoiding zeroes in
        ; "mov ebx,1" opcode, so instead "xor ebx,ebx mov bl,1" is used
    mov dl,13           ; edx = 13 = length of string
    mov al,4            ; eax = 4 = sys_write
    int 0x80            ; sys_write(STD_OUT, 'hello, world!', 13);
    dec bl              ; ebx = 0 = exit code "OK"
    mov al,1            ; eax = 1 = sys_exit
    int 0x80            ; sys_exit(0);
string:
    call code           ; return address == string address -> pushed on stack
    ; also "code:" is ahead, so relative offset is negative => no zero in opcode
    ; resulting call opcode is "E8 EC FF FF FF"

    ; following bytes are NOT executed as code, they contain string data
    push 0x6f6c6c65     ; 'hello'
    sub al,0x20         ; ', '
    ja  short $+0x6f+2  ; 'wo'
    jb  short $+0x6c+2  ; 'rl'
    db 'd!'

为了进行编译,我确实使用了nasm -f elf *.asm; ld -m elf_i386 -s -o demo *.o(忽略警告),以向后反编译并检查实际机器代码是如何形成指令的,您可以应用objdump -M intel -d demo

(上面的代码和objdump 也适用于在线网站:http://www.tutorialspoint.com/compile_assembly_online.php,如果你想测试一下)

【讨论】:

    【解决方案2】:

    字符串永远不会被执行,因为您在到达该点之前发出了exit_program 系统调用。

    这是正常代码的样子:

    asm
    asm
    more asm
    call subroutine   ->> will branch to subroutine
            subroutine:  asm
                         more asm
                         ret   ->> executing will return to point after call.
    xor eax,eax          <<-- first instruction after the ret
    ret                  <<-- return to caller.
    

    这是code 子例程的注释版本。

    code:
    pop ecx        ;get 'hello world'
    mov bl,1       ;write to stdout
    mov dl,13      ;length is 14
    mov al,4       ;syscall write
    int 0x80       ;perform the write
    dec bl         ;set exit code to 0 {success}
    mov al,1       ;syscall exit
    int 0x80       ;exit the program
    ;the executing does not return here!
    string: 
    call code 
    db 'hello, world!'
    

    更好的方法是将最后一部分编码如下:

    .code
    hello: db 'hello, world!'
    
    .text
    move ecx,hello
    mov edx,13
    call print         //returns as normal
    call exit_program  //will not return
    
    print: mov eax,4
    mov ebx,1
    int 0x80
    ret
    
    exit_program: 
    xor ebx,ebx
    mov eax,1
    int 0x80
    

    这有以下好处:

    • 指令较少;
    • 它不会因调用/返回不匹配而导致缓慢
    • 它不会弄乱字节寄存器
    • 它简单易懂。
    • 您可以在程序的其他部分重用printexit_program 子例程。

    【讨论】:

    • JFYI:我,问题的原始代码是shell-exploit-code,因此它在代码中包含数据,仅使用相对寻址并且是“PIC” , 并避免在生成的机器代码中出现零字节(您的 mov edx,13 在编译后将包含零,这使得它不适合作为有效负载字符串),因此这很可能是该代码以这种“混淆”方式编写的原因。跨度>
    猜你喜欢
    • 1970-01-01
    • 2020-04-16
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    • 2010-10-16
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多