From db36552c422bc59ee1ddc2eb5804f4931e60c11f Mon Sep 17 00:00:00 2001 From: Jason Hood Date: Sat, 8 Feb 2014 01:10:51 +1000 Subject: [PATCH] Work with 64-bit AnyCPU; copy original IDT to IAT; log improvements. --- ANSI.c | 204 ++++++++++++++++++++++++++++----------------------- LLW.c | 115 ----------------------------- ansicon.c | 183 +++++++++++++++++++++++++-------------------- ansicon.h | 42 ++++++----- injdll.c | 152 +++++++++++++++++++++++++++++++------- makefile.gcc | 8 +- makefile.vc | 10 +-- procrva.c | 85 +++++++++++++++++++++ proctype.c | 62 +++++++++++++++- util.c | 26 +++---- 10 files changed, 532 insertions(+), 355 deletions(-) delete mode 100644 LLW.c create mode 100644 procrva.c diff --git a/ANSI.c b/ANSI.c index a95bf71..26a107a 100644 --- a/ANSI.c +++ b/ANSI.c @@ -111,12 +111,14 @@ v1.66, 20 & 21 September, 2013: fix 32-bit process trying to detect 64-bit process. - v1.70, 25 January to 4 February, 2014: + v1.70, 25 January to 7 February, 2014: don't hook ourself from LoadLibrary or LoadLibraryEx; update the LoadLibraryEx flags that should not cause hooking; - inject by manipulating the import directory table; + inject by manipulating the import directory table; for 64-bit AnyCPU use + ntdll's LdrLoadDll via CreateRemoteThread; restore original attribute on detach (for LoadLibrary/FreeLibrary usage); - log: remove the quotes around the CreateProcess command line string. + log: remove the quotes around the CreateProcess command line string and + distinguish NULL and "" args. */ #include "ansicon.h" @@ -956,10 +958,6 @@ ParseAndPrintString( HANDLE hDev, // - Matt Pietrek ~ Windows 95 System Programming Secrets. // - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. -// Macro for adding pointers/DWORDs together without C arithmetic interfering -#define MakeVA( cast, offset ) (cast)((DWORD_PTR)(pDosHeader)+(DWORD)(offset)) - - const char APIKernel[] = "kernel32.dll"; const char APIConsole[] = "API-MS-Win-Core-Console-"; const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-"; @@ -987,9 +985,8 @@ API_DATA APIs[] = HMODULE hKernel; // Kernel32 module handle HINSTANCE hDllInstance; // Dll instance handle -TCHAR hDllName[MAX_PATH]; // Dll file name #if defined(_WIN64) || defined(W32ON64) -LPTSTR hDllNameType; // pointer to process type within above +LPTSTR DllNameType; // pointer to process type within DllName #endif typedef struct @@ -1150,7 +1147,7 @@ BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore ) if (hModuleSnap == INVALID_HANDLE_VALUE) { - DEBUGSTR( 1, L"Failed to create snapshot!" ); + DEBUGSTR( 1, L"Failed to create snapshot (%lu)!", GetLastError() ); return FALSE; } @@ -1186,68 +1183,81 @@ BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore ) // ========== Child process injection +static LPTSTR get_program( LPTSTR app, BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) +{ + app[MAX_PATH-1] = '\0'; + + if (lpApp == NULL) + { + // Extract the program from the command line. I would use + // GetModuleFileNameEx, but it doesn't work when a process is created + // suspended and setting up a delay until it does work sometimes + // prevents the process running at all. GetProcessImageFileName works, + // but it's not supported in 2K. + LPTSTR name; + LPCTSTR term = L" \t"; + + if (wide) + { + LPCTSTR pos; + for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; + if (*pos == '"') + { + term = L"\""; + ++pos; + } + wcsncpy( app, pos, MAX_PATH-1 ); + } + else + { + LPCSTR pos; + for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; + if (*pos == '"') + { + term = L"\""; + ++pos; + } + MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_PATH-1 ); + } + // CreateProcess only works with surrounding quotes ('"a name"' works, but + // 'a" "name' fails), so that's all I'll test, too. However, it also + // tests for a file at each separator ('a name' tries "a.exe" before + // "a name.exe") which I won't do. + name = wcspbrk( app, term ); + if (name) + *name = '\0'; + } + else + { + if (wide) + wcsncpy( app, lpApp, MAX_PATH-1 ); + else + MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_PATH-1 ); + } + return get_program_name( app ); +} + // Inject code into the target process to load our DLL. void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, LPPROCESS_INFORMATION child_pi, BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) { - int type; - PBYTE base; - BOOL gui; + int type; + PBYTE base; + BOOL gui; + WCHAR app[MAX_PATH]; + LPTSTR name = NULL; + if (log_level) + { + name = get_program( app, wide, lpApp, lpCmd ); + DEBUGSTR( 1, L"%s (%lu)", name, child_pi->dwProcessId ); + } type = ProcessType( child_pi, &base, &gui ); if (gui && type > 0) { - TCHAR app[MAX_PATH]; - LPTSTR name; - LPCTSTR term = L" \t"; - - app[MAX_PATH-1] = '\0'; - if (lpApp == NULL) - { - // Extract the program from the command line. I would use - // GetModuleFileNameEx, but it doesn't work when a process is created - // suspended and setting up a delay until it does work sometimes - // prevents the process running at all. GetProcessImageFileName works, - // but it's not supported in 2K. - if (wide) - { - LPCTSTR pos; - for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; - if (*pos == '"') - { - term = L"\""; - ++pos; - } - wcsncpy( app, pos, MAX_PATH-1 ); - } - else - { - LPCSTR pos; - for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; - if (*pos == '"') - { - term = L"\""; - ++pos; - } - MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_PATH ); - } - // CreateProcess only works with surrounding quotes ('"a name"' works, but - // 'a" "name' fails), so that's all I'll test, too. However, it also - // tests for a file at each separator ('a name' tries "a.exe" before - // "a name.exe") which I won't do. - name = wcspbrk( app, term ); - if (name) - *name = '\0'; - } - else - { - if (wide) - wcsncpy( app, lpApp, MAX_PATH-1 ); - else - MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_PATH ); - } - name = get_program_name( app ); + if (name == NULL) + name = get_program( app, wide, lpApp, lpCmd ); if (!search_env( L"ANSICON_GUI", name )) { DEBUGSTR( 1, L" %s", zIgnoring ); @@ -1257,31 +1267,34 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, if (type > 0) { #ifdef _WIN64 - if (type == 32) - { - ansi_bits[0] = '3'; - ansi_bits[1] = '2'; - InjectDLL32( child_pi, base ); - } - else + if (type == 64) { ansi_bits[0] = '6'; ansi_bits[1] = '4'; InjectDLL( child_pi, base ); } + else if (type == 32) + { + ansi_bits[0] = '3'; + ansi_bits[1] = '2'; + InjectDLL32( child_pi, base ); + } + else // (type == 48) + { + InjectDLL64( child_pi ); + } #else #ifdef W32ON64 - if (type == 64) + if (type != 32) { TCHAR args[64]; STARTUPINFO si; PROCESS_INFORMATION pi; - wcscpy( hDllNameType, L"CON.exe" ); - wsprintf( args, L"ansicon -P%lu:%lu", - child_pi->dwProcessId, child_pi->dwThreadId ); + wcscpy( DllNameType, L"CON.exe" ); + wsprintf( args, L"ansicon -P%ld", child_pi->dwProcessId ); ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); - if (CreateProcess( hDllName, args, NULL, NULL, FALSE, 0, NULL, NULL, + if (CreateProcess( DllName, args, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi )) { WaitForSingleObject( pi.hProcess, INFINITE ); @@ -1289,8 +1302,8 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, CloseHandle( pi.hThread ); } else - DEBUGSTR( 1, L"Could not execute \"%s\"", hDllName ); - wcscpy( hDllNameType, L"32.dll" ); + DEBUGSTR(1, L"Could not execute \"%s\" (%lu)", DllName, GetLastError()); + wcscpy( DllNameType, L"32.dll" ); } else #endif @@ -1339,6 +1352,13 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName, { PROCESS_INFORMATION child_pi; + DEBUGSTR( 1, L"CreateProcessA: %s%S%s, %S", + (lpApplicationName == NULL) ? L"" : L"\"", + (lpApplicationName == NULL) ? "" : lpApplicationName, + (lpApplicationName == NULL) ? L"" : L"\"", + (lpCommandLine == NULL) ? "" : + (*lpCommandLine == '\0') ? "" : lpCommandLine ); + if (!CreateProcessA( lpApplicationName, lpCommandLine, lpThreadAttributes, @@ -1349,12 +1369,11 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName, lpCurrentDirectory, lpStartupInfo, &child_pi )) + { + DEBUGSTR( 1, L" Failed (%lu)", GetLastError() ); return FALSE; + } - DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", %S", - child_pi.dwProcessId, - (lpApplicationName == NULL) ? "" : lpApplicationName, - (lpCommandLine == NULL) ? "" : lpCommandLine ); Inject( dwCreationFlags, lpProcessInformation, &child_pi, FALSE, lpApplicationName, lpCommandLine ); @@ -1375,6 +1394,13 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName, { PROCESS_INFORMATION child_pi; + DEBUGSTR( 1, L"CreateProcessW: %s%s%s, %s", + (lpApplicationName == NULL) ? L"" : L"\"", + (lpApplicationName == NULL) ? L"" : lpApplicationName, + (lpApplicationName == NULL) ? L"" : L"\"", + (lpCommandLine == NULL) ? L"" : + (*lpCommandLine == '\0') ? L"" : lpCommandLine ); + if (!CreateProcessW( lpApplicationName, lpCommandLine, lpThreadAttributes, @@ -1385,12 +1411,11 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName, lpCurrentDirectory, lpStartupInfo, &child_pi )) + { + DEBUGSTR( 1, L" Failed (%lu)", GetLastError() ); return FALSE; + } - DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", %s", - child_pi.dwProcessId, - (lpApplicationName == NULL) ? L"" : lpApplicationName, - (lpCommandLine == NULL) ? L"" : lpCommandLine ); Inject( dwCreationFlags, lpProcessInformation, &child_pi, TRUE, lpApplicationName, lpCommandLine ); @@ -1819,13 +1844,10 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) log_level = _wtoi( logstr ); prog = get_program_name( NULL ); #if defined(_WIN64) || defined(W32ON64) - hDllNameType = hDllName - 6 + + DllNameType = DllName - 6 + #endif - GetModuleFileName( hInstance, hDllName, lenof(hDllName) ); -#ifdef _WIN64 - ansi_bits = (LPSTR)hDllNameType; -#endif - set_ansi_dll( hDllName ); + GetModuleFileName( hInstance, DllName, lenof(DllName) ); + set_ansi_dll(); hDllInstance = hInstance; // save Dll instance handle #ifdef _WIN64 diff --git a/LLW.c b/LLW.c deleted file mode 100644 index bacf893..0000000 --- a/LLW.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - Locate the relative address of LoadLibraryW in kernel32.dll. This is needed - to get the 32-bit address from 64-bit code, and it eliminates the possibility - of it already being hooked. -*/ - -#include "ansicon.h" - -static PIMAGE_DOS_HEADER pDosHeader; - -#define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset)) - - -static int export_cmp( const void* a, const void* b ) -{ - return strcmp( (LPCSTR)a, MakeVA( LPCSTR, *(const PDWORD)b ) ); -} - - -DWORD get_LLW32r( void ) -{ - HMODULE kernel32; - TCHAR buf[MAX_PATH]; - UINT len; - PIMAGE_NT_HEADERS32 pNTHeader; - PIMAGE_EXPORT_DIRECTORY pExportDir; - PDWORD fun_table, name_table; - PWORD ord_table; - PDWORD pLLW; - DWORD LLWr; - -#ifdef _WIN64 - len = GetSystemWow64Directory( buf, MAX_PATH ); -#else - len = GetSystemDirectory( buf, MAX_PATH ); -#endif - wcscpy( buf + len, L"\\kernel32.dll" ); - kernel32 = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE ); - if (kernel32 == NULL) - { - DEBUGSTR( 1, L"Unable to load 32-bit kernel32.dll!" ); - return 0; - } - // The handle uses low bits as flags, so strip 'em off. - pDosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)kernel32 & ~0xFFFF); - pNTHeader = MakeVA( PIMAGE_NT_HEADERS32, pDosHeader->e_lfanew ); - pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, - pNTHeader->EXPORTDIR.VirtualAddress ); - - fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions ); - name_table = MakeVA( PDWORD, pExportDir->AddressOfNames ); - ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals ); - - pLLW = bsearch( "LoadLibraryW", name_table, pExportDir->NumberOfNames, - sizeof(DWORD), export_cmp ); - if (pLLW == NULL) - { - DEBUGSTR( 1, L"Could not find 32-bit LoadLibraryW!" ); - LLWr = 0; - } - else - { - LLWr = fun_table[ord_table[pLLW - name_table]]; - } - FreeLibrary( kernel32 ); - return LLWr; -} - - -#ifdef _WIN64 -DWORD64 get_LLW64r( void ) -{ - HMODULE kernel32; - TCHAR buf[MAX_PATH]; - UINT len; - PIMAGE_NT_HEADERS pNTHeader; - PIMAGE_EXPORT_DIRECTORY pExportDir; - PDWORD fun_table, name_table; - PWORD ord_table; - PDWORD pLLW; - DWORD64 LLWr; - - len = GetSystemDirectory( buf, MAX_PATH ); - wcscpy( buf + len, L"\\kernel32.dll" ); - kernel32 = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE ); - if (kernel32 == NULL) - { - DEBUGSTR( 1, L"Unable to load 64-bit kernel32.dll!" ); - return 0; - } - // The handle uses low bits as flags, so strip 'em off. - pDosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)kernel32 & ~0xFFFF); - pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); - pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, - pNTHeader->EXPORTDIR.VirtualAddress ); - - fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions ); - name_table = MakeVA( PDWORD, pExportDir->AddressOfNames ); - ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals ); - - pLLW = bsearch( "LoadLibraryW", name_table, pExportDir->NumberOfNames, - sizeof(DWORD), export_cmp ); - if (pLLW == NULL) - { - DEBUGSTR( 1, L"Could not find 64-bit LoadLibraryW!" ); - LLWr = 0; - } - else - { - LLWr = fun_table[ord_table[pLLW - name_table]]; - } - FreeLibrary( kernel32 ); - return LLWr; -} -#endif diff --git a/ansicon.c b/ansicon.c index 1f5eea7..1bdb27c 100644 --- a/ansicon.c +++ b/ansicon.c @@ -77,16 +77,17 @@ v1.63, 25 July, 2013: don't write the reset sequence if output is redirected. - v1.70, 31 January to 3 February, 2014: + v1.70, 31 January to 7 February, 2014: restore the original (current, not default) attributes if using ansicon.exe when it's already installed; use ANSICON_DEF if defined and -m not given; -e and -t will not output anything if the DLL could not load; use Unicode output (_O_U16TEXT, for compilers/systems that support it); - log: 64-bit addresses get an underscore between the 8-digit groups. + log: 64-bit addresses get an underscore between the 8-digit groups; + add error codes to some message. */ -#define PDATE L"4 February, 2014" +#define PDATE L"7 February, 2014" #include "ansicon.h" #include "version.h" @@ -168,14 +169,20 @@ int my_fputws( const wchar_t* s, FILE* f ) #define _putws( s ) my_fputws( s L"\n", stdout ) +#if defined(_WIN64) +LPTSTR DllNameType; +#endif + // Find the name of the DLL and inject it. BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app ) { DWORD len; - WCHAR dll[MAX_PATH]; int type; PBYTE base; +#ifdef _WIN64 + if (app != NULL) +#endif DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); type = ProcessType( ppi, &base, gui ); if (type <= 0) @@ -186,92 +193,109 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app ) } len = (DWORD)(prog - prog_path); - memcpy( dll, prog_path, TSIZE(len) ); + memcpy( DllName, prog_path, TSIZE(len) ); #ifdef _WIN64 - wsprintf( dll + len, L"ANSI%d.dll", type ); - ansi_bits = (LPSTR)(dll + len + 4); - set_ansi_dll( dll ); - if (type == 32) - InjectDLL32( ppi, base ); - else + wsprintf( DllName + len, L"ANSI%d.dll", (type == 48) ? 64 : type ); + DllNameType = DllName + len + 4; + set_ansi_dll(); + if (type == 64) InjectDLL( ppi, base ); + else if (type == 32) + InjectDLL32( ppi, base ); + else // (type == 48) + InjectDLL64( ppi ); #else - wcscpy( dll + len, L"ANSI32.dll" ); - set_ansi_dll( dll ); + wcscpy( DllName + len, L"ANSI32.dll" ); + set_ansi_dll(); InjectDLL( ppi, base ); #endif + return TRUE; } // Use CreateRemoteThread to load our DLL in the target process. -void RemoteLoad( LPPROCESS_INFORMATION ppi ) +void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app ) { HANDLE hSnap; MODULEENTRY32 me; PBYTE LLW; BOOL fOk; - WCHAR dll[MAX_PATH]; DWORD len; LPVOID mem; HANDLE thread; + DWORD ticks; #ifdef _WIN64 BOOL WOW64; + int type; #endif + DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); + // Find the base address of kernel32.dll. - LLW = NULL; - hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, - ppi->dwProcessId ); - if (hSnap != INVALID_HANDLE_VALUE) + ticks = GetTickCount(); + while ((hSnap = CreateToolhelp32Snapshot( + TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ppi->dwProcessId )) + == INVALID_HANDLE_VALUE) { - me.dwSize = sizeof(MODULEENTRY32); - for (fOk = Module32First( hSnap, &me ); fOk; - fOk = Module32Next( hSnap, &me )) - { - if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0) - { - LLW = me.modBaseAddr; - break; - } - } - CloseHandle( hSnap ); - } + DWORD err = GetLastError(); #ifndef _WIN64 - else if (GetLastError() == ERROR_PARTIAL_COPY) - { - fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr ); - return; - } + if (err == ERROR_PARTIAL_COPY) + { + DEBUGSTR( 1, L" Ignoring 64-bit process (use x64\\ansicon)" ); + fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr ); + return; + } #endif - if (LLW == NULL) - { - no_llw: + // I really don't think this would happen, but if it does, give up after + // two seconds to avoid a potentially infinite loop. + if (err == ERROR_BAD_LENGTH && GetTickCount() - ticks < 2000) + { + Sleep( 0 ); + continue; + } + DEBUGSTR( 1, L" Unable to create snapshot (%lu)", err ); + no_go: fputws( L"ANSICON: unable to inject into parent.\n", stderr ); return; } + LLW = NULL; + me.dwSize = sizeof(MODULEENTRY32); + for (fOk = Module32First( hSnap, &me ); fOk; fOk = Module32Next( hSnap, &me )) + { + if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0) + { + LLW = me.modBaseAddr; + break; + } + } + CloseHandle( hSnap ); + if (LLW == NULL) + { + DEBUGSTR( 1, L" Unable to locate kernel32.dll (%lu)", GetLastError() ); + goto no_go; + } len = (DWORD)(prog - prog_path); - memcpy( dll, prog_path, TSIZE(len) ); + memcpy( DllName, prog_path, TSIZE(len) ); #ifdef _WIN64 - if (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) - { - wcscpy( dll + len, L"ANSI32.dll" ); - LLW += get_LLW32r(); - } - else - { - wcscpy( dll + len, L"ANSI64.dll" ); - LLW += get_LLW64r(); - } + type = (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) ? 32 : 64; + wsprintf( DllName + len, L"ANSI%d.dll", type ); + LLW += GetProcRVA( L"kernel32.dll", "LoadLibraryW", type ); #else - wcscpy( dll + len, L"ANSI32.dll" ); - LLW += get_LLW32r(); + wcscpy( DllName + len, L"ANSI32.dll" ); + LLW += GetProcRVA( L"kernel32.dll", "LoadLibraryW" ); #endif if (LLW == me.modBaseAddr) - goto no_llw; + goto no_go; + mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE ); - WriteProcMem( mem, dll, TSIZE(len + 11) ); + if (mem == NULL) + { + DEBUGSTR( 1, L" Unable to allocate virtual memory (%lu)", GetLastError() ); + goto no_go; + } + WriteProcMem( mem, DllName, TSIZE(len + 11) ); thread = CreateRemoteThread( ppi->hProcess, NULL, 4096, (LPTHREAD_START_ROUTINE)LLW, mem, 0, NULL ); WaitForSingleObject( thread, INFINITE ); @@ -341,12 +365,18 @@ int main( void ) #ifdef _WIN64 if (*arg == '-' && arg[1] == 'P') { - swscanf( arg + 2, L"%u:%u", &pi.dwProcessId, &pi.dwThreadId ); + pi.dwProcessId = _wtoi( arg + 2 ); pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId ); - pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); - Inject( &pi, &gui, arg ); - CloseHandle( pi.hThread ); - CloseHandle( pi.hProcess ); + if (pi.hProcess == NULL) + { + DEBUGSTR( 1, L" Unable to open process %lu (%lu)", + pi.dwProcessId, GetLastError() ); + } + else + { + Inject( &pi, &gui, NULL ); + CloseHandle( pi.hProcess ); + } return 0; } #endif @@ -378,14 +408,10 @@ int main( void ) case 'p': shell = FALSE; - if (GetParentProcessInfo( &pi, arg + 3 )) + if (GetParentProcessInfo( &pi, arg )) { pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); - pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); - SuspendThread( pi.hThread ); - RemoteLoad( &pi ); - ResumeThread( pi.hThread ); - CloseHandle( pi.hThread ); + RemoteLoad( &pi, arg ); CloseHandle( pi.hProcess ); } else @@ -702,35 +728,30 @@ BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ) } -// Obtain the process and thread identifiers of the parent process. +// Obtain the process identifier of the parent process. BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name ) { HANDLE hSnap; PROCESSENTRY32 pe; - THREADENTRY32 te; BOOL fOk; - hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, 0 ); - + hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); if (hSnap == INVALID_HANDLE_VALUE) - return FALSE; - - find_proc_id( hSnap, GetCurrentProcessId(), &pe ); - if (!find_proc_id( hSnap, pe.th32ParentProcessID, &pe )) { - CloseHandle( hSnap ); + DEBUGSTR( 1, L"Failed to create snapshot (%lu)", GetLastError() ); return FALSE; } - te.dwSize = sizeof(te); - for (fOk = Thread32First( hSnap, &te ); fOk; fOk = Thread32Next( hSnap, &te )) - if (te.th32OwnerProcessID == pe.th32ProcessID) - break; - + fOk = (find_proc_id( hSnap, GetCurrentProcessId(), &pe ) && + find_proc_id( hSnap, pe.th32ParentProcessID, &pe )); CloseHandle( hSnap ); + if (!fOk) + { + DEBUGSTR( 1, L"Failed to locate parent" ); + return FALSE; + } ppi->dwProcessId = pe.th32ProcessID; - ppi->dwThreadId = te.th32ThreadID; wcscpy( name, pe.szExeFile ); return fOk; @@ -897,7 +918,7 @@ L" [-e|E string | -t|T [file(s)] | program [args]]\n" L"\n" L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n" L" \t\t +4=output, +8=append) for program (-p is unaffected)\n" -L" -i\t\tinstall - add ANSICON to the AutoRun entry (also implies -p)\n" +L" -i\t\tinstall - add ANSICON to CMD's AutoRun entry (also implies -p)\n" L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n" L" -I -U\t\tuse local machine instead of current user\n" L" -m\t\tuse grey on black (\"monochrome\") or as default color\n" diff --git a/ansicon.h b/ansicon.h index d5c62a5..fb0b272 100644 --- a/ansicon.h +++ b/ansicon.h @@ -15,15 +15,12 @@ #ifdef _WIN64 #define _WIN32_WINNT 0x0600 // MinGW-w64 wants this defined for Wow64 stuff #else -#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread +#define _WIN32_WINNT 0x0500 #endif #include #include #include -#define lenof(array) (sizeof(array)/sizeof(*(array))) -#define TSIZE(size) ((size) * sizeof(TCHAR)) - #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20 #endif @@ -31,13 +28,19 @@ #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x20 #endif +#define lenof(array) (sizeof(array)/sizeof(*(array))) +#define TSIZE(size) ((size) * sizeof(TCHAR)) +#define PTRSZ sizeof(PVOID) + +// Macro for adding pointers/DWORDs together without C arithmetic interfering +#define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset)) + #define EXPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] #define IMPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] #define BOUNDDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] #define IATDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] #define COMDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] - // Reduce the verbosity of some functions (assuming variable names). #define ReadProcVar(a, b) ReadProcMem( a, b, sizeof(*(b)) ) #define WriteProcVar(a, b) WriteProcMem( a, b, sizeof(*(b)) ) @@ -45,8 +48,6 @@ #define WriteProcMem(a, b, c) WriteProcessMemory( ppi->hProcess, a, b, c, NULL ) #define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr ) -#define PTRSZ sizeof(PVOID) - typedef struct { @@ -60,24 +61,31 @@ typedef struct } GRM, *PGRM; // Graphic Rendition Mode -int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* ); +int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* ); +BOOL Wow64Process( HANDLE ); -void InjectDLL( LPPROCESS_INFORMATION, PBYTE ); -void InjectDLL32( LPPROCESS_INFORMATION, PBYTE ); +void InjectDLL( LPPROCESS_INFORMATION, PBYTE ); +#ifdef _WIN64 +void InjectDLL32( LPPROCESS_INFORMATION, PBYTE ); +void InjectDLL64( LPPROCESS_INFORMATION ); +DWORD GetProcRVA( LPCTSTR, LPCSTR, int ); +#else +DWORD GetProcRVA( LPCTSTR, LPCSTR ); +#endif -DWORD get_LLW32r( void ); -DWORD64 get_LLW64r( void ); extern TCHAR prog_path[MAX_PATH]; extern LPTSTR prog; LPTSTR get_program_name( LPTSTR ); -extern char ansi_dll[MAX_PATH]; -extern DWORD ansi_len; -extern char* ansi_bits; -void set_ansi_dll( LPTSTR ); +extern TCHAR DllName[MAX_PATH]; +extern LPTSTR DllNameType; +extern char ansi_dll[MAX_PATH]; +extern DWORD ansi_len; +extern char* ansi_bits; +void set_ansi_dll( void ); extern int log_level; -void DEBUGSTR( int level, LPTSTR szFormat, ... ); +void DEBUGSTR( int level, LPTSTR szFormat, ... ); #endif diff --git a/injdll.c b/injdll.c index 7efe624..1d6dc17 100644 --- a/injdll.c +++ b/injdll.c @@ -1,14 +1,17 @@ /* Inject the DLL into the target process by modifying its import descriptor - table. The target process must have been created suspended. + table. The target process must have been created suspended. However, for a + 64-bit system with a .NET AnyCPU process, inject via LdrLoadDll in ntdll.dll + and CreateRemoteThread (since AnyCPU is stored as i386, but loads as AMD64, + preventing imports from working). */ #include "ansicon.h" -// Search for a suitable free area after the main image. (32-bit code could +// Search for a suitable free area after the main image (32-bit code could // really go anywhere, but let's keep it relatively local.) -PVOID FindMem( HANDLE hProcess, PBYTE base, DWORD len ) +static PVOID FindMem( HANDLE hProcess, PBYTE base, DWORD len ) { MEMORY_BASIC_INFORMATION minfo; PBYTE ptr; @@ -43,20 +46,20 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase ) DWORD pr; IMAGE_DOS_HEADER DosHeader; IMAGE_NT_HEADERS NTHeader, *pNTHeader; - PIMAGE_IMPORT_DESCRIPTOR pImports, pANSI_ImportDesc; + PIMAGE_IMPORT_DESCRIPTOR pImports; IMAGE_COR20_HEADER ComHeader, *pComHeader; union { PBYTE pB; PLONG_PTR pL; + PIMAGE_IMPORT_DESCRIPTOR pI; } ip; ReadProcVar( pBase, &DosHeader ); pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew); ReadProcVar( pNTHeader, &NTHeader ); - len = 4 * PTRSZ + ansi_len - + sizeof(*pANSI_ImportDesc) + NTHeader.IMPORTDIR.Size; + len = 4 * PTRSZ + ansi_len + sizeof(*pImports) + NTHeader.IMPORTDIR.Size; pImports = malloc( len ); if (pImports == NULL) { @@ -79,24 +82,23 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase ) *ip.pL++ = 0; memcpy( ip.pB, ansi_dll, ansi_len ); ip.pB += ansi_len; - pANSI_ImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ip.pB; - pANSI_ImportDesc->OriginalFirstThunk = rva + 2 * PTRSZ; - pANSI_ImportDesc->TimeDateStamp = 0; - pANSI_ImportDesc->ForwarderChain = 0; - pANSI_ImportDesc->Name = rva + 4 * PTRSZ; - pANSI_ImportDesc->FirstThunk = rva; + ip.pI->OriginalFirstThunk = rva + 2 * PTRSZ; + ip.pI->TimeDateStamp = 0; + ip.pI->ForwarderChain = 0; + ip.pI->Name = rva + 4 * PTRSZ; + ip.pI->FirstThunk = rva; ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress, - pANSI_ImportDesc + 1, NTHeader.IMPORTDIR.Size ); + ip.pI + 1, NTHeader.IMPORTDIR.Size ); WriteProcMem( pMem, pImports, len ); free( pImports ); - NTHeader.IMPORTDIR.VirtualAddress = rva + 4 * PTRSZ + ansi_len; - NTHeader.IMPORTDIR.Size += sizeof(*pANSI_ImportDesc); - - // If there's no IAT, copy the IDT. + // If there's no IAT, copy the original IDT (to allow writable ".idata"). if (NTHeader.IATDIR.VirtualAddress == 0) NTHeader.IATDIR = NTHeader.IMPORTDIR; + NTHeader.IMPORTDIR.VirtualAddress = rva + 4 * PTRSZ + ansi_len; + NTHeader.IMPORTDIR.Size += sizeof(*pImports); + // Remove bound imports, so the updated import table is used. NTHeader.BOUNDDIR.VirtualAddress = 0; NTHeader.BOUNDDIR.Size = 0; @@ -130,19 +132,20 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase ) DWORD pr; IMAGE_DOS_HEADER DosHeader; IMAGE_NT_HEADERS32 NTHeader, *pNTHeader; - PIMAGE_IMPORT_DESCRIPTOR pImports, pANSI_ImportDesc; + PIMAGE_IMPORT_DESCRIPTOR pImports; IMAGE_COR20_HEADER ComHeader, *pComHeader; union { PBYTE pB; PLONG pL; + PIMAGE_IMPORT_DESCRIPTOR pI; } ip; ReadProcVar( pBase, &DosHeader ); pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew); ReadProcVar( pNTHeader, &NTHeader ); - len = 16 + ansi_len + sizeof(*pANSI_ImportDesc) + NTHeader.IMPORTDIR.Size; + len = 16 + ansi_len + sizeof(*pImports) + NTHeader.IMPORTDIR.Size; pImports = malloc( len ); if (pImports == NULL) { @@ -165,21 +168,20 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase ) *ip.pL++ = 0; memcpy( ip.pB, ansi_dll, ansi_len ); ip.pB += ansi_len; - pANSI_ImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ip.pB; - pANSI_ImportDesc->OriginalFirstThunk = rva + 8; - pANSI_ImportDesc->TimeDateStamp = 0; - pANSI_ImportDesc->ForwarderChain = 0; - pANSI_ImportDesc->Name = rva + 16; - pANSI_ImportDesc->FirstThunk = rva; + ip.pI->OriginalFirstThunk = rva + 8; + ip.pI->TimeDateStamp = 0; + ip.pI->ForwarderChain = 0; + ip.pI->Name = rva + 16; + ip.pI->FirstThunk = rva; ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress, - pANSI_ImportDesc + 1, NTHeader.IMPORTDIR.Size ); + ip.pI + 1, NTHeader.IMPORTDIR.Size ); WriteProcMem( pMem, pImports, len ); free( pImports ); - NTHeader.IMPORTDIR.VirtualAddress = rva + 16 + ansi_len; - NTHeader.IMPORTDIR.Size += sizeof(*pANSI_ImportDesc); if (NTHeader.IATDIR.VirtualAddress == 0) NTHeader.IATDIR = NTHeader.IMPORTDIR; + NTHeader.IMPORTDIR.VirtualAddress = rva + 16 + ansi_len; + NTHeader.IMPORTDIR.Size += sizeof(*pImports); NTHeader.BOUNDDIR.VirtualAddress = 0; NTHeader.BOUNDDIR.Size = 0; VirtProtVar( pNTHeader, PAGE_READWRITE ); @@ -199,4 +201,98 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase ) } } } + + +/* + Locate the base address of 64-bit ntdll.dll. This is supposedly really at + the same address for every process, but let's find it anyway. A newly- + created suspended 64-bit process has two images in memory: the process itself + and ntdll.dll - the one that is a DLL must be ntdll.dll. (A 32-bit WOW64 + process has three images - the process and both 64- & 32-bit ntdll.dll). +*/ +static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi ) +{ + PBYTE ptr; + MEMORY_BASIC_INFORMATION minfo; + IMAGE_DOS_HEADER dos_header; + IMAGE_NT_HEADERS nt_header; + + for (ptr = NULL; + VirtualQueryEx( ppi->hProcess, ptr, &minfo, sizeof(minfo) ); + ptr += minfo.RegionSize) + { + if (minfo.BaseAddress == minfo.AllocationBase + && ReadProcVar( minfo.BaseAddress, &dos_header ) + && dos_header.e_magic == IMAGE_DOS_SIGNATURE + && ReadProcVar( (PBYTE)minfo.BaseAddress + dos_header.e_lfanew, + &nt_header ) + && nt_header.Signature == IMAGE_NT_SIGNATURE + && (nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL)) + { + return minfo.BaseAddress; + } + } + + DEBUGSTR( 1, L" Failed to find ntdll.dll!" ); + return NULL; +} + + +void InjectDLL64( LPPROCESS_INFORMATION ppi ) +{ + PBYTE ntdll; + DWORD rLdrLoadDll; + PBYTE pMem; + DWORD len; + HANDLE thread; + BYTE code[64]; + union + { + PBYTE pB; + PUSHORT pS; + PDWORD pD; + PBYTE* pL; + } ip; + + ntdll = get_ntdll( ppi ); + if (ntdll == NULL) + return; + + rLdrLoadDll = GetProcRVA( L"ntdll.dll", "LdrLoadDll", 64 ); + if (rLdrLoadDll == 0) + return; + + pMem = VirtualAllocEx( ppi->hProcess, NULL, 4096, MEM_COMMIT, + PAGE_EXECUTE_READ ); + if (pMem == NULL) + { + DEBUGSTR( 1, L" Failed to allocate virtual memory (%lu)", GetLastError() ); + return; + } + + len = (DWORD)TSIZE(wcslen( DllName ) + 1); + ip.pB = code; + + *ip.pL++ = ntdll + rLdrLoadDll; // address of LdrLoadDll + *ip.pD++ = 0x38ec8348; // sub rsp, 0x38 + *ip.pD++ = 0x244c8d4c; // lea r9, [rsp+0x20] + *ip.pD++ = 0x058d4c20; // lea r8, L"path\to\ANSI64.dll" + *ip.pD++ = 16; // xor edx, edx + *ip.pD++ = 0xc933d233; // xor ecx, ecx + *ip.pS++ = 0x15ff; // call LdrLoadDll + *ip.pD++ = -34; // add rsp, 0x38 + *ip.pD++ = 0x38c48348; // ret + *ip.pS++ = 0x00c3; // alignment for the name + *ip.pS++ = (USHORT)(len - TSIZE(1)); // UNICODE_STRING.Length + *ip.pS++ = (USHORT)len; // UNICODE_STRING.MaximumLength + *ip.pD++ = 0; // padding + *ip.pL++ = pMem + 56; // UNICODE_STRING.Buffer + WriteProcMem( pMem, code, ip.pB - code ); + WriteProcMem( pMem + (ip.pB - code), DllName, len ); + thread = CreateRemoteThread( ppi->hProcess, NULL, 4096, + (LPTHREAD_START_ROUTINE)(pMem + 8), NULL, 0, NULL ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + VirtualFreeEx( ppi->hProcess, pMem, 0, MEM_RELEASE ); +} #endif diff --git a/makefile.gcc b/makefile.gcc index 6a5b781..bbc9e36 100644 --- a/makefile.gcc +++ b/makefile.gcc @@ -44,8 +44,8 @@ endif endif endif -X86OBJS = x86/proctype.o x86/injdll.o x86/util.o -X64OBJS = x64/proctype.o x64/injdll.o x64/util.o +X86OBJS = x86/injdll.o x86/proctype.o x86/util.o +X64OBJS = x64/injdll.o x64/proctype.o x64/util.o x64/procrva.o # Determine the appropriate separator to run multiple commands - ";" for sh.exe # and "&" for CMD.EXE. $(SHELL) is initially defined to "sh.exe" - if it @@ -92,7 +92,7 @@ ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x86: cmd /c "mkdir x86" -x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/LLW.o x86/ansiconv.o +x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/procrva.o x86/ansiconv.o $(LDmsg)$(CC) -m32 $+ -s -o $@ $(IVER) x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o @@ -101,7 +101,7 @@ x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o x64: cmd /c "mkdir x64" -x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/LLW.o x64/ansiconv.o +x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o $(LDmsg)$(CC) -m64 $+ -s -o $@ $(IVER) x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o diff --git a/makefile.vc b/makefile.vc index bcca7c2..04febb9 100644 --- a/makefile.vc +++ b/makefile.vc @@ -65,8 +65,8 @@ LIBS = advapi32.lib user32.lib $(LIBS64) # Identify ansicon.exe using "ANSI" as a version number. LINK = /link /version:20033.18771 -X86OBJS = x86\proctype.obj x86\injdll.obj x86\util.obj -X64OBJS = x64\proctype.obj x64\injdll.obj x64\util.obj +X86OBJS = x86\injdll.obj x86\proctype.obj x86\util.obj +X64OBJS = x64\injdll.obj x64\proctype.obj x64\util.obj x64\procrva.obj !IF !DEFINED(V) V = 0 @@ -93,7 +93,7 @@ ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll x86: mkdir x86 -x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\LLW.obj x86\ansicon.res +x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\procrva.obj x86\ansicon.res $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512 !IF "$(_NMAKE_VER)" == "9.00.30729.01" $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1 @@ -111,7 +111,7 @@ x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res x64: mkdir x64 -x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\LLW.obj x64\ansicon.res +x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res @@ -133,7 +133,7 @@ ANSI.rc: version.h util.c: ansicon.h version.h injdll.c: ansicon.h proctype.c: ansicon.h -LLW.c: ansicon.h +procrva.c: ansicon.h x64\ANSI32.obj: ANSI.c $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? diff --git a/procrva.c b/procrva.c new file mode 100644 index 0000000..259f9b1 --- /dev/null +++ b/procrva.c @@ -0,0 +1,85 @@ +/* + Get the RVA of a function directly from a module. This allows 64-bit code to + work with 32-bit DLLs, and eliminates (or at least reduces) the possibility + of the function already being hooked. +*/ + +#include "ansicon.h" + +static PIMAGE_DOS_HEADER pDosHeader; + + +static int export_cmp( const void* a, const void* b ) +{ + return strcmp( (LPCSTR)a, MakeVA( LPCSTR, *(const PDWORD)b ) ); +} + + +#ifdef _WIN64 +DWORD GetProcRVA( LPCTSTR module, LPCSTR func, int bits ) +#else +DWORD GetProcRVA( LPCTSTR module, LPCSTR func ) +#endif +{ + HMODULE hMod; + TCHAR buf[MAX_PATH]; + UINT len; + PIMAGE_NT_HEADERS pNTHeader; + PIMAGE_EXPORT_DIRECTORY pExportDir; + PDWORD fun_table, name_table; + PWORD ord_table; + PDWORD pFunc; + DWORD rva; + +#ifdef _WIN64 + if (bits == 32) + len = GetSystemWow64Directory( buf, MAX_PATH ); + else +#endif + len = GetSystemDirectory( buf, MAX_PATH ); + buf[len++] = '\\'; + wcscpy( buf + len, module ); + hMod = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE ); + if (hMod == NULL) + { +#ifdef _WIN64 + DEBUGSTR( 1, L"Unable to load %d-bit %s (%lu)!", + bits, module, GetLastError() ); +#else + DEBUGSTR( 1, L"Unable to load %s (%lu)!", module, GetLastError() ); +#endif + return 0; + } + // The handle uses low bits as flags, so strip 'em off. + pDosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)hMod & ~0xFFFF); + pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); +#ifdef _WIN64 + if (bits == 32) + pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, + ((PIMAGE_NT_HEADERS32)pNTHeader)->EXPORTDIR.VirtualAddress ); + else +#endif + pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, + pNTHeader->EXPORTDIR.VirtualAddress ); + fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions ); + name_table = MakeVA( PDWORD, pExportDir->AddressOfNames ); + ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals ); + + pFunc = bsearch( func, name_table, pExportDir->NumberOfNames, + sizeof(DWORD), export_cmp ); + if (pFunc == NULL) + { +#ifdef _WIN64 + DEBUGSTR( 1, L"Could not find %d-bit %s!", bits, func ); +#else + DEBUGSTR( 1, L"Could not find %s!", func ); +#endif + rva = 0; + } + else + { + rva = fun_table[ord_table[pFunc - name_table]]; + } + FreeLibrary( hMod ); + return rva; +} diff --git a/proctype.c b/proctype.c index 646b9a9..da17093 100644 --- a/proctype.c +++ b/proctype.c @@ -2,12 +2,44 @@ Test for a valid process (i386 for x86; that or AMD64 for x64). We can get that info from the image header, which means getting the process's base address (which we need anyway, to modify the imports). The simplest way to - do that is to enumerate the pages, looking for an executable image. + do that is to enumerate the pages, looking for an executable image. A .NET + AnyCPU process has a 32-bit structure, but will load as 64-bit when possible. + The 64-bit version (both DLLs) will say this is type 48 (halfway between 32 & + 64); the 32-bit version will ignore it if run on a 64-bit OS. */ #include "ansicon.h" +#if !defined(_WIN64) && !defined(W32ON64) +static BOOL ProcessIs64( HANDLE hProcess ) +{ + BOOL wow; + + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL ); + static LPFN_ISWOW64PROCESS fnIsWow64Process; + + if (fnIsWow64Process == INVALID_HANDLE_VALUE) + return FALSE; + + if (fnIsWow64Process == NULL) + { + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( + GetModuleHandle( L"kernel32.dll" ), "IsWow64Process" ); + if (fnIsWow64Process == NULL) + { + fnIsWow64Process = INVALID_HANDLE_VALUE; + return FALSE; + } + } + + // If IsWow64Process fails, say it is 64, since injection probably wouldn't + // work, either. + return !(fnIsWow64Process( hProcess, &wow ) && wow); +} +#endif + + int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui ) { PBYTE ptr; @@ -43,6 +75,34 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui ) { if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { + PIMAGE_NT_HEADERS32 pNTHeader = (PIMAGE_NT_HEADERS32)&nt_header; + if (pNTHeader->COMDIR.VirtualAddress != 0 && + pNTHeader->COMDIR.Size != 0) + { + IMAGE_COR20_HEADER ComHeader, *pComHeader; + pComHeader = (PIMAGE_COR20_HEADER)((PBYTE)minfo.BaseAddress + + pNTHeader->COMDIR.VirtualAddress); + ReadProcVar( pComHeader, &ComHeader ); + if ((ComHeader.Flags & COMIMAGE_FLAGS_ILONLY) && + !(ComHeader.Flags & COMIMAGE_FLAGS_32BITREQUIRED)) + { +#if defined(_WIN64) || !defined(W32ON64) // W32ON64 will log due to -P + DEBUGSTR( 1, L" AnyCPU %s (base = %.8X)", + (*gui) ? L"GUI" : L"console", + PtrToUint( minfo.BaseAddress ) ); +#endif +#if defined(_WIN64) || defined(W32ON64) + return 48; +#else + if (ProcessIs64( ppi->hProcess )) + { + DEBUGSTR( 1, L" Unsupported (use x64\\ansicon)" ); + return 0; + } + return 32; +#endif + } + } DEBUGSTR( 1, L" 32-bit %s (base = %.8X)", (*gui) ? L"GUI" : L"console", PtrToUint( minfo.BaseAddress ) ); diff --git a/util.c b/util.c index a4e2c77..63c4e53 100644 --- a/util.c +++ b/util.c @@ -11,6 +11,7 @@ LPTSTR prog; int log_level; +TCHAR DllName[MAX_PATH]; // Dll file name char ansi_dll[MAX_PATH]; DWORD ansi_len; #ifdef _WIN64 @@ -43,31 +44,27 @@ LPTSTR get_program_name( LPTSTR program ) // Get the ANSI path of the DLL for the import. If it can't be converted, -// just use the name and hope it's on the PATH. Returns the length of the -// path/name, including padding to make it dword-aligned. The 64-bit version -// expects ansi_bits to point to the size within dll on entry. -void set_ansi_dll( LPTSTR dll ) +// just use the name and hope it's on the PATH. +void set_ansi_dll( void ) { BOOL bad; - ansi_len = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, dll, -1, + ansi_len = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, DllName, -1, NULL, 0, NULL, &bad ); if (bad || ansi_len > MAX_PATH) { - ansi_len = 12; - memcpy( ansi_dll, "ANSI32.dll\0", 12 ); #ifdef _WIN64 - if (*ansi_bits == '6') - { - ansi_dll[4] = '6'; - ansi_dll[5] = '4'; - } ansi_bits = ansi_dll + 4; + if (*DllNameType == '6') + memcpy( ansi_dll, "ANSI64.dll\0", 12 ); + else #endif + memcpy( ansi_dll, "ANSI32.dll\0", 12 ); + ansi_len = 12; } else { - WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, dll, -1, + WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, DllName, -1, ansi_dll, MAX_PATH, NULL, NULL ); #ifdef _WIN64 ansi_bits = ansi_dll + ansi_len - 7; @@ -104,6 +101,9 @@ void DEBUGSTR( int level, LPTSTR szFormat, ... ) { SYSTEMTIME now; GetLocalTime( &now ); + fseek( file, 0, SEEK_END ); + if (ftell( file ) != 0) + putc( '\n', file ); fprintf( file, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d) started " "%d-%.2d-%.2d %d:%.2d:%.2d\n", log_level,