避免使用JMP CALL POP技术进行shellcode NASM的JMP?

时间:2017-12-11 21:19:25

标签: linux assembly nasm x86-64 shellcode

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

输出: 示例1 Hello World

其他样本(2& 3)打印垃圾值

示例1

global _start
section .text
        _start:
                jmp widen

        pHworld:
                pop rsi
                xor rax,rax
                mov al,1
                mov rdi,rax
                mov rdx,rdi
                add rdx,11
                syscall
                ;Exit
                xor rax,rax
                mov rax,60
                xor rdi,rdi
                syscall
        widen:
                call pHworld
                Hworld db "Hello World",0xa

示例2

global _start
section .text
        _start:
                call pHworld
                Hworld db "Hello World",0xa

        pHworld:
                pop rsi
                xor rax,rax
                mov al,1
                mov rdi,rax
                mov rdx,rdi
                add rdx,11
                syscall
                ;Exit
                xor rax,rax
                mov rax,60
                xor rdi,rdi
                syscall

示例3

global _start
section .text
        _start:
                jmp label1

        label1:
                call pHworld
                Hworld db "Hello World",0xa

        pHworld:
                pop rsi
                xor rax,rax
                mov al,1
                mov rdi,rax
                mov rdx,rdi
                add rdx,11
                syscall
                ;Exit
                xor rax,rax
                mov rax,60
                xor rdi,rdi
                syscall

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

global _start
section .text
pHworld:
    pop rsi
    xor rax,rax
    mov al,1
    mov rdi,rax
    mov rdx,rdi
    add rdx,11
    syscall
    xor rax,rax
    xor rdi,rdi
    mov al,60
    syscall

l1:
    call pHworld
    Hworld db "Hello World", 0xa

_start:
    jmp l1
    enter code here

取样4


    ./hworld2.s:     file format elf64-x86-64


    Disassembly of section .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             add    rdx,0xb
      400090:       0f 05                   syscall 
      400092:       48 31 c0                xor    rax,rax
      400095:       48 31 ff                xor    rdi,rdi
      400098:       b0 3c                   mov    al,0x3c
      40009a:       0f 05                   syscall 

    000000000040009c :
      40009c:       e8 df ff ff ff          call   400080 

    00000000004000a1 :
      4000a1:       48                      rex.W
      4000a2:       65                      gs
      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                and    BYTE PTR [rdi+0x6f],dl
      4000a9:       72 6c                   jb     400117 
      4000ab:       64                      fs
      4000ac:       0a eb                   or     ch,bl

    00000000004000ad :
      4000ad:       eb ed                   jmp    40009c 

1 个答案:

答案 0 :(得分:7)

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

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

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

    call next
    nop
next:

将编码为:

e8 01 00 00 00

由于 JMP 指令支持rel8(相对字节位移),如果目标不超过127字节,NASM可以在内存中生成 JMP 指令离(有符号字节是-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

相关问题