0

I'm creating a program that shows the registers called dumpRegisters. The registers need to match what is shown in the Registers window in the debugger (using Visual Studio). The registers in the window of the debugger are: EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP, EFL. 10 registers total.

I know the Irvine Library already has this procedure, but I'm creating it from scratch.

I'm able to show most of the registers. However, I'm struggling on how to display the ESP (extended stack pointer) register?

I use push and pop throughout the program, so when I call a showRegister procedure, it outputs the ESP at that point, but ESP continues to change, so the when the program exits, the ESP I showed is not the same. I've tried to push and pop esp, however, it still doesn't match depending on where I push and pop esp, sometimes it also only shows the first 4 registers.

How can I get the ESP register to match the registers window in the debugger?

dumpRegisters PROC
    push eax
    push edx
    mov edx, OFFSET essp
    mov eax, esp
    INVOKE showRegister, OFFSET essp, esp
    pop edx
    pop eax
dumpRegisters ENDP
showRegister PROC,
                regName:PTR BYTE , regValue:DWORD

    ;push esp

    call writeString
    call writeHex
    Tab
    
    ;pop esp 

    ret
showRegister ENDP
  • I was facing the same challenge recently: display the CPU registers including `rSP` and `rIP` without interactive debugger. Perhaps my solution might inspire you: https://euroassembler.eu/maclib/debug.htm – vitsoft May 21 '22 at 07:26

1 Answers1

1

After printing or saving other incoming args, calculate what ESP must have been before the call dumpRegsiters (typo?) that pushed a 4-byte return address, plus whatever other pushes you did.

lea eax, [esp+12] should be correct after 2 pushes.

You should probably start with a pushf to save EFLAGS first, so you can use instructions that modify FLAGS like add or sub. Then of course adjust your offsets accordingly. Unless it turns out that the Irvine32 functions like WriteHex save/restore EFLAGS themselves, then you're probably just juggling incoming data.

You could even start with pusha and loop through popping and printing, if you don't need to print register names with the values. One of the few use-cases for that instruction, although you still might want to calculate ESP instead of using what it pushed.

(And I guess get EIP from your return address, if you don't mind having the address after the call. If you want the address before, you have to figure out whether it was a 5-byte call rel32, or a call through a function pointer like 2-byte call eax.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • I want the EIP before the call. I'm pretty sure it's a 2 byte call eax. Still unsure how to show the EIP register? –  May 21 '22 at 01:36
  • @LucindaCamacho: `call dumpRegisters` would be a 5-byte `call rel32`. That's definitely the normal use-case for Irvine32 DumpRegs, inserted as a debug-print. There is no bulletproof solution to get EIP from before the call. By far the easiest is to load the return address and subtract 5, because that's normally right. Anything fancier and you'd have to decode backwards in the machine code, which is not possible unambiguously. – Peter Cordes May 21 '22 at 01:51
  • For any backwards-disassembly algorithm, there'd be a way to fool it. e.g. the last 4 bytes of 7-byte `call [func_ptr_table + eax*4]` would be an absolute address, so that's an arbitrary 4 bytes that could end with the machine code for `call ecx` or whatever. [Is it possible to decode x86-64 instructions in reverse?](https://stackoverflow.com/q/52415761) (no) – Peter Cordes May 21 '22 at 01:55
  • @LucindaCamacho: The Irvine32 `DumpRegs` function just prints the return address it was passed: https://csc.csudh.edu/mmccullough/asm/help/source/irvinelib/dumpregs.htm - *EIP displays the offset of the instruction which follows the call to DumpRegs.* I'd highly recommend doing that. That makes much more sense to me than taking any possibly-wrong guesses, even with an insane amount of work (compared to the simplicity of the rest of DumpRegs) disassembling machine code at the call site to look for one of various possible `call` opcodes, and then any number of prefixes before it. – Peter Cordes May 21 '22 at 01:59
  • Alternatively, you could declare an array of dwords in your data segment and just store the registers in each. – puppydrum64 Nov 23 '22 at 12:21