Running code within another process's address space
lwn.net
lwn.net
[1]: https://docs.microsoft.com/en-us/windows/win32/api/processth...
Standard debuggers use ptrace to implement breakpoints by replacing instructions in debugged process with the assembly code INT3 (or equivalents). This makes the cpu trap when it reaches that instruction which transfers control to the kernel who pauses the process and notifies the debugger.
This approach doesn't work for Python because there are no native assembly instructions for the Python code - only for the C interpreter.
Most Python debuggers attach to already running processes by injecting a few lines of native code (ptrace on Linux, CreateRemoteThread on Windows) which then bootstraps a new Python thread that actually implements the debugger. (As the linked article mentions, on Linux you can inject code using ptrace today but it isn't the most convenient.)
This is also the trick that tools like pyrasite take to inject python code into running processes.
On that note, if anyone wants to easily debug/profile code running on kubernetes clusters with no prior setup, I'm looking for beta testers for an open source framework my cofounder and I are developing. It enables general application maintenance on kubernetes and not just debugging/profiling, but that's the part we need testers for right now
My "debugger" was a shim or wrapper around GDB. For debugging C/C++ code, it just piped standard in/out with GDB. However if it recognized a "set breakpoint" that was actually for a line of script code, it would intercept it. Instead of telling GDB to set a breakpoint there, it would do two things:
It would use GDB to manually call a function in the target program instructing the interpreter to watch for that script line ("set script breakpoint"). Then it would set a normal (native) breakpoint, but in the special function the interpreter would call from its main loop ("catch break here").
Conversely while relaying output from GDB back to the IDE, if it saw it hit a breakpoint in the "catch break here" function, it wouldn't report that. Instead the shim would use GDB to call the "why did I break" function and report the result of that back to the IDE. It was like magic getting to see the IDE bring up the correct script file and line as if the debugger hit it!
Other extensions like single stepping are obvious from these primitives.
Although I do usually just end up hijacking a dll already used by the program when I want to sneak my own code within a process that I don't have the source for, for the sake of future usability. (When patching the executable on-disk isn't desirable, and you don't want to go insane running an injector each time you start the process.)
That’s hard to do for an OS component, due to the anti-viral measures in the OS. Modern windows freaks out when you replace DLLs in c:/Windows/System32 directory.
Last time I used CreateRemoteThread was when a client wanted Windows 7 equivalent of Desktop Duplication API (introduced in Windows 8). I injected my DLL into the desktop compositor (dwm.exe process), hooked IDXGISwapChain.Present and IDXGISwapChain.ResizeBuffers methods, and wrote some C++ to copy desktop image into another ID3D10Texture2D in VRAM, and share the copy with the video capturing process.
Once the process is up and running it has the control over this (with LoadLibraryEx and SetDefaultDllDirectories) and it's also possible to tighten search logic at the OS level (with a registry patch), but if the machine is under attacker's control, the only option is to validate loaded module list from within the app.
Guide to inject your .NET dlls into a native process: https://www.codeproject.com/Articles/607352/Injecting-NET-As...
It's also why "anti-cheat drivers" have never gone away, they allow a greater level of control over what's done to your protected processes.
I'm not sure I am okay with that - there's only so much I'm willing to put up with to play some game - but I do understand why developers/producers would want such draconian solutions. In some game genres, cheating is rampant.
Also if you have permission, you can call CreateThread [2] system call and start a thread in other task. (Normally you don't have permission.)
[1]: https://threos.io/
[2]: https://threos.io/docs/threos/html/syscall.html#syscall_Crea...
Handle CreateThread(Handle task, PFN_THREADENTRY start, void* data, uintptr_t type, void* sp);
This is sufficiently hard to use that it was roundly hated. What you really want is a whole, parallel linker-loader environment in the agent thread so you could say "load this shared object and call this function in it with these arguments".