Log in

View Full Version : # IDA-Pro steals RIP ? introduction in relative addressing


nezumi-lab
July 14th, 2009, 22:22
intraarterial injection: i was involved into a project on design a software-level protection, based on anti-dbg tricks that should work in 32- and 64-bit environment causing no conflict with legal apps. also, my shell-code locator has to learn how to recognize x86-64 exploits, so… I took a deep breath and dived into 64-bit word. well, I’m not newbit here, but digging up the anti-dbg tricks working everywhere sounds like a challenge. ok, anti-dbg tricks, shell-codes… good point to begin with.

kotal: x86 does not allow to address EIP register directly (PDP-11 does), but supports relative addressing in the flow control commands (”the” means “all”, for example: CALL L1 it’s a relative call. in the machine representation it looks like: E8 61 06 00 00, where E8h is opcode of CALL and 61 06 00 00 - a relative 32bit signed offset of the target, calculated from the _end_ of the CALL.

it’s very important for shell-codes, because it gives them ability to work being loaded at any offset. for protections it’s useful well. to prevent dumping - just allocate the memory on the heap and copy your procedure there. no dumper is able to create a workable PE image out of heap!

drawbacks: aside of benefits of relative addressing it has its own disadvantages. guess, what happens if we copy our function which calls the function we can’t copy (for example, API). the delta between CALL and the target will be changed, forcing us to recalculate all relative addresses, or… (turn your mind on) start to use absolute addressing, for example: mov eax, offset API_func/CALL eax;

home and dry: x86-64 does not allow to use RIP (former EIP) as a general purpose register (MOV RAX, RIP does not work), but it supports relative addressing almost everywhere (let me to quite the Intel manuals:”RIP-relative addressing allows specific ModR/M modes to address memory relative to
the 64-bit RIP using a signed 32-bit displacement. This provides an offset range of -/+2GB from the RIP
. what it does mean?! for shell-code writers it means a lot!!! from now on we don’t need in GetPC subroutine (usually, CALL L1/L1:POP r32) and can use RIP directly. and this is the part where we meet the problem of the stolen RIP.

anaphylactic shock: please, consider the following code. this is how IDA-Pro 5.5 disassembles it. remember: it’s a piece of a real shell-code, so, concentrate your mind into fuming acid and do not miss the point (see the picture bellow as well):

.code:0000000000401000 start proc near
.code:0000000000401000 mov ecx, 69h
.code:0000000000401005 jmp short loc_40100C
.code:0000000000401007
.code:0000000000401007 loc_401007:
.code:0000000000401007 nop
.code:0000000000401008 xor [eax+eax], cl
.code:000000000040100C
.code:000000000040100C loc_40100C:
.code:000000000040100C lea rax, loc_401013
.code:0000000000401013
.code:0000000000401013 loc_401013:
.code:0000000000401013 loop loc_401007
.code:0000000000401015 mov r9d, 0

how do you like it?! ok, let me to be more specific. how do you like the line: “lea rax, loc_401013″?! what?! did you say: “looks clear!” hello no!!! look closely!!! Option -> Text representation -> Number of opcode bytes -> 9. do you see _now_ what IDA-Pro hides from us?!

.code:000000000040100C 48 8D 05 00 00 00 00 lea rax, loc_401013
.code:0000000000401013 loc_401013:

oh, my unholy cow!!! “LEA RAX, loc_401013” turns out to be “LEA RAX, [RIP]“, thus we’re dealing with position-independent code. in a way, IDA-Pro is correct. she is just calculates RIP on the fly and replaces it by the effective offset. but, we - hackers - want to know if the code is position independent or not!!!

breakdown: HIEW also replaces RIP by effective offset. please consider the following line: 0040100C: 488D05000000001 LEA RAX, [000401013]

ok, do you want to get high? well, let’s do it, ppl!

00000000: 488D0500000000 lea rax,[7]
00000007: 488D0500000000 lea rax,[00000000E]
0000000E: 488D0500000000 lea rax,[000000015]

the same opcodes produce different targets, how funny!!! of course, it’s an opcode of LEA RAX, [RIP] command and I would like to have an option which enables/disables showing RIP, because I do need in very much!!!

in passing: look at the encoder again. don’t you think that it damages the loop?! ok, lets trace the code with any debugger or with our own mind if we have no 64-bit debugger under our hands.

“loop loc_401007″ has E2h F2h opcode. in binary representation F2h is “011110010″, so the lowest bit is zero, thus, when ECX = = 1, the target of loop will change from 401007h to 401008h (401007h ^ 1 = 401008h). as result - NOP will be skipped. of course, it might be INC EBX (opcode 43h) - in that case, EBX would be increased not by ECX (as it’s expected), but by (ECX - 1). how interesting…

well, when ECX = = 0, LOOP just does not pass the control to the target, so everything works fine.

off the record: in normal shell-codes you probably meet something like LEA EAX, [RIP-1] (opcode: 8B05FFFFFFFF), since commands with the positive offsets have zeros in opcodes and shell-codes do not like zeros very much (because of ASCIIZ, where Zero is a string terminator).

http://nezumi-lab.org/gif/x86_RIP01.pngan example of real 64bit shell-code with hidden RIP




http://nezumi-lab.org/blog/?p=212