0

For a university assignment I'm tasked with overwriting the Divide-By-Zero interrupt handler using assembly code in x86-64, making it so that the divide operation result will be the divided number - 1.

So to my understanding, I need to alter the value of the divided register before returning to the program.

How can I get the program's original register values and alter them? Where are they stored?

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
BladesV
  • 51
  • 3
  • 3
    They're not stored anywhere; when your interrupt handler gets control, all the registers (except `cs:rip`, `rflags`, and possibly the stack `ss:rsp`) are just as they were when the faulting instruction started. And whatever you leave in those registers upon returning from the handler will be what the program sees when it restarts the faulting instruction. – Nate Eldredge Dec 06 '20 at 21:07
  • The x86-64 architecture reference manuals will explain exactly what happens when the CPU handles an exception, and what changes in between the faulting instruction and the start of the handler. You'll want to study this explanation carefully. Anything it doesn't mention should be assumed to stay the same. – Nate Eldredge Dec 06 '20 at 21:24
  • 2
    I'd be curious what OS this is for? – Michael Petch Dec 06 '20 at 22:00

1 Answers1

4

Interrupts / exceptions only save CS:RIP, RFLAGS, and the user-space SS:RSP, in the exception frame itself. All other register values are unmodified. x86 doesn't do register bank-switching like some other ISAs.

Interrupt handlers have to save/restore every register they want to use, to make sure they don't modify the user-space state. You'll need that to get yourself a couple scratch registers (unless you make multiple assumptions and only support one operand-size and instruction length), but the part of the user-space state you want to modify is still in registers.


Fortunately the dividend is in a fixed location, either AH:AL (aka AX), DX:AX, EDX:EAX, or RDX:RAX depending on the operand-size of the division instruction https://www.felixcloutier.com/x86/idiv or div. (Or if you include 16 / 32-bit compat mode user-space, also possibly just AL for aam imm8)

So you don't have to decode the addressing mode, just prefixes and the opcode, to decide which parts of RDX and RAX are actually involved.

But to return with RIP pointing after the divide instruction, you do actually need to work out the instruction length, e.g. 2-byte div ecx vs. div word [rdi + r10*2 + 256] (9 bytes: REX and operand-size prefixes, opcode, modrm+SIB + disp32).

You do know that the length is <= 15 bytes (including possibly redundant prefixes), otherwise it would have faulted with #UD instead of #DE.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • In the worst case there can be redundant or meaningless prefixes extending the size of the instruction to the 15 byte limit. – Ross Ridge Dec 06 '20 at 21:41
  • @RossRidge: Yes, I wasn't trying to say that 9 bytes was the maximum. just give 2 examples. I didn't include a REX or address-size which could both be used non-redundantly. Oops, but I included RDX in the addressing mode, I should change that. And probably also not make it look like I'm trying to show the *most* extreme example. – Peter Cordes Dec 06 '20 at 21:45
  • 1
    I just wanted to make it more clear tp the original poster what full instruction decoding entailed. – Ross Ridge Dec 06 '20 at 21:49
  • @RossRidge: Ah right. I didn't mention it in my first edit because there's no need to *enforce* or detect that limit, and the obvious implementation (looping through prefixes until an opcode) should Just Work. If I'm not mistaken, no prefixes can affect the length of the *rest* of the instruction so you don't even need to record what you saw. (ModRM enodings are [designed so length-finding doesn't need to consider extra register bits from REX](https://stackoverflow.com/questions/52522544/rbp-not-allowed-as-sib-base), making R12 and R13 potentially worse as base registers.) – Peter Cordes Dec 06 '20 at 22:08