IDA series, part 2: debugging a .NET executable
Some time ago, I was looking at a piece of malware written in .NET. It called
WriteProcessMemory and I was interested in dumping its buffer
before it was written, but managed debuggers (such as dnSpy) don’t allow you to easily break on native APIs (or maybe I just don’t know how).
Besides, the sample was obfuscated and de4dot didn’t help, so a native debugger was the clear choice.
However, if you load a .NET binary in IDA (as a PE file1, not as a .NET assembly), put a breakpoint on the entry point, and run it, it will never break:
By the time you manually break, press G, then type kernel32_WriteProcessMemory, then put a breakpoint, then resume execution, the malware has already had time to call it, and you missed your chance to dump that data.
Why does it not break, though?
Obviously, because the code is never executed: the stub is never called, because the real entry point of a .NET binary is
MSCOREE!ShellShim__CorExeMain (WinDbg helps a lot here):
0:000> bp $exentry ; <- put a breakpoint on the actual entry point 0:000> g ; <- run Breakpoint 0 hit ... MSCOREE!ShellShim__CorExeMain: 70fd7cef 8bff mov edi,edi
We now know the real entry point, but
MSCOREE is not in the (static) view of IDA, so we can’t put a breakpoint. So, how can we break on it? (aside from using a different debugger, that is)
Changing the first break event
By default, you can only breakpoint on your binary’s entry point (which might be CRT initialization, a TLS callback, or a custom entry point), but still not the very first possible breakpoint, at which the first thread gets set up and which transfers control to your binary. If you’re familiar with OllyDbg, you’ll recognize this as the system breakpoint. In order to set up IDA so it breaks there, go to Debugger > Debugger options... and mark the Suspend on debugging start checkbox:
Then, run the binary, and you’ll see this:
At this point, there’s nothing aside from your binary, and
ntdll.dll. After executing the first instruction, some more modules will be loaded (
the RTL will prepare and run your binary. To get to the
MSCOREE!ShellShim__CorExeMain function, just step a bit until you find a call to
kernel32_BaseThreadInitThunk, and check the
value of the
EDX register: it points to your target.
This approach is not the best, mostly because other debuggers such as WinDbg are much more powerful and, in general, better (for stuff specific to Windows), but anyway I thought I’d write this post, just for the sake of doing it with IDA.
That’s it for today. If you want me to talk about a specific topic, please manifest yourself in the comments on reddit or hacker news.