[PATCH 1/2] rundll32: Re-exec as 32-bit or 64-bit if necessary.

Vincent Povirk vincent at codeweavers.com
Sun Sep 22 20:06:22 CDT 2019


Signed-off-by: Vincent Povirk <vincent at codeweavers.com>
---
 programs/rundll32/Makefile.in |   2 +-
 programs/rundll32/rundll32.c  | 162 ++++++++++++++++++++++++++++++++++
 2 files changed, 163 insertions(+), 1 deletion(-)

diff --git a/programs/rundll32/Makefile.in b/programs/rundll32/Makefile.in
index 6ab5eda37e..adc586ca4f 100644
--- a/programs/rundll32/Makefile.in
+++ b/programs/rundll32/Makefile.in
@@ -1,5 +1,5 @@
 MODULE    = rundll32.exe
-IMPORTS   = user32
+IMPORTS   = shlwapi user32
 
 EXTRADLLFLAGS = -mwindows -municode -mno-cygwin
 
diff --git a/programs/rundll32/rundll32.c b/programs/rundll32/rundll32.c
index 3d4a4687e2..ae925b50a4 100644
--- a/programs/rundll32/rundll32.c
+++ b/programs/rundll32/rundll32.c
@@ -38,6 +38,7 @@
 #include "windows.h"
 #include "wine/winbase16.h"
 #include "wine/debug.h"
+#include "shlwapi.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(rundll32);
 
@@ -239,6 +240,149 @@ static LPWSTR get_next_arg(LPWSTR *cmdline)
     return arg;
 }
 
+static LPCWSTR find_arg_start(LPCWSTR cmdline)
+{
+    LPCWSTR s;
+    BOOL in_quotes;
+    int bcount;
+
+    bcount=0;
+    in_quotes=FALSE;
+    s=cmdline;
+    while (1) {
+        if (*s==0 || ((*s=='\t' || *s==' ') && !in_quotes)) {
+            /* end of this command line argument */
+            break;
+        } else if (*s=='\\') {
+            /* '\', count them */
+            bcount++;
+        } else if ((*s=='"') && ((bcount & 1)==0)) {
+            /* unescaped '"' */
+            in_quotes=!in_quotes;
+            bcount=0;
+        } else {
+            /* a regular character */
+            bcount=0;
+        }
+        s++;
+    }
+    return s;
+}
+
+static BOOL reexec_self(BOOL as_64bit)
+{
+    static const WCHAR exe_name[] = {'\\','r','u','n','d','l','l','3','2','.','e','x','e',0};
+    static const WCHAR sysnative[] = {'\\','S','y','s','N','a','t','i','v','e',0};
+    WCHAR systemdir[MAX_PATH];
+    LPCWSTR args;
+    WCHAR *cmdline;
+    STARTUPINFOW si = {0};
+    PROCESS_INFORMATION pi;
+    BOOL result;
+
+    TRACE("restarting %s\n", as_64bit ? "as 64-bit" : "as 32-bit");
+
+    if (as_64bit)
+    {
+        GetWindowsDirectoryW(systemdir, MAX_PATH);
+        wcscat(systemdir, sysnative);
+    }
+    else
+    {
+        GetSystemWow64DirectoryW(systemdir, MAX_PATH);
+    }
+
+    args = find_arg_start(GetCommandLineW());
+
+    cmdline = HeapAlloc(GetProcessHeap(), 0,
+        (wcslen(systemdir)+wcslen(exe_name)+wcslen(args)+1)*sizeof(WCHAR));
+
+    wcscpy(cmdline, systemdir);
+    wcscat(cmdline, exe_name);
+    wcscat(cmdline, args);
+
+    si.cb = sizeof(si);
+
+    if (CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
+    {
+        CloseHandle(pi.hThread);
+        WaitForSingleObject(pi.hProcess, INFINITE);
+        CloseHandle(pi.hProcess);
+        result = TRUE;
+    }
+    else
+    {
+        WINE_TRACE("failed to restart, err=%d\n", GetLastError());
+        result = FALSE;
+    }
+
+    HeapFree(GetProcessHeap(), 0, cmdline);
+
+    return result;
+}
+
+static WORD get_image_type(LPCWSTR image_name)
+{
+    HANDLE file;
+    IMAGE_DOS_HEADER dos_header;
+    IMAGE_NT_HEADERS nt_headers;
+    DWORD bytes_read;
+    WORD result;
+
+    if (PathIsRelativeW(image_name))
+    {
+        /* There's no reliable way to find the actual file. */
+        return 0;
+    }
+
+    file = CreateFileW(image_name, GENERIC_READ, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
+        NULL, OPEN_EXISTING, 0, NULL);
+    if (file == INVALID_HANDLE_VALUE)
+    {
+        WINE_TRACE("unable to open %s for reading, err=%d\n", wine_dbgstr_wn(image_name, -1), GetLastError());
+        return 0;
+    }
+
+    if (!ReadFile(file, &dos_header, sizeof(dos_header), &bytes_read, NULL) ||
+        bytes_read != sizeof(dos_header) ||
+        dos_header.e_magic != IMAGE_DOS_SIGNATURE)
+    {
+        WINE_TRACE("unable to read dos header from %s\n", wine_dbgstr_wn(image_name, -1));
+        CloseHandle(file);
+        return 0;
+    }
+
+    SetFilePointer(file, dos_header.e_lfanew, NULL, FILE_BEGIN);
+
+    if (!ReadFile(file, &nt_headers, sizeof(nt_headers), &bytes_read, NULL) ||
+        bytes_read < 6) /* enough to read magic and machine architecture */
+    {
+        WINE_TRACE("unable to read extended header from %s\n", wine_dbgstr_wn(image_name, -1));
+        CloseHandle(file);
+        return 0;
+    }
+
+    if (((IMAGE_OS2_HEADER*)&nt_headers)->ne_magic == IMAGE_OS2_SIGNATURE)
+    {
+        WINE_TRACE("%s is an NE (16-bit) image\n", wine_dbgstr_wn(image_name, -1));
+        result = IMAGE_OS2_SIGNATURE;
+    }
+    else if (nt_headers.Signature == IMAGE_NT_SIGNATURE)
+    {
+        result = nt_headers.FileHeader.Machine;
+        WINE_TRACE("%s is a PE image with machine 0x%x\n", wine_dbgstr_wn(image_name, -1), result);
+    }
+    else
+    {
+        WINE_TRACE("%s is an image with an unrecognized extended header\n", wine_dbgstr_wn(image_name, -1));
+        result = 0;
+    }
+
+    CloseHandle(file);
+
+    return result;
+}
+
 int WINAPI wWinMain(HINSTANCE instance, HINSTANCE hOldInstance, LPWSTR szCmdLine, int nCmdShow)
 {
     HWND hWnd;
@@ -247,6 +391,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE hOldInstance, LPWSTR szCmdLine
     BOOL unicode = FALSE, win16;
     STARTUPINFOW info;
     HMODULE hDll;
+    WORD dll_type;
 
     hWnd=NULL;
     hDll=NULL;
@@ -270,6 +415,23 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE hOldInstance, LPWSTR szCmdLine
         szEntryPoint = get_next_arg(&szCmdLine);
     WINE_TRACE("EntryPoint=%s\n",wine_dbgstr_w(szEntryPoint));
 
+    /* Figure out what type of dll it is. */
+    dll_type = get_image_type(szDllName);
+
+    /* Re-exec with another architecture if necessary. */
+#ifdef __i386__
+    if (dll_type == IMAGE_FILE_MACHINE_AMD64) {
+        BOOL is_wow64;
+        if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64 && reexec_self(TRUE))
+            goto CLEANUP;
+    }
+#endif
+#ifdef __x86_64__
+    if ((dll_type == IMAGE_FILE_MACHINE_I386 || dll_type == IMAGE_OS2_SIGNATURE) &&
+        reexec_self(FALSE))
+        goto CLEANUP;
+#endif
+
     /* Load the library */
     hDll=LoadLibraryW(szDllName);
     if (hDll)
-- 
2.20.1




More information about the wine-devel mailing list