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