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?
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.
In the worst case there can be redundant or meaningless prefixes extending the size of the instruction to the 15 byte limit.
@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.
I just wanted to make it more clear tp the original poster what full instruction decoding entailed.
@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, making R12 and R13 potentially worse as base registers.)