Is it possible to manipulate the instruction pointer in 8086 assembly?
Asked Answered
S

2

2

I want to know if I can manipulate (read and change the value of) the instruction pointer (IP) in 8086 assembly.

For example,

Say IP is currently storing 0200h. I would like to read this value and change it to something else, say 4020h. How could I do that?

Shiff answered 15/11, 2017 at 7:46 Comment(2)
IIRC, all x86 instructions setting/adjusting instruction pointer to/by particular value are: jmp, call, ret, iret, int, syscall, jCC, jcxz, loop (jCC = all conditional jumps) ... all instructions of course by default increment instruction pointer to point to the next one (if not reset by particular value like in case of jmp). And some instructions may set instruction pointer by triggering some trap mechanism (division error, invalid memory access, ...). Some of that does not apply to x86-16 (syscall and memory access). ... to read it you can use the fake-call trick, reading it from stack.Spout
re: your edit: call 4020h. The assembler will figure out what rel16 displacement to use given the current IP. The old value (+ the length of the call instruction) will be on the stack where the code at 4020h could pop it with pop (or pop back into IP with ret), or load it with a mov.Nada
C
8

If you wanted to set the instruction pointer to a known value, say hex value 4020h, you could jump directly to that address:

jmp 4020h

Or if some memory location, myVariable, held the value you wanted to store in IP you could do an indirect jump:

jmp [myVariable]

The result of a jmp (indirect or direct) modifies the instruction pointer.

Reading the instruction pointer is problematic. Position independent code on Linux used to work by using a set of code something like:

 call getIP

with

 :getIP
 mov bx, [sp] ; Read the return address into BX.
 ret

For other methods of reading IP, see Stack Overflow: reading IP.

Concerned answered 15/11, 2017 at 7:49 Comment(13)
what is this address: 0x1234 ? can i change ip register address directly ? note: i am a complete beginner in assembly so don't know much about it.Shiff
@Doda it's not problematic. Call whereAmI: POP AX, JMP AXRosabella
@Doda read 9086 user manual for architecture, read assembler manual for instructions. You will then be able to understand mksteve answer and upvote it. ATM, you seem to be asking irrelevant questions after getting a good answer. The JMP instruction IS changing the IP register value directly - that's all it does. The IP register does does not have an address in the memory map. 0x1234 is just a value mksteve used as an example. You are asking meaningless questions. Read the manuals. Before you ask about instruction details, you need to understand the architecture and how computers work.Rosabella
@Doda - The way to handle downvotes is to make new posts and get upvotes for those. :-) A possible explanation for the downvotes might be that a few people knew that MOV IP, X isn't missing but just spelled JMP X, and that you perhaps could have found that out yourself by reading Intel's manual.Wold
@Bo Persson okay should i then delete this question because everyone is saying its dump question ?Shiff
@Doda - I don't think you can delete it now, as that would also remove the upvotes for the answer. Wouldn't be fair to steve. But if it hurts that much, you can have an extra upvote to make it a net win. :-)Wold
@BoPersson you seem like a really nice person :D thanks alot and yes i shouldn't delete it. Because it would not be fair with mksteve as his answer is right and he deserve it. :) peace be with you all. have a nice day. thanks again BoPerssonShiff
btw i have a question regarding AAA instruction and i think its a good one.Shiff
@Doda You got downvoted because you asked a question that can be answered easily using Google or by looking in the manual. There is no way to get rid of downvotes. Do not delete the question, but next time, please to some research effort before writing a question and tell us everything you already know in the question so we know exactly where you need help. Vague questions like yours (for example, you are not specific about what “manipulate the instruction pointer” is supposed to even mean) are very hard to answer and get downvotes.Klement
@Doda Much better! Now the question can be answered in a reasonably manner.Klement
Possibly worth mentioning that x86-64 added a RIP-relative addressing mode, so lea rsi, [rel some_address] is all you need in PIC code. (Or lea rsi, [RIP + 0] to get the address of the next instruction.)Nada
@Peter Cordes woah! that was out of my mind! i think i will stick to mksteve as his answer is quite easy.Shiff
@mksteve: jmp 4020h isn't an indirect jump, it's a direct jump. It's only encodeable in position-dependent code, because it has to be encoded as a rel16 from the end of the instruction. The only absolute direct jumps are FAR jumps, and this is NEAR.Nada
N
4

Related: Reading program counter directly (I updated the accepted answer there to not suck, and to cover 32 bit vs. 64-bit, because it's the canonical Q&A for reading IP. No mention of writing IP, because that's sort of a conceptual-understanding thing: writing IP is a jump, but it's possible that your code can be running without knowing where it was loaded, so the use-cases are totally different.)

Also a near duplicate: Why can't you set the instruction pointer directly? asks why RIP/EIP/IP isn't exposed directly for use with instructions that work on integer registers like AX. (i.e. why does add IP, AX not work as an indirect jump.) TL:DR: some ISAs like ARM do expose the program counter as one of the integer registers, but x86 has few registers and using one register encoding for IP in the machine code would take away a general-purpose integer register.


You can write IP directly with jmp or call, but you can only read it by having call push it.

(Technically call isn't the only option for reading IP. You could use int or some other interrupt and have the interrupt-handler look at the context before iret, but that's the same idea as call just much more complicated and slower.)


In position-dependent code, the address of every instruction is known at link time. You can use the address of any label as an immediate constant or part of an addressing mode. e.g.

mov ax, $         ; ax = address of the start of the MOV instruction (NASM syntax)

Or

mov  ax, label   ; or MASM:  mov ax, OFFSET label

label:

Say IP is currently storing 0200h i would like to read this value & change it to something else say 4020h. how could i do that ?

call 4020h

The assembler will figure out what rel16 displacement to use given the current IP. (Or you could put 4020h in a register and call ax, if you want a position-independent way to jump to a fixed IP value (offset relative to cs, so still not an absolute address. For that you need a far call, and can use a ptr16:16 absolute direct with the address as an immediate.)

The old value (+ the length of the call instruction) will be on the stack where the code at 4020h could pop it with pop (or pop back into IP with ret), or load it with a mov.


In general, avoid mis-matched call / ret. (i.e. don't just pop the return address into a register and return with a jmp). That will cause branch mispredicts because you unbalance the return-address predictor stack. (http://agner.org/optimize/ and Return address prediction stack buffer vs stack-stored return address?)

On CPUs newer than PIII, call next_insn / pop ax is efficient because call rel32=0 is special-cased and doesn't break the return-address predictor stack. See Reading program counter directly.

@mksteve's suggestion to call a function that does mov bx, [sp] / ret instead of just call next_instruction / pop bx is good on early Intel P6-family CPUs like PPro. But note that [sp] isn't a valid 16-bit addressing mode, so this is extra clunky in 16-bit. Perhaps pop ax / push ax / ret would suck less if you really wanted to do it in 16-bit code.


In 64-bit code, you can read the current value of RIP more directly: lea rax, [rip]. This is much more commonly used for position-independent addressing of static data. e.g. lea rax, [rel my_table] or add dword [rel global_counter], 2 will tell the assembler+linker to figure out what rel32 to use to reach the symbol you wanted. This works within an executable or within a dynamic library, where the distance between the code and the data is constant even if the library is loaded at a different address.

Nada answered 19/11, 2017 at 6:50 Comment(2)
Cool so we can't directly access IP in 8086 ?Shiff
@Doda: You can write it directly with jmp, but you can only read it by having call push it. (Interrupts also push the current cs:ip as part of the return context for iret, including software-generated interrupts like from int, but when you just want to read IP the only sane option is call.)Nada

© 2022 - 2024 — McMap. All rights reserved.