0

Windows 10, x64 , x86

My current knowledge

Lets say it is quad core, there will be 4 individual program counters which will point to 4 different locations of code for parallel execution.

Each of this program counters indicates where a computer is in its program sequence.

The address it points to changes after a context switch where another threads program counter gets placed onto the program counter to execute.

What I want to do:

Im in Kernel Mode my thread is running on core 1 and I want to read the current instruction pointer of core 2.

Expected Results:

0x203123 is the address of the instruction pointer and this address belongs to this thread and this thread belongs to this process... etc.

Anyone knows how to do it or can give me good book references, links etc...

Ojav
  • 678
  • 6
  • 22
  • looks like the answer is no https://stackoverflow.com/questions/599968/reading-program-counter-directly – Alan Birtles May 12 '20 at 16:51
  • @AlanBirtles: That's about having code find out *its own* address. x86-64 can do that easily with a RIP-relative LEA. This question is about reading a private register from another core. That problem is the same for RIP or any of the general-purpose registers like RAX; they're private state of a core so you'd have to send an interrupt and get the interrupt handler to put the register value somewhere in memory. Or perhaps they want to get the saved user-space state of various tasks, indexed by PID not what core they're running on; that's a very different question. – Peter Cordes May 12 '20 at 17:21
  • You can't literally find out what another core's instruction pointer (RIP) is, the best you can do is send an IPI (interprocessor interrupt) to the other core that will execute your own code that obtains the saved RIP from the moment the IPI was received on that core, which is going to be sometime after the IPI was sent. You should explain what you're really trying to do as knowing the current RIP of each core isn't actually useful information. – Ross Ridge May 12 '20 at 22:28

2 Answers2

3

Although I don't believe it's officially documented, there is a ZwGetContextThread exported from ntdll.dll. Being undocumented, things can change (and I haven't tried it in quite a while) but at least when I last tried it, you called it with a thread handle and a pointer to a CONTEXT structure, and it would return that thread's context.

I'm not certain exactly how up-to-date that is though. It's never mattered to me, so I haven't checked, but my guess would be that the IP in the CONTEXT you get is whatever was saved the last time the thread was suspended. So, if you want something (reasonably) current, you'd use ZwSuspendThread, get the context, then ZwResumeThread to start it running again.

Here I suppose I'm probably supposed to give the standard lines about undocumented function being subject to change, using them being a bad idea, and that you should generally leave all of this alone. Ah well, I been disappointing teachers and other authority figures for years, and I guess I'm not changing right now.

On the other hand, there may be a practical problem here. If you really need data that's really current, this probably isn't going to work very well for you. What it gives you will be kind of current at best. On the other hand, really current is almost a meaningless concept with information that goes out of date every clock cycle.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 3
    If `NtGetContextThread` is still implemented like it was in Server 2003, then according to ReactOS [`NtGetContextThread`](https://github.com/reactos/reactos/blob/893a3c9d030fd8b078cbd747eeefd3f6ce57e560/ntoskrnl/ps/debug.c#L332), it uses a special APC that calls `PspGetContext` in the target thread and synchronizes on an event. – Eryk Sun May 12 '20 at 22:50
2

Anyone knows how to do it or can give me good book references, links etc...

For 80x86 hardware (regardless of operating system); there are only 3 ways to do this (that I know of):

a) send an inter-processor interrupt to the other CPU, and have an interrupt handler that stores the "return EIP" (from its stack) at a known address in memory so that your CPU can read "value of EIP immediately before interrupt" (with synchronization so that your CPU doesn't read before the value is written, etc).

b) put the other CPU into some kind of "debug mode" (single-stepping, last branch recording, ...) so that (either code in a debug exception handler or the CPU's hardware itself) is constantly writing EIP values to memory that you can read.

Of course both of these options will ruin performance, and the value you get will probably be useless (because EIP would've changed after you obtain it but before you can use the obtained value). To ensure the value is still useful; you'd need the other CPU to wait until after you've consumed the obtained value (and are ready for the next value); and to do that you'd have to resort to single-step debugging facilities (with the waiting in the debug exception handler), where you'll be lucky if you can get performance better than a thousand times slower (and can probably improve performance by simply disabling other CPUs completely).

Also note that they still won't accurately tell you EIP in all cases (e.g. if the CPU is in SMM/System Management Mode and is beyond the control of the OS); and I doubt Windows kernel supports any of it (e.g. kernel should support single-stepping of user-space processes/threads to allow debuggers to work, but won't support single-stepping of kernel and will probably lock up the computer due to various "waiting for lock to be released for 6 days" problems).

The last of the 3 options is:

c) Run the OS inside an emulator/simulator instead of running it on real hardware. In that case you can probably modify the emulator/simulator's code to inject EIP values somewhere (maybe some kind of virtual "EIP reporting device"?). This will ruin performance of the emulator/simulator, but you may be able to hide that (e.g. "virtual time inside the emulator passes at a rate of one second per 1000 seconds of real time outside the emulator").

Brendan
  • 35,656
  • 2
  • 39
  • 66