无 NULL 的 shellcode

7

我正在尝试将我写的汇编程序转换为无 NULL 的 shellcode。

但是,对于某些指令,我不确定该如何进行操作。
其中一些指令(使用 Intel 语法)包括:

push 0x1000

并且

mov BYTE [eax],0x31

我希望避免使用成千上万次的inc eax指令。我在想,也许可以通过对值进行xor操作来实现创意解决方案,对于第二个问题,也许有一个标志可以设置为只接受8位常数。

2个回答

5
push 0x1000

如果您可以多余一个寄存器(并且不介意丢失标志位),那么以下方式是否可行:
xor eax, eax
inc eax
shl eax, 12
push eax

mov BYTE [eax],0x31

这里的零不是来自常数,而是来自寻址模式。请尝试:
xchg eax, ecx
mov BYTE [ecx],0x31
xchg eax, ecx

0

如果你想在内存中创建一个包含0字节的值,你不能直接将其用作pushmov的立即操作数。你可以使用常量的特殊属性在寄存器中构造它,或者将其与其他内容进行异或运算。(然后使用push eax或其他空闲寄存器)。

    xor eax,eax      ; 2 bytes
    bts eax, 12      ; 4 bytes; eax |= 1<<12

    xor eax,eax      ; 2 bytes
    mov ah, 0x10     ; 2 bytes; eax = 0x10 << 8

或者将一个已经清零的寄存器 push 到栈中,并进行非零字节的存储,如果您想稍后保留清零的寄存器。 push eax / mov byte [esp+1], 0x10

我们不关心性能,因此部分寄存器合并不是问题,窄存储器后面重新加载双字加载时也不会出现存储转发延迟。


通过push/pop或LEA指令,可以用3个字节构建范围在[-128, +127]之间的常量。

   push 12            ; 2 bytes, works for -128 .. +127
   pop  eax           ; 1 byte

   lea  ecx, [eax+127] ; reg+disp8 addressing mode.  ECX = 139

另请参阅{{link1:在x86 / x64机器代码中打高尔夫球的技巧}}


通常情况下,如果没有特殊的常数属性,您可以使用XOR、ADD或SUB在2个指令中构造它(或LEA或其他)。XOR是最容易看到不会创建任何零字节的方法,只需选择任何一个常数即可,该常数与您想要的值在相同位置上没有相同的字节。(它不必在每个字节中都相同,例如0x12345678也可以工作)
   mov  eax, 0x1000 ^ 0x55555555     ;  mov eax, 0x55554555
   xor  eax, 0x55555555              ; cancel out the constant

由于我们使用了EAX,因此这将汇编为两个5字节的指令:

  b8 55 45 55 55          mov    eax,0x55554555
  35 55 55 55 55          xor    eax,0x55555555

没有备用寄存器,
  68 55 45 55 55          push   0x55554555       # 0x1000 ^ 0x55555555
  81 34 24 55 55 55 55    xor    DWORD PTR [esp],0x55555555

避免在 [eax] 寻址模式中出现零 ModRM

不仅仅是 [eax],而是与 AL 或 EAX 源一起使用,或者与使用 /0 作为额外的操作码位于 /r 字段中的立即指令

mov r/m8, imm8 (C6 /0 ib) 就是这样一条指令,因此您不能将其与普通的 [eax] 寻址模式一起使用。如果您可以偏移地址,例如 inc eax / [eax-1],那么可以使用它。

如果可以,请首先使用其他寄存器作为地址,否则有各种方法可以解决

 c6 00 31                mov    BYTE PTR [eax],0x31   # nope, ModRM=00

 b1 31                   mov    cl,0x31               # using another register for data
 88 08                   mov    BYTE PTR [eax],cl     # /r field = 1 for the CL source


 80 20 31                and    BYTE PTR [eax],0x31   # clear bits we don't want
 80 08 31                or     BYTE PTR [eax],0x31   # set bits we do want

 # use a different register temporarily as @user200783 suggested
 91                      xchg   ecx,eax
 c6 01 31                mov    BYTE PTR [ecx],0x31
 91                      xchg   ecx,eax   # presumably need to restore it, otherwise you'd have used another reg in the first place.


  # offset the address.
 40                      inc    eax
 c6 40 ff 31             mov    BYTE PTR [eax-0x1],0x31
 # 48                      dec    eax   # optional, if you want to restore it

几个行不通的想法:

 # halve the address (if it's known to be aligned by 2):
 d1 e8                   shr    eax,1
 c6 04 00 31             mov    BYTE PTR [eax+eax*1],0x31   # Nope, SIB = 00

 # write more bytes: nope: mov r/m32, imm32  also has a /0 as part of the opcode
 c7 00 31 11 11 11       mov    DWORD PTR [eax],0x11111131

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接