NULL-free shellcode
Asked Answered
L

2

7

I am trying to convert an assembly program I wrote into NULL-free shellcode.

However, I am unsure how to go about this for certain instructions.
Some of them (in Intel syntax) include:

push 0x1000

and

mov BYTE [eax],0x31

I want to avoid using thousands of inc eax instructions. I was thinking maybe something creative with xor-ing values, and for the second, maybe if there was a flag to set to make it take a constant of only 8 bits.

Leucoplast answered 28/7, 2011 at 3:57 Comment(0)
P
5
push 0x1000

If you can spare a register (and you don't mind clobbering the flags), how about something like:

xor eax, eax
inc eax
shl eax, 12
push eax

mov BYTE [eax],0x31

The zero here does not come from the constant, but from the addressing mode. Try:

xchg eax, ecx
mov BYTE [ecx],0x31
xchg eax, ecx
Perimeter answered 28/7, 2011 at 4:11 Comment(0)
B
0

If you want to create a value in memory that contains a 0 byte, you can't use it directly as an immediate operand for push or mov. You can construct it in a register using special properties of the constant, or XOR it with something. (Then push eax, or whatever spare register you used.)

    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

Or push an already zeroed register and do a byte store of the non-zero byte, if you want to keep the zeroed register for later. push eax / mov byte [esp+1], 0x10

We don't care about performance so partial-register merging isn't a problem, nor are store-forwarding stalls from a narrow store that's later reloaded with a dword load.


Constants within [-128, +127] of 0 (via push/pop) or another register (with LEA) can be constructed in 3 bytes

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

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

See also Tips for golfing in x86/x64 machine code


In general, without having special properties for the constant, you can construct it in 2 instructions with an XOR, ADD, or SUB. (Or LEA or whatever). XOR is easiest to see that you're not going to create any zero bytes, just pick any constant that doesn't have the same byte in the same position as the value you want. (It doesn't have to be same in each byte, e.g. 0x12345678 also works)

   mov  eax, 0x1000 ^ 0x55555555     ;  mov eax, 0x55554555
   xor  eax, 0x55555555              ; cancel out the constant

Since we used EAX, this assembles to two 5-byte instructions:

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

Without a spare register,

  68 55 45 55 55          push   0x55554555       # 0x1000 ^ 0x55555555
  81 34 24 55 55 55 55    xor    DWORD PTR [esp],0x55555555

Avoiding a zero in the ModRM from a [eax] addressing mode

It's not just [eax], it's using it with an AL or EAX source, or with an immediate instruction that uses /0 as extra opcode bits in the /r field.

mov r/m8, imm8 (C6 /0 ib) is one such instruction, so you can't use it with a plain [eax] addressing mode. If you can offset the address, like inc eax / [eax-1], that works.

Use a different register for the address in the first place if you can, otherwise there are various things you can do

 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

A couple ideas that don't work:

 # 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
Buckboard answered 2/10, 2022 at 20:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.