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

即使这两个程序都遵守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的Objdump


    ./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 


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

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


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

    call next
    nop
next:

将被编码为:

e8 01 00 00 00

由于JMP指令支持rel8 (相对字节位移),如果目标不超过127个字节(有符号字节为-128至+127),则NASM可以在内存中向前生成JMP指令。 此JMP指令:

    jmp next
    nop
next:

将被编码为:

eb 01

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

prev:
    call prev

将被编码为:

e8 fb ff ff ff

注意额外的字节不是0.这就是为什么在内存中调用一个点以避免产生零。


样品2

如果我们将上述内容牢记在心,我们只需要检查Sample 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 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

链接地址: http://www.djcxy.com/p/15235.html

上一篇: Avoiding the JMP in the JMP CALL POP technique for shellcode NASM?

下一篇: GCC optimizer emits strange conditional jumps, why?