避免 shellcode NASM 的 JMP CALL POP 技术中的 JMP?

Posted

技术标签:

【中文标题】避免 shellcode NASM 的 JMP CALL POP 技术中的 JMP?【英文标题】:Avoiding the JMP in the JMP CALL POP technique for shellcode NASM? 【发布时间】:2018-05-25 11:41:26 【问题描述】:

尽管这两个程序都遵守 shellcode 执行所需的相对寻址指令,并且都在执行时打印所需的消息,但当用作 shellcode 时,第二个示例会失败。谁能解释这种行为?奇怪的是,与第一个相同的第三个样本也失败了。

输出: 示例 1 你好世界

Other Samples(2&3) 打印垃圾值

示例 1

全局_start 部分 .text _开始: jmp 加宽 酸碱度世界: 流行音乐 xor rax,rax 移动,1 mov rdi,rax mov rdx,rdi 添加 rdx,11 系统调用 ;出口 xor rax,rax mov rax,60 xor rdi,rdi 系统调用 扩大: 调用 pHworld Hworld db "Hello World",0xa

示例 2

全局_start 部分 .text _开始: 调用 pHworld Hworld db "Hello World",0xa 酸碱度世界: 流行音乐 xor rax,rax 移动,1 mov rdi,rax mov rdx,rdi 添加 rdx,11 系统调用 ;出口 xor rax,rax mov rax,60 xor rdi,rdi 系统调用

示例 3

全局_start 部分 .text _开始: 跳转标签1 标签1: 调用 pHworld Hworld db "Hello World",0xa 酸碱度世界: 流行音乐 xor rax,rax 移动,1 mov rdi,rax mov rdx,rdi 添加 rdx,11 系统调用 ;出口 xor rax,rax mov rax,60 xor rdi,rdi 系统调用

无法满足我的好奇心,我尝试了另一种变体,尽管我的 objdump 没有任何 0x00,但它失败了(打印垃圾值)。 示例 4

全局_start 部分 .text 酸碱度世界: 流行音乐 xor rax,rax 移动,1 mov rdi,rax mov rdx,rdi 添加 rdx,11 系统调用 xor rax,rax xor rdi,rdi 移动,60 系统调用 l1: 调用 pHworld Hworld db“你好世界”,0xa _开始: 跳转 l1 在此处输入代码

sample4 的 Objdump

./hworld2.s:文件格式elf64-x86-64 部分.text的反汇编: 0000000000400080 : 400080: 5e 流行 rsi 400081: 48 31 c0 xor rax,rax 400084:b0 01 移动,0x1 400086: 48 89 c7 mov rdi,rax 400089: 48 89 fa mov rdx,rdi 40008c: 48 83 c2 0b 添加 rdx,0xb 400090: 0f 05 系统调用 400092: 48 31 c0 xor rax,rax 400095: 48 31 ff xor rdi,rdi 400098: b0 3c 移动,0x3c 40009a: 0f 05 系统调用 000000000040009c: 40009c: e8 df ff ff ff 呼叫 400080 00000000004000a1: 4000a1: 48 雷克斯.W 4000a2:65 克 4000a3: 6c ins BYTE PTR es:[rdi],dx 4000a4: 6c ins BYTE PTR es:[rdi],dx 4000a5: 6f 输出 dx,DWORD PTR ds:[rsi] 4000a6: 20 57 6f 和字节 PTR [rdi+0x6f],dl 4000a9: 72 6c jb 400117 4000ab:64 飞秒 4000ac: 0a eb 或 ch,bl 00000000004000ad: 4000ad: eb ed jmp 40009c

【问题讨论】:

第三个显然与第一个不同。它使用 jmp/call 但布局不同。 请注意,64 位代码不需要 call/pop 将 PC 相对地址获取到寄存器中。您可以使用 RIP 相对 LEA。例如Avoiding 0xFF bytes in shellcode using CALL to read RIP?,或者只是 jmp 转发你的字符串并使用 lea rdi, [rel msg] 表示否定的 rel32。它实际上并不比 call/pop 小,而是指令更少。 【参考方案1】:

TL;DR :使用 shellcode,您希望避免编码 0x00 字节,否则当代码用作漏洞利用的字符串时,它们将在第一个 0x00 处截断。这将有效地缩短您的代码。

额外的 0x00 字节在漏洞利用之外运行时不会导致问题,因为它们没有被转换为字符串。指令的执行与任何正常的可执行文件一样。


JMP/CALL/POP 方法中的JMP 指令的原因是为了消除在生成的代码中插入不需要的 0x00 字节。 64 位代码中的JMP 具有rel8rel32 编码。在 64 位代码中,CALL 仅具有 rel32 编码。这意味着如果您在 64 位代码中使用 CALL 在内存中进行小规模传输,它将被编码为 32 位零扩展目标。该零扩展名会导致在 shell 代码中放置不需要的 0x00 值。在 64 位代码中,此 CALL 指令:

    call next
    nop
next:

将被编码为:

e8 01 00 00 00

由于 JMP 指令支持rel8(相对字节位移),如果目标距离不超过 127 个字节(有符号字节为 -128 到 +127)。该 JMP 指令:

    jmp next
    nop
next:

将被编码为:

eb 01

所以没有多余的零。您可能会问为什么 JMP/CALL/POP 方法中的 CALL 指令有效。原因是负值的符号扩展到高字节。用 1 填充高位不会产生额外的 0x00 字节,因此它可以工作。这个 CALL 指令:

prev:
    call prev

将被编码为:

e8 fb ff ff ff

请注意,额外的字节不是 0。这就是为什么在内存中更早地调用某个点可以避免生成零。


样本 2

如果我们牢记上述内容,我们只需要检查示例 2 的生成代码,看看哪里出了问题。 objdump -D ./sample2 -Mintel 生成:

0000000000000000 <_start>:
   0:   e8 0c 00 00 00          call   11 <pHworld>   <---------- Extra zeros

0000000000000005 <Hworld>:
   5:   48                      rex.W
   6:   65 6c                   gs ins BYTE PTR es:[rdi],dx
   8:   6c                      ins    BYTE PTR es:[rdi],dx
   9:   6f                      outs   dx,DWORD PTR ds:[rsi]
   a:   20 57 6f                and    BYTE PTR [rdi+0x6f],dl
   d:   72 6c                   jb     7b <pHworld+0x6a>
   f:   64 0a 5e 48             or     bl,BYTE PTR fs:[rsi+0x48]

0000000000000011 <pHworld>:
  11:   5e                      pop    rsi
  12:   48 31 c0                xor    rax,rax
  15:   b0 01                   mov    al,0x1
  17:   48 89 c7                mov    rdi,rax
  1a:   48 89 fa                mov    rdx,rdi
  1d:   48 83 c2 0b             add    rdx,0xb
  21:   0f 05                   syscall
  23:   48 31 c0                xor    rax,rax
  26:   b8 3c 00 00 00          mov    eax,0x3c       <---------- Extra zeros
  2b:   48 31 ff                xor    rdi,rdi
  2e:   0f 05                   syscall

所以我们看到了 CALL 被额外的零编码的问题,这就是您需要传统 JMP/CALL/POP 的原因。 mov eax, 60 存在第二个问题,因为它编码了额外的字节。我想你的意思是使用mov al, 60

样本 3

objdump -D ./sample3 -Mintel 生成:

0000000000000000 <_start>:
   0:   eb 00                   jmp    2 <label1>     <---------- Extra zeros

0000000000000002 <label1>:
   2:   e8 0c 00 00 00          call   13 <pHworld>   <---------- Extra zeros

0000000000000007 <Hworld>:
   7:   48                      rex.W
   8:   65 6c                   gs ins BYTE PTR es:[rdi],dx
   a:   6c                      ins    BYTE PTR es:[rdi],dx
   b:   6f                      outs   dx,DWORD PTR ds:[rsi]
   c:   20 57 6f                and    BYTE PTR [rdi+0x6f],dl
   f:   72 6c                   jb     7d <pHworld+0x6a>
  11:   64 0a 5e 48             or     bl,BYTE PTR fs:[rsi+0x48]

0000000000000013 <pHworld>:
  13:   5e                      pop    rsi
  14:   48 31 c0                xor    rax,rax
  17:   b0 01                   mov    al,0x1
  19:   48 89 c7                mov    rdi,rax
  1c:   48 89 fa                mov    rdx,rdi
  1f:   48 83 c2 0b             add    rdx,0xb
  23:   0f 05                   syscall
  25:   48 31 c0                xor    rax,rax
  28:   b8 3c 00 00 00          mov    eax,0x3c       <---------- Extra zeros
  2d:   48 31 ff                xor    rdi,rdi
  30:   0f 05                   syscall

与示例 2 的问题类型相同。


样本 1

objdump -D ./sample1 -Mintel 生成:

0000000000000000 <_start>:
   0:   eb 1f                   jmp    21 <widen>

0000000000000002 <pHworld>:
   2:   5e                      pop    rsi
   3:   48 31 c0                xor    rax,rax
   6:   b0 01                   mov    al,0x1
   8:   48 89 c7                mov    rdi,rax
   b:   48 89 fa                mov    rdx,rdi
   e:   48 83 c2 0b             add    rdx,0xb
  12:   0f 05                   syscall
  14:   48 31 c0                xor    rax,rax
  17:   b8 3c 00 00 00          mov    eax,0x3c       <---------- Extra zeros
  1c:   48 31 ff                xor    rdi,rdi
  1f:   0f 05                   syscall

0000000000000021 <widen>:
  21:   e8 dc ff ff ff          call   2 <pHworld>

0000000000000026 <Hworld>:
  26:   48                      rex.W
  27:   65 6c                   gs ins BYTE PTR es:[rdi],dx
  29:   6c                      ins    BYTE PTR es:[rdi],dx
  2a:   6f                      outs   dx,DWORD PTR ds:[rsi]
  2b:   20 57 6f                and    BYTE PTR [rdi+0x6f],dl
  2e:   72 6c                   jb     9c <Hworld+0x76>
  30:   64                      fs
  31:   0a                      .byte 0xa

虽然你说sample 1 有效,但还是有问题需要改正。 mov rax, 60 必须是 mov al, 60

【讨论】:

感谢您提供如此清晰的解释!我将跟进编码的工作原理。另外我不知道呼叫的方向会产生影响,也非常感谢您的解释! 先生,您能否也查看一下编辑并告诉我为什么 sample-4 没有产生输出?以这种方式重构代码时,我犯了什么概念性错误? @YuvrajSingh:关于示例 4 在可执行文件中,您有一个入口点(_start 是默认值),它允许加载程序在文件中的任何位置启动。当涉及到运行 shell 代码时 - shell 代码总是从第一条指令开始执行。因此,在示例 4 中,当转换为 shell 代码时,它从 pop rsi 开始,它将随机值作为指针放入 RSI 中并尝试打印它。这就是为什么您以 JMP 作为第一条指令开始 顺便说一句,如果我正在构建 shellcode,我会在其中放置一些比 nop 更有用的东西,比如我的一个字符串。

以上是关于避免 shellcode NASM 的 JMP CALL POP 技术中的 JMP?的主要内容,如果未能解决你的问题,请参考以下文章

Linux Shellcode“你好,世界!”

获取程序真正的起始执行地址

C 中的 Shellcode - 这是啥意思?

shellcode在C环境下的调用

c_cpp shellcode_ExcelRegisterXLL.c

当我调用jmp时,我在c内联汇编中出现了段错误