[PATCH 3/3] dbghelp: fix failing SymInitialize for a live 32bit target from a 64bit caller in WOW64

Eric Pouech eric.pouech at gmail.com
Mon Aug 30 11:26:38 CDT 2021


When:
- caller of dbghelp is a 64bit process,
- invoking SymInitialize on a 32bit live target running under WOW64

SymInitialize fails because:
- check_live_target() erroneously reads the 64bit PEB of the target,
  while it actually wants the 32bit PEB.
- as the ELF base header address isn't set (hidden in CloudFileFlags)
  in the 64bit PEB, hence causing the failure

So ensure that check_live_target() actually reads the 32bit PEB when
handling a 32bit process.


----

I couldn't find a simpler way to fix it :-(
(offsetting PEB address by 0x1000 works but is way too hacky)

And I'm still not happy with the result. Is there a better way to solve
this?

Signed-off-by: Eric Pouech <eric.pouech at gmail.com>

---
 dlls/dbghelp/dbghelp.c |   58 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 54 insertions(+), 4 deletions(-)

diff --git a/dlls/dbghelp/dbghelp.c b/dlls/dbghelp/dbghelp.c
index 00d7b61fbd8..9475e2dc2ac 100644
--- a/dlls/dbghelp/dbghelp.c
+++ b/dlls/dbghelp/dbghelp.c
@@ -27,6 +27,7 @@
 #include "wine/debug.h"
 #include "wdbgexts.h"
 #include "winnls.h"
+#include <tlhelp32.h>
 
 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
 
@@ -282,11 +283,51 @@ const WCHAR *process_getenv(const struct process *process, const WCHAR *name)
     return NULL;
 }
 
+/* retrieves the PEB32 address of a 32bit target under wow64 when dbghelp is a 64bit process */
+static PEB* get_peb32_addr(HANDLE proc)
+{
+    HANDLE snap;
+    HANDLE thread;
+    THREADENTRY32 te;
+    PEB* ret = NULL;
+    DWORD pid = GetProcessId(proc);
+    THREAD_BASIC_INFORMATION thread_info;
+    TEB teb;
+    TEB32 teb32;
+    SIZE_T res;
+
+    snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+    if (snap == INVALID_HANDLE_VALUE) return NULL;
+
+    te.dwSize = sizeof(te);
+    if (Thread32First(snap, &te))
+    {
+        do
+        {
+            if (te.th32OwnerProcessID == pid &&
+                te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID) &&
+                (thread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te.th32ThreadID)) != NULL)
+            {
+                if (NtQueryInformationThread(thread, ThreadBasicInformation, &thread_info, sizeof(thread_info), NULL) == 0 &&
+                    ReadProcessMemory(proc, thread_info.TebBaseAddress, &teb, sizeof(teb), &res ) &&
+                    ReadProcessMemory(proc, teb.Tib.ExceptionList, &teb32, sizeof(teb32), &res))
+                {
+                    ret = (PEB*)(ULONG_PTR)teb32.Peb;
+                }
+                CloseHandle(thread);
+            }
+            te.dwSize = sizeof(te);
+        } while (!ret && Thread32Next(snap, &te));
+    }
+    CloseHandle(snap);
+    return ret;
+}
+
 /******************************************************************
  *		check_live_target
  *
  */
-static BOOL check_live_target(struct process* pcs)
+static BOOL check_live_target(struct process* pcs, BOOL wow64, BOOL child_wow64)
 {
     PROCESS_BASIC_INFORMATION pbi;
     ULONG_PTR base = 0, env = 0;
@@ -300,11 +341,20 @@ static BOOL check_live_target(struct process* pcs)
 
     if (!pcs->is_64bit)
     {
+        void* peb32_addr;
         DWORD env32;
         PEB32 peb32;
+
         C_ASSERT(sizeof(void*) != 4 || FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment) == 0x48);
-        if (!ReadProcessMemory(pcs->handle, pbi.PebBaseAddress, &peb32, sizeof(peb32), NULL)) return FALSE;
-        if (!ReadProcessMemory(pcs->handle, (char *)pbi.PebBaseAddress + 0x460 /* CloudFileFlags */, &base, sizeof(base), NULL)) return FALSE;
+        if (!wow64 && child_wow64)
+        {
+            peb32_addr = get_peb32_addr(pcs->handle);
+            if (!peb32_addr) return FALSE;
+        }
+        else
+            peb32_addr = pbi.PebBaseAddress;
+        if (!ReadProcessMemory(pcs->handle, peb32_addr, &peb32, sizeof(peb32), NULL)) return FALSE;
+        if (!ReadProcessMemory(pcs->handle, (char *)peb32_addr + 0x460 /* CloudFileFlags */, &base, sizeof(base), NULL)) return FALSE;
         if (read_process_memory(pcs, peb32.ProcessParameters + 0x48, &env32, sizeof(env32))) env = env32;
     }
     else
@@ -454,7 +504,7 @@ BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeP
     pcs->next = process_first;
     process_first = pcs;
     
-    if (check_live_target(pcs))
+    if (check_live_target(pcs, wow64, child_wow64))
     {
         if (fInvadeProcess)
             EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);




More information about the wine-devel mailing list