Reputation: 31
I'm working here with binary obfuscation, so a got a buffer that is filled with op-codes, and I'm using Linux, so, all function calls uses the same caller/callee conventions and no problem here.
My question is about the E8 opcode, This opcode takes near calls using relative address.
My question is that: I know the address where call comes from, I know the address where I have to call, so, how can I find the shift address that I must put in the E8 call? This is:
signed long src = (signed long)buffer + shift; //get the position where E8 instruction is
signed long dst = (signed long)srand; //get the destination position where i want to call (yes, srand(long) function in this case.)
So in my buffer I have:
buffer[] = "[....]\xE8\xFF\xFA\xFE\x54[.....]"; //example
I need to replace with a valid pointer to srand, how can I get the relative address from what I have?
I just thought that i can use FF instruction to call direct, but I couldn't figure out how to do this. I cant copy the address to (say) $eax because I can't put more op-codes than 5 in the replacement (it will make all jmp calls above go bananas), and I can't understand if there is a way to make a direct call in 5 bytes.
So if somebody know how to get the right value to replace the E8 relative shifting address, or if there is a way to make some sort of direct call keeping the same functional properties as E8 call and just using 5 bytes...
(Before ask, I tried to put FF XX XX XX XX as XX being the real address and it didn;t work, the x86 doesn't looks it like a call, it interpret as a INC (???) and some random thing after. I tried replace in this way:
inline void endian_swap(long& x) {
x = (x>>24) |
((x<<8) & 0x00FF0000) |
((x>>8) & 0x0000FF00) |
(x<<24);
}
endian_swap(dst);
endian_swap(src);
unsigned int p = dst - src;
endian_swap(p);
And put the address that I found to E8 call. It didn't work anyway.
Upvotes: 1
Views: 3470
Reputation: 31
I solved it by doing:
long dst = (long)srand;
long src = ((long)buffer) + shift + 5; //begin of buffer + actual position + this instruction size
long p = dst - src;
p = htonl(p);
Than I replace the call on the buffer and everything works well.
Upvotes: 2
Reputation: 62096
The relative addresses in the near call
and jxx
/near jmp
instructions equals the target address where you want to transfer control minus the address of the instruction immediately following your call
or jump
instruction. Relative addresses are relative to the address of the next instruction, not the one that's transferring control. IOW, you have to take into account the length of your call
or jump
instruction if its address operand is relative.
Generally there's no equivalent to a call
or jump
instruction that's 5 bytes or shorter.
You can simulate jmp
as push target address
+ ret
, but in 32-bit mode with arbitrary target addresses you get at least 1+4+1=6 bytes for those 2 instructions. You can simulate call
in the same way, but you will have add another push
or call
instruction to place the return address on the stack. So, to those 6 bytes you add 5 more.
There's an "absolute" version of "jmp" (and IIRC "call" as well) that takes the address operand as an immediate consisting of the target offset and target segment. Such an instruction will be at least 1+4+2=7 bytes long (4 bytes for offset, 2 bytes for segment selector).
If you use a variant of call
or jmp
that takes the target address from a specified memory location (e.g. call [ebx]
), that instruction is going to be at least 1+1=2 bytes long (opcode + ModR/M byte), but you'll have to load a register with the address of that memory location containing the target address and that'll cost you some other 1+4=5 bytes, giving you at least 7 bytes. There's also a variant that allows you to specify the target address in a register (e.g. jmp ebx
), but again, because of having to load the register, you come out at at least 7 bytes.
The only way you can make your call
/jump
instruction shorter is when the target address is very close to the address of that instruction (in which case you can use either a rel16
form (with the appropriate operand or address (I don't remember which one) override prefix) or a rel8
form if available) OR when the target address is small (in which case push target address
can be either the shorter push Ib
or the shorter operand size prefix + push Iw
).
Upvotes: 3