Wrong assembly generated by shellcraft.execve('ecx', [], 0)
Closed this issue · 1 comments
Output:
/* execve(path='ecx', argv=[], envp=0) */
/* push argument array [] */
/* push '\x00' */
push 1
dec byte ptr [esp]
xor ecx, ecx
push ecx /* null terminate */
mov ecx, esp
mov ebx, ecx // <- ecx has be polluted
xor edx, edx
/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80
Because shellcraft.execve('ecx', [], 0)
will call push_array
first, which modifies ecx
.
Same situation occurs when architecture is amd64, which pollutes rsi
during pushing the empty array.
This is a bug, but I don't think it's something that can be easily fixed (and right now we lack sufficient resources to handle e.g. #1189 in a timely manner).
For this to work the way you intend (i.e., have the register value of ecx
as the first syscall argument), we'd have to encode a bunch of register dependency logic into the pushstr
and pushstr_array
. There's not a nice way to do it.
Generally, pwntools tries to just "be magic" and have things work the way you'd expect. This one unfortunately isn't one of those cases, and you have to do a little bit of work.
I'd recommend moving the value manually to ebx
, and invoking with execve('ebx', ...)
.
>>> print shellcraft.mov('ebx', 'ecx') + shellcraft.execve('ebx', [], {})
mov ebx, ecx
/* execve(path='ebx', argv=[], envp={}) */
/* push argument array [] */
/* push '\x00' */
push 1
dec byte ptr [esp]
xor edx, edx
push edx /* null terminate */
mov edx, esp
/* push argument array [] */
/* push '\x00' */
push 1
dec byte ptr [esp]
xor ecx, ecx
push ecx /* null terminate */
mov ecx, esp
/* setregs noop */
/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80
Alternately, you can specify argv
and envp
such that the offending code is not emitted.
>>> print shellcraft.execve('ecx', 0, 0)
/* execve(path='ecx', argv=0, envp=0) */
mov ebx, ecx
xor ecx, ecx
xor edx, edx
/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80
Finally, you can always invoke the syscall manually:
>>> print shellcraft.syscall(constants.SYS_execve, 'ecx', 0, 0)
/* call execve('ecx', 0, 0) */
push SYS_execve /* 0xb */
pop eax
mov ebx, ecx
xor ecx, ecx
cdq /* edx=0 */
int 0x80
Ultimately this is a bug, but not one that we can fix without major rework, as i386 has a very limited number of registers, and ecx
is a volatile register in most ABIs. Hopefully the workarounds I suggested will help.