只是为了指出我所说的“以其他方式定义字节值”的意思,您的代码的这个变体将做同样的事情,但它显示了如何通过指令定义字符串,以及如何通过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,如果你想测试一下)