Software Engineering Blog

Tipps und Tricks aus dem Leben eines Systemadministrators.

Reverse Engineering a Dotnet Monitor

von

For my master thesis I had to reverse engineer a Dotnet monitor to intercept certain function calls. While this is not scientific enought to put in in the thesis, it might help someone facing similar problems.

The general strategy to locate the synchronisation related symbols is to use a tiny C# program with a synchronized loop (see below). This issues many calls to the monitor. Our goal is to find the unknown Monitor.Exit symbol. Thereto we set a breakpoint on the known Monitor.Enter symbol and inspect the assembly at the breakpoint location. We know, that each entry to the monitor must be followed by an exit statement. So the number of entry and exit calls must match. Using this information, we simply count the number of calls to functions near the Monitor.Enter breakpoint. This finally points us to JIT_MonExit.

The WinDbg console output of the sampel C# program is shown below. Breakpoints are set on:

  • System.Private.CoreLib!System.Threading.Monitor.Enter*
  • coreclr!JIT_MonExit

To trace the objects the monitor is bound to, we further inspect the function arguments. For the Monitor.Enter function, we know that it takes two arguments. Furthermore the calling convention is cdecl. Hence, the first argument is 0x20d'3835b9d8 and the second 0x0 (read in right-to-left order). For tracing, we must be able to distinct between monitors on different objects. From the Monitor.Enter function we are able to derive that the first argument is a reference to the object. Hence, a distinction between different monitors is trivially possible by checking the first argument of the Enter and Exit function calls.

Sample Program The shown function is executed in parallel by multiple threads.

public static void IncByOne(param p)
        {
                lock (p)
                {
                    Inc(p);
                    int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
                    Console.WriteLine("Value on {0} is {1}", threadId, p.acc);
                }
            }
        }

WinDbg Output

Breakpoint 2 hit
System_Private_CoreLib!System.Threading.Monitor.Enter(System.Object, Boolean ByRef)$##6001EDB:
00007ff9`c11bdb40 4883ec28        sub     rsp,28h
0:006> kv
 # Child-SP          RetAddr           : Args to Child                                                           : Call Site
00 000000e8`c25ff4b8 00007ff9`fe1fed8f : 0000020d`38359eb0 0000020d`38359ec8 00000000`00000000 0000020d`3835b9d8 : System_Private_CoreLib!System.Threading.Monitor.Enter(System.Object, Boolean ByRef)$##6001EDB
01 000000e8`c25ff4c0 00007ff9`e7da5ac7 : 0000020d`38358fc8 00007ff9`d1b4552f 0000020d`38359920 0000020d`38359938 : System_Console!System.IO.SyncTextWriter.WriteLine(System.String)$##60001B6+0x2f
02 000000e8`c25ff510 00007ff9`fe1f570e : 00000000`00000003 000000e8`c25ff600 0000020d`38359078 00000000`00000007 : System_Runtime_Extensions!System.IO.TextWriter.WriteLine(System.String, System.Object, System.Object)$##6000458+0x47
[...]
13 000000e8`c25ffe60 00007ffa`0f6c0c31 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
14 000000e8`c25ffe90 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
0:006> g
Breakpoint 1 hit
coreclr!JIT_MonExit:
00007ff9`d1b94650 33d2            xor     edx,edx
0:006> kv
 # Child-SP          RetAddr           : Args to Child                                                           : Call Site
00 000000e8`c25ff4b8 00007ff9`fe1fedc0 : 0000020d`38359eb0 0000020d`38359ec8 00000000`00000000 0000020d`3835b9d8 : coreclr!JIT_MonExit [E:\A\_work\2188\s\src\vm\amd64\JitHelpers_InlineGetThread.asm @ 673] 
01 000000e8`c25ff4c0 00007ff9`e7da5ac7 : 0000020d`38358fc8 00007ff9`d1b4552f 0000020d`38359920 0000020d`38359938 : System_Console!System.IO.SyncTextWriter.WriteLine(System.String)$##60001B6+0x60
02 000000e8`c25ff510 00007ff9`fe1f570e : 00000000`00000003 000000e8`c25ff600 0000020d`38359078 00000000`00000007 : System_Runtime_Extensions!System.IO.TextWriter.WriteLine(System.String, System.Object, System.Object)$##6000458+0x47
[...]
13 000000e8`c25ffe60 00007ffa`0f6c0c31 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
14 000000e8`c25ffe90 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

Zurück

Kommentare

Einen Kommentar schreiben

Was ist die Summe aus 1 und 6?

Ähnliche Beiträge

von

We reverse engineer a Dotnet Monitor in Windbg to see how it is internally implemented.

von

As SSDs are getting cheaper, low HDD / SSD ratios of 10/1 or better become an option. This article describes how to tune bcache for this scenario from an empirical perspective.