x86 E8 and FF calls, how to find the E8 shifting address 'on the fly'? basic x86 ASM calls

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

Answers (2)

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

Alexey Frunze
Alexey Frunze

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

Related Questions