我正在学习shellcode。
我在教程中找到了这个shellcode:
python -c 'print "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80 "' > shellcode
我想要做的是反汇编这个非常基本的shellcode,以了解它是如何工作的。
这就是我所做的:
$ objdump -D -b binary -m i8086 shellcode
shellcode: file format binary
Disassembly of section .data:
00000000 <.data>:
0: 90 nop
1: 90 nop
2: 90 nop
3: 90 nop
4: 90 nop
5: 90 nop
6: 90 nop
7: 90 nop
8: 90 nop
9: 31 c0 xor %ax,%ax
b: 50 push %ax
c: 68 2f 2f push $0x2f2f
f: 73 68 jae 0x79
11: 68 2f 62 push $0x622f
14: 69 6e 89 e3 50 imul $0x50e3,-0x77(%bp),%bp
19: 53 push %bx
1a: 89 e1 mov %sp,%cx
1c: b0 0b mov $0xb,%al
1e: cd 80 int $0x80
或者:
$ ndisasm shellcode
00000000 90 nop
00000001 90 nop
00000002 90 nop
00000003 90 nop
00000004 90 nop
00000005 90 nop
00000006 90 nop
00000007 90 nop
00000008 90 nop
00000009 31C0 xor ax,ax
0000000B 50 push ax
0000000C 682F2F push word 0x2f2f
0000000F 7368 jnc 0x79
00000011 682F62 push word 0x622f
00000014 696E89E350 imul bp,[bp-0x77],word 0x50e3
00000019 53 push bx
0000001A 89E1 mov cx,sp
0000001C B00B mov al,0xb
0000001E CD80 int 0x80
此shellcode包含被解释为x86指令的字符串。 有没有办法在跳跃上放置适当的标签?
有没有办法显示字符串而不是解码字符串上的x86指令。我知道这并不容易,因为没有带有章节和标题的精灵......
答案 0 :(得分:0)
这是冯·诺依曼架构的结果。代码和数据只是计算机内存中的数字。因此,反汇编程序无法知道(没有关于字节序列的任何先验信息)什么是代码以及什么是数据。意思是,你必须手动完成。
幸运的是,这很容易做到。只需用nop(\ x90)替换字符串数据,然后再次对其进行反汇编。然后,您可以通过替换nop区域将字符串数据放回源代码中。
还要确保使用正确的目标CPU进行反汇编。我认为这个shellcode不太可能适用于16位8086 CPU。
答案 1 :(得分:0)
如果你有使用call
或jmp
来跳过某些数据的shellcode,那么如果反汇编程序在将数据视为同步时失去同步,则必须用NOP替换字符串说明,正如@DavidJ建议的那样。
在这种情况下,您只是以错误的模式进行反汇编。
jnc
显然是假的(我认为你意识到)。
反汇编程序将push
opcode(0x68
字节)视为push imm16
的开头,因为这是16位模式的工作原理。但在32位和64位模式下,相同的操作码是push imm32
的开头。因此push
指令实际上是5个字节而不是3个,而下一个指令实际上是下一个push
。
伪造短 - jnc
是一个巨大的暗示,这是不是 16位代码。
使用ndisasm -b32
或-b64
。 Ndisasm可以读取stdin的输入,因此我使用了python2 -c 'print "... "' | ndisasm - -b32
。
使用objdump
时,如果您更喜欢英特尔语法,请使用objdump -d -Mintel
。所以你可以 objdump -Mintel -bbinary -D -mi386 /tmp/shellcode
32位(-mi386
选择x86作为架构(而不是ARM或MIPS或其他),并暗示-Mi386
32位模式也是如此)。
或者对于64位,objdump -D -b binary -mi386 -Mx86-64 /tmp/shellcode
有效。 (objdump
无法从stdin中读取二进制文件:/)有关objdump
选项的更多信息,请查看-M
手册页。
我在~/.bashrc
:alias disas='objdump -drwC -Mintel'
中使用此别名,因为我通常反汇编ELF可执行文件/对象以查看编译器执行的操作,而不是shellcode。你的别名可能需要-D
。
我非常确定这是32位代码,因为在64位模式下,两次推送会留下空隙。不是push imm64
,但push imm32
是64位推送,立即符号扩展为64位。在64位模式下,您可以使用
push 'abcd'
mov [rsp+4], 'efgh'
以rsp指向"abcdefgh"
结束。
此外,使用带有堆栈地址的int 0x80
是一个很大的线索,这不是64位代码。 int 0x80
在64位模式下适用于Linux,但它会将所有输入截断为32位:What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
ndisasm的32位反汇编是:
00000000 90 nop
00000001 90 nop
00000002 90 nop
00000003 90 nop
00000004 90 nop
00000005 90 nop
00000006 90 nop
00000007 90 nop
00000008 90 nop
00000009 31C0 xor eax,eax
0000000B 50 push eax
0000000C 682F2F7368 push dword 0x68732f2f
00000011 682F62696E push dword 0x6e69622f
00000016 89E3 mov ebx,esp
00000018 50 push eax
00000019 53 push ebx
0000001A 89E1 mov ecx,esp
0000001C B00B mov al,0xb
0000001E CD80 int 0x80
00000020 200A and [edx],cl
看起来很健全。它不包含分支,但
有没有办法在跳跃上放置适当的标签?
是的,Agner Fog的objconv
反汇编程序可以在分支目标上放置标签,以帮助您确定哪个分支在哪里。
见How do I disassemble raw x86 code?