DynamoRIO / dynamorio

Dynamic Instrumentation Tool Platform
Other
2.66k stars 562 forks source link

Fail to inject dynamorio.dll into (MSYS2 debugging build of) Inkscape on Windows #4245

Open debach opened 4 years ago

debach commented 4 years ago

Describe the bug When running bin64/drrun.exe -debug -- c:/inkscape/msys2-debug-build/inkscape.exe in WinDbg64 on a 64-bit Windows 10 1903 (Build 18362), the module dynamorio.dll is not loaded successfully. The client application Inkscape runs without issues, but dynamo_auto_start is never reached and dynamorio.dll never appears in the list of loaded modules.

To Reproduce

Edit: It seems you don’t need to compile Inkscape on your own, you can also just download and install the precompiled binaries from https://inkscape.org/release/0.92.4/windows/64-bit/. A DynamoRIO release build will also fail to inject into that binary, see below under Additional context.

Steps to reproduce the behavior:

  1. Clone the Inkscape sources from https://gitlab.com/inkscape/inkscape. I have version e6e5ca7714.
  2. Follow the build instructions “Compiling Inkscape on Windows with MSYS2”, but use cmake -G Ninja -DCMAKE_BUILD_TYPE=DEBUG .. instead of cmake -G Ninja ...
  3. Build a debug version of DynamoRIO (version 228933df; newest as of today) with Visual Studio 2017 Win64.
  4. Run drrun.exe -debug -- inkscape.exe with WinDbg64. Set a breakpoint:

    .childdbg 1 g

    bu dynamorio!dynamo_auto_start g g

Expected behavior The breakpoint dynamo_auto_start should be hit.

Screenshots or Pasted Text For my debugging attempts, see below.

Versions

What version of DynamoRIO are you using?

I’m using a debug build of 228933df.

Does the latest build from https://github.com/DynamoRIO/dynamorio/wiki/Latest-Build solve the problem?

Not tested: I’m using the version that is one commit ahead of the latest cronbuild release.

What operating system version are you running on? ("Windows 10" is not sufficient: give the release number.)

Windows 10 Version 1903 (Build 18362.752)

Is your application 32-bit or 64-bit?

64-bit

Additional context In a release build of Inkscape – built without the additional -DCMAKE_BUILD_TYPE=DEBUG switch above – the breakpoint dynamo_auto_start is hit. The module dynamorio.dll is loaded successfully and shows in the output of lm for the process inkscape.exe, but not for the process gdbus.exe. Even running drrun.exe -t drcov -- inkscape/msys2-release-build/inkscape.exe works fine and outputs a coverage file.

Edit: I’ve found that only DynamoRIO debug builds succeed to inject, but only into Inkscape non-debug builds, including the precompiled binaries from the official homepage; even drcov works in these cases. On the other hand, DynamoRIO release builds fail to inject into any Inkscape build, and also not with the precompiled binaries.

Debugging attempts

This is what I’ve done in WinDbg:

0:000> .childdbg 1
0:000> sxe ld:dynamorio

The above sets a breakpoint for when a dynamorio module is loaded, which will actually be hit twice.

0:000> g
<187 ModLoads omitted>
ntdll!LdrpDoDebuggerBreak+0x30:
00007ff8`cadd121c cc              int     3
1:004> bu dynamorio!dynamo_auto_start
1:004> g

<3 ModLoads omitted>
ModLoad: 00000000`30030000 00000000`305d0000   C:\projects\dynamorio\builds\build-vs17-x64-228933df-debug\lib64\debug\dynamorio.dll
ntdll!NtMapViewOfSection+0x14:
00007ff8`cad9c5c4 c3              ret
1:004> |
   0    id: 79cc    create  name: drrun.exe
.  1    id: 53d0    child   name: C:\projects\inkscape\master\build-debug\inkscape\bin\inkscape.exe
1:004> ~*
.  4  Id: 53d0.7830 Suspend: 1 Teb: 00000000`0029d000 Unfrozen
      Start: inkscape+0x14c0 (00000000`004014c0)
      Priority: 0  Priority class: 32  Affinity: ff
   5  Id: 53d0.5fb0 Suspend: 1 Teb: 00000000`0029f000 Unfrozen
      Start: ntdll!TppWorkerThread (00007ff8`cad33d60)
      Priority: 0  Priority class: 32  Affinity: ff
   6  Id: 53d0.3024 Suspend: 1 Teb: 00000000`002a1000 Unfrozen
      Start: ntdll!TppWorkerThread (00007ff8`cad33d60)
      Priority: 0  Priority class: 32  Affinity: ff
   7  Id: 53d0.4670 Suspend: 1 Teb: 00000000`002a3000 Unfrozen
      Start: ntdll!TppWorkerThread (00007ff8`cad33d60)
      Priority: 0  Priority class: 32  Affinity: ff
1:004> k
 # Child-SP          RetAddr           Call Site
00 00000000`006df488 00007ff8`cad2a233 ntdll!NtMapViewOfSection+0x14
01 00000000`006df490 00007ff8`cad29f96 ntdll!LdrpMinimalMapModule+0x103
02 00000000`006df550 00007ff8`cad2d5b7 ntdll!LdrpMapDllWithSectionHandle+0x1a
03 00000000`006df5a0 00007ff8`cad2e608 ntdll!LdrpMapDllNtFileName+0x183
04 00000000`006df6a0 00007ff8`cad2e360 ntdll!LdrpMapDllFullPath+0xe0
05 00000000`006df830 00007ff8`cad22536 ntdll!LdrpProcessWork+0x74
06 00000000`006df890 00007ff8`cad222a8 ntdll!LdrpLoadDllInternal+0x13e
07 00000000`006df910 00007ff8`cad21764 ntdll!LdrpLoadDll+0xa8
08 00000000`006dfac0 00007ff8`c8b256f0 ntdll!LdrLoadDll+0xe4
09 00000000`006dfbb0 00007ff8`c8b5c771 KERNELBASE!LoadLibraryExW+0x170
0a 00000000`006dfc20 00007ff8`c8b5bd3f KERNELBASE!LoadLibraryExA+0x31
0b 00000000`006dfc60 00000000`00080013 KERNELBASE!LoadLibraryA+0x3f
0c 00000000`006dfc90 00007ff8`ca9ceb60 0x80013
0d 00000000`006dfc98 00000000`00000000 KERNEL32!LoadLibraryAStub

When I continue, the same ModLoad at the same address is hit again:

1:004> g
ModLoad: 00000000`30030000 00000000`305d0000   C:\projects\dynamorio\builds\build-vs17-x64-228933df-debug\lib64\debug\dynamorio.dll
ntdll!NtMapViewOfSection+0x14:
00007ff8`cad9c5c4 c3              ret

Note that the following call stack has a different frame 6, because there is now a LdrpDrainWorkQueue between LdrpLoadDllInternal and LdrpProcessWork. There is no dynamorio.dll in the output of lm at any point:

1:004> k
 # Child-SP          RetAddr           Call Site
00 00000000`006df468 00007ff8`cad2a233 ntdll!NtMapViewOfSection+0x14
01 00000000`006df470 00007ff8`cad29f96 ntdll!LdrpMinimalMapModule+0x103
02 00000000`006df530 00007ff8`cad2d5b7 ntdll!LdrpMapDllWithSectionHandle+0x1a
03 00000000`006df580 00007ff8`cad701f2 ntdll!LdrpMapDllNtFileName+0x183
04 00000000`006df680 00007ff8`cad2e419 ntdll!LdrpMapDllRetry+0x9a
05 00000000`006df7f0 00007ff8`cad2e93f ntdll!LdrpProcessWork+0x12d
06 00000000`006df850 00007ff8`cad22540 ntdll!LdrpDrainWorkQueue+0x183
07 00000000`006df890 00007ff8`cad222a8 ntdll!LdrpLoadDllInternal+0x148
08 00000000`006df910 00007ff8`cad21764 ntdll!LdrpLoadDll+0xa8
09 00000000`006dfac0 00007ff8`c8b256f0 ntdll!LdrLoadDll+0xe4
0a 00000000`006dfbb0 00007ff8`c8b5c771 KERNELBASE!LoadLibraryExW+0x170
0b 00000000`006dfc20 00007ff8`c8b5bd3f KERNELBASE!LoadLibraryExA+0x31
0c 00000000`006dfc60 00000000`00080013 KERNELBASE!LoadLibraryA+0x3f
0d 00000000`006dfc90 00007ff8`ca9ceb60 0x80013
0e 00000000`006dfc98 00000000`00000000 KERNEL32!LoadLibraryAStub
1:004> g
<5 ModLoads omitted>

Now a new process starts:

<New process starts with 42 ModLoads>
(74d0.41d0): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00007ff8`cadd121c cc              int     3
2:020> |
   0    id: 79cc    create  name: drrun.exe
   1    id: 53d0    child   name: C:\projects\inkscape\master\build-debug\inkscape\bin\inkscape.exe
.  2    id: 74d0    child   name: C:\projects\inkscape\master\build-debug\inkscape\bin\gdbus.exe
2:020> g
<20 ModLoads omitted>
<Debugee is running>

At this point, Inkscape is running without any apparent issues. I hit ctrl+break in WinDbg to manually break into the process:

(79cc.6ff4): Break instruction exception - code 80000003 (first chance)
ntdll!DbgBreakPoint:
00007ff8`cad9faf0 cc              int     3
0:000> ~0k
 # Child-SP          RetAddr           Call Site
00 0000003f`2297ce28 00007ff8`89abbc20 ntdll!NtWaitForSingleObject+0x14
01 0000003f`2297ce30 00007ff8`89ac0f12 drinjectlib!nt_wait_event_with_timeout+0x20 [c:\projects\dynamorio\core\win32\ntdll.c @ 3752] 
02 0000003f`2297ce70 00007ff8`89ab160c drinjectlib!os_wait_handle+0x52 [c:\projects\dynamorio\core\win32\os.c @ 8549] 
03 0000003f`2297ceb0 00007ff6`37a1508b drinjectlib!dr_inject_wait_for_child+0x3c [c:\projects\dynamorio\core\win32\injector.c @ 951] 
04 0000003f`2297cef0 00007ff6`37a31a94 drrun!wmain+0x219b [c:\projects\dynamorio\tools\drdeploy.c @ 1769] 
05 0000003f`2297f980 00007ff6`37a319be drrun!invoke_main+0x34 [d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 91] 
06 0000003f`2297f9c0 00007ff6`37a3187e drrun!__scrt_common_main_seh+0x12e [d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
07 0000003f`2297fa30 00007ff6`37a31b09 drrun!__scrt_common_main+0xe [d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331] 
08 0000003f`2297fa60 00007ff8`ca9c7bd4 drrun!wmainCRTStartup+0x9 [d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_wmain.cpp @ 17] 
09 0000003f`2297fa90 00007ff8`cad6ced1 KERNEL32!BaseThreadInitThunk+0x14
0a 0000003f`2297fac0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
derekbruening commented 4 years ago

Note that dynamorio.dll will not show up in lm after DR initializes because DR removes itself from the PEB module lists (unless you pass -no_hide). Furthermore, certain types of injections don't use the system loader at all, and thus dynamorio.dll is never in any PEB module list, although those types are off by default (too difficult to support clients using system libraries there). That said, the default late injection used by drrun today does have dynamorio.dll loaded in a normal manner and visible initially until toward the end of DR initialization.

What happens if inkscape is launched by an app already under DR control? E.g., using the create_process app:

bin64/drrun.exe -- bin64/create_process.exe c:/inkscape/msys2-debug-build/inkscape.exe

That will use a different, earlier injection.

There is also an earlier-still injection though I'm not 100% sure how robust it is anymore (not enough devs maintaining it):

bin64/drrun.exe -early_inject_map -early_inject_location 5 -- bin64/create_process.exe c:/inkscape/msys2-debug-build/inkscape.exe