[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