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

Avoiding the JMP in the JMP CALL POP technique for shellcode NASM?

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

输出: 示例 1 你好世界

其他示例(2&3) 打印垃圾值

示例 1

全局_start
节.text
        _开始:
                跳转加宽

        酸碱度世界:
                pop rsi
                xor rax,rax
                mov al,1
                mov rdi,rax
                mov rdx,rdi
                添加 rdx,11
                系统调用
                ;出口
                xor rax,rax
                mov rax,60
                xor rdi,rdi
                系统调用
        扩大:
                致电 pHworld
                Hworld 数据库 "Hello World",0xa

示例 2

全局_start
节.text
        _开始:
                致电 pHworld
                Hworld 数据库 "Hello World",0xa

        酸碱度世界:
                pop rsi
                xor rax,rax
                mov al,1
                mov rdi,rax
                mov rdx,rdi
                添加 rdx,11
                系统调用
                ;出口
                xor rax,rax
                mov rax,60
                xor rdi,rdi
                系统调用

示例 3

全局_start
节.text
        _开始:
                jmp标签1

        标签 1:
                致电 pHworld
                Hworld 数据库 "Hello World",0xa

        酸碱度世界:
                pop rsi
                xor rax,rax
                mov al,1
                mov rdi,rax
                mov rdx,rdi
                添加 rdx,11
                系统调用
                ;出口
                xor rax,rax
                mov rax,60
                xor rdi,rdi
                系统调用

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

全局_start
节.text
酸碱度世界:
    pop rsi
    xor rax,rax
    mov al,1
    mov rdi,rax
    mov rdx,rdi
    添加 rdx,11
    系统调用
    xor rax,rax
    xor rdi,rdi
    移动 al,60
    系统调用

l1:
    致电 pHworld
    Hworld 数据库 "Hello World", 0xa

_开始:
    跳转 l1
    在此处输入代码

样本4的Objdump


    ./hworld2.s: 文件格式 elf64-x86-64


    .text 部分的反汇编:

    0000000000400080 :
      400080:5e pop rsi
      400081: 48 31 c0 xor rax,rax
      400084: b0 01 mov al,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 mov al,0x3c
      40009a: 0f 05 系统调用

    000000000040009c:
      40009c: e8 df ff ff ff 呼叫 400080

    00000000004000a1 :
      4000a1: 48 rex.W
      4000a2:65 克
      4000a3: 6c ins BYTE PTR es:[rdi],dx
      4000a4: 6c ins BYTE PTR es:[rdi],dx
      4000a5: 6f outs dx,DWORD PTR ds:[rsi]
      4000a6: 20 57 6f 和 BYTE PTR [rdi+0x6f],dl
      4000a9: 72 6c jb 400117
      4000ab:64飞秒
      4000ac: 0a eb 或 ch,bl

    00000000004000ad :
      4000ad: eb ed jmp 40009c

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

当 运行 在漏洞利用之外时,额外的 0x00 字节不会导致问题,因为它们不会被转换为字符串。这些指令像任何普通可执行文件一样执行。


之所以在JMP/CALL/POP方法中使用JMP指令是为了消除插入不需要的0x00字节生成的代码。 JMP in 64-bit code has a rel8 and rel32 encoding. In 64-bit code CALL 只有 rel32 编码。这意味着如果您在 64 位代码中使用 CALL 在内存中进行小的前向传输,它将被编码为 32 位零扩展目标。该零扩展将导致不需要的 0x00 值被放置在 shell 代码中。在 64 位代码中,此 CALL 指令:

    call next
    nop
next:

将被编码为:

e8 01 00 00 00

由于JMP指令支持rel8(相对字节位移)NASM可以生成一条JMP指令如果目标距离不超过 127 个字节(有符号字节为 -128 到 +127),则在内存中转发。这条 JMP 指令:

    jmp next
    nop
next:

将被编码为:

eb 01

所以没有额外的零。你可能会问为什么 CALL 方法中的 JMP/CALL/POP 指令有效。原因是负值的符号扩展到高位字节。用 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.