Assembly 如何重新编程外壳代码片段以避免空字节?

Assembly 如何重新编程外壳代码片段以避免空字节?,assembly,x86-64,nasm,shellcode,Assembly,X86 64,Nasm,Shellcode,我已经编写了一个x64linux程序集。它所做的只是打印一行“你好,世界”,仅此而已。然而,我想做的是通过objdump从它的目标文件复制字节,这样我就可以为缓冲区溢出攻击生成自己的外壳代码 我面临的问题是外壳代码包含大量空字节,这将终止外壳代码的执行 root@kali:~/C scripts/shellcode/Assembly Based Shellcode# cat print.asm section .text global _start _start: mov r

我已经编写了一个x64linux程序集。它所做的只是打印一行“你好,世界”,仅此而已。然而,我想做的是通过objdump从它的目标文件复制字节,这样我就可以为缓冲区溢出攻击生成自己的外壳代码

我面临的问题是外壳代码包含大量空字节,这将终止外壳代码的执行

root@kali:~/C scripts/shellcode/Assembly Based Shellcode# cat print.asm
section .text
 
global _start
 
_start:
 
    mov rax, 1
    mov rdi, 1
    mov rsi, message
    mov rdx, 12
    syscall
 
    mov rax, 60
    xor rdi, rdi
    syscall
 
message:
    db "Hello world", 10
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# nasm -f elf64 print.asm && ld print.o -o print && ./print
Hello world
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# objdump -D print.o
 
print.o:     file format elf64-x86-64
 
 
Disassembly of section .text:
 
0000000000000000 <_start>:
   0:   b8 01 00 00 00          mov    $0x1,%eax
   5:   bf 01 00 00 00          mov    $0x1,%edi
   a:   48 be 00 00 00 00 00    movabs $0x0,%rsi
  11:   00 00 00
  14:   ba 0c 00 00 00          mov    $0xc,%edx
  19:   0f 05                   syscall
  1b:   b8 3c 00 00 00          mov    $0x3c,%eax
  20:   48 31 ff                xor    %rdi,%rdi
  23:   0f 05                   syscall
 
0000000000000025 <message>:
  25:   48                      rex.W
  26:   65 6c                   gs insb (%dx),%es:(%rdi)
  28:   6c                      insb   (%dx),%es:(%rdi)
  29:   6f                      outsl  %ds:(%rsi),(%dx)
  2a:   20 77 6f                and    %dh,0x6f(%rdi)
  2d:   72 6c                   jb     9b <message+0x76>
  2f:   64                      fs
  30:   0a                      .byte 0xa
root@kali:~/C scripts/shellcode/Assembly Based Shellcode#
root@kali:~/C scripts/shell代码/基于程序集的shell代码#cat print.asm
第节.案文
全球启动
_开始:
莫夫·拉克斯,1
mov rdi,1
移动rsi,消息
mov rdx,12
系统调用
莫夫·拉克斯,60岁
异或rdi,rdi
系统调用
信息:
db“你好,世界”,10
root@kali:~/C scripts/shellcode/Assembly-Based外壳代码#nasm-f elf64 print.asm和ld print.o-o print和&./print
你好,世界
root@kali:~/C scripts/shellcode/Assembly-Based外壳代码#objdump-D print.o
print.o:文件格式elf64-x86-64
第节的分解。正文:
0000000000000000 :
0:b8 01 00 mov$0x1,%eax
5:bf 01 00 mov$0x1,%edi
a:48 be 00 movabs$0x0,%rsi
11:   00 00 00
14:ba 0c 00 mov$0xc,%edx
19:0f 05系统调用
1b:b8 3c 00 mov$0x3c,%eax
20:48 31 ff异或%rdi,%rdi
23:0f 05系统调用
0000000000000025 :
25:48雷克斯西
26:65 6c gs insb(%dx),%es:(%rdi)
28:6c insb(%dx),%es:(%rdi)
29:6f输出%ds:(%rsi),(%dx)
2a:2077 6f和%dh,0x6f(%rdi)
2d:72 6c jb 9b
2f:64英尺
30:0a.字节0xa
root@kali:~/C脚本/外壳代码/基于程序集的外壳代码#
我希望外壳代码没有空字节。但事实并非如此。
有人能帮我更正我的代码吗?

您似乎对程序集和缓冲区溢出感到困惑

我对程序集文件进行了如下重新编程:

section .text

GLOBAL _start

_start:

    xor rax, rax                  ; Clear the RAX register
    push rax                      ; Push the NULL byte [ string terminator ]
    add al, 0x1                   ; RAX = 1, to put the system in sys_write mode
    mov rdi, rax                  ; RDI = 1, to setup the fist parameter for write ( file descriptor to write to ). The integral value for 'stdout' is 1.
    lea rsi, [rel msg+0x41414141] ; Move the relative RIP address of msg to RSI to prepare the string buffer for writing to the stdout. Also add a large 4-byte offset to evade NULL bytes.
    sub rsi, 0x41414141           ; Subtract that large offset to make the RSI point correctly.
    xor rdx, rdx                  ; Empty the 3rd argument for write
    mov dl, 0xc                   ; RDX = 12, 12 ==> string length of msg
    syscall                       ; system call

msg db "Hello world", 0xa
root@kali:~/Desktop/assembly# nasm -f elf64 main.asm; ld main.o -o main.elf; ./main.elf 
Hello world
Segmentation fault
编辑:根据评论部分的讨论,我删除了终止的空字节 然后我把程序编译成这样:

section .text

GLOBAL _start

_start:

    xor rax, rax                  ; Clear the RAX register
    push rax                      ; Push the NULL byte [ string terminator ]
    add al, 0x1                   ; RAX = 1, to put the system in sys_write mode
    mov rdi, rax                  ; RDI = 1, to setup the fist parameter for write ( file descriptor to write to ). The integral value for 'stdout' is 1.
    lea rsi, [rel msg+0x41414141] ; Move the relative RIP address of msg to RSI to prepare the string buffer for writing to the stdout. Also add a large 4-byte offset to evade NULL bytes.
    sub rsi, 0x41414141           ; Subtract that large offset to make the RSI point correctly.
    xor rdx, rdx                  ; Empty the 3rd argument for write
    mov dl, 0xc                   ; RDX = 12, 12 ==> string length of msg
    syscall                       ; system call

msg db "Hello world", 0xa
root@kali:~/Desktop/assembly# nasm -f elf64 main.asm; ld main.o -o main.elf; ./main.elf 
Hello world
Segmentation fault
seg故障实际上并不重要,因为您希望将其用作缓冲区溢出攻击的外壳代码,因此无论如何都存在seg故障

现在,从目标代码中提取字节:

root@kali:~/Desktop/assembly# for i in $(objdump -D main.o | grep "^ " | cut -f2); do echo -n "\x$i"; done; echo
\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a
[选项]:您可以在内存损坏攻击中执行外壳代码之前测试外壳代码

为此,只需复制上述命令中的字节,并创建一个新的C文件[似乎您对C脚本也感到困惑]

#include <stdio.h>

int main(void) {

    char shellcode[] = "\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a";
    int (*ret)() = (int (*)())shellcode;
    // The above line will create an integer pointer ret make it point to a function which doesn't require parameter [ indicated by the () ]. Then it will type casting to cast the shellcode to a function pointer of the same type.
    // So this will essentially cast your shellcode array address to a function pointer which you can later use to call it as a function and execute the code.
    ret(); // Execute the shellcode

}

从上面的代码来看,外壳代码似乎工作得很好

我已经在一个基本的应用程序上尝试过了,它很有效,所以你的问题应该得到解决

需要提及的另一点是,只有当您的可执行应用程序[您将要利用的应用程序]使用停止在空字节(如
strcpy()
)上的输入方法时,才需要删除空字节

如果您的可执行文件使用诸如
gets()
fgets()
之类的输入函数,那么您就不必担心空字节[除非您也希望看到格式字符串漏洞] 这来自fgets的手册页:

fgets()从流中最多读入一个小于大小的字符,并将它们存储到s指向的缓冲区中。EOF或换行后,读取停止。如果换行符是 读取时,将其存储到缓冲区中。终止的空字节('\0')存储在缓冲区中最后一个字符之后

这显然意味着空字节不应该干扰您的攻击


我希望你的疑虑得到澄清

使用字节加载并找到其他替代方法以避免零字节。参考指令集参考。例如,
mov-rax,1
可以写成
xor-eax,eax;mov al,1
。不幸的是,我找不到该集合引用。如果你做了,请也给我。即使这样也帮了我谢谢你@nltc见。另一个有用的参考是and。请注意,除了修复NUL字节外,还需要使代码位置独立。让我看看我是否能写一个合适的答案。@fuz,请写!你说的职位独立是什么意思?谢谢!您的回答似乎澄清了我的疑问,是的,新的汇编文件工作得非常完美!将此标记为正确答案。
push-rax
似乎毫无意义。
write
系统调用采用显式长度,而不是以0结尾的字符串。因此,即使执行此操作时RSP指向
msg:db…
结尾后的8个字节,也没有必要这样做。还有几个浪费的REX前缀。此外,您可以通过向前跳过字符串来避免
子rsi,0x41…
,使
rel32
为负,高
0xff
字节而不是零。(如果您想避免使用
0xff
字节,这是一个很好的技巧。或者我想,对于缓冲区大小错误的FGET,确保换行符位于末尾。)此外,
push 1
/
pop rax
是一种将寄存器设置为小立即常数的更紧凑的方法@PeterCordes你是对的,代码有点长,也有点不必要。但毕竟,从这个问题来看,用户似乎对汇编不是很有经验,因此我从其他评论中选取了一些要点,以便于用户理解。是的,我在回答的其余部分对有用的评论/文本投了赞成票。但特别是对于一个没有经验的用户来说,引入一个问题中没有的
push-rax
只是误导和混淆。在您没有提到的非常特定的情况下,它只会0-0终止
msg
,这是不需要的。