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:

breakpoint

it never breaks

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:

suspend on debugging start

Then, run the binary, and you’ll see this:

first instruction

At this point, there’s nothing aside from your binary, and ntdll.dll. After executing the first instruction, some more modules will be loaded (kernel32, kernelbase, mscoree), and 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.

the target

Disclaimer

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.

  1. Make sure to load the binary as a PE file rather than as a .NET assembly, so that IDA can launch it using the standard Windows debugger ↩︎