diff --git a/ANSI-LLW.asm b/ANSI-LLW.asm new file mode 100644 index 0000000..e26cc61 --- /dev/null +++ b/ANSI-LLW.asm @@ -0,0 +1,23 @@ +; ANSI-LLW.asm - Output the 32-bit address of LoadLibraryW. +; +; Jason Hood, 1 February, 2013. +; +; A FASM (flatassembler.net) version of the C code, which virus scanners didn't +; like for some reason. + + +format PE Console 4.0 + +include 'win32a.inc' + + mov eax, [LoadLibraryW] + ret + +data import + + library kernel32,'KERNEL32.DLL' + + import kernel32,\ + LoadLibraryW,'LoadLibraryW' + +end data diff --git a/ANSI-LLW.c b/ANSI-LLW.c new file mode 100644 index 0000000..28991f0 --- /dev/null +++ b/ANSI-LLW.c @@ -0,0 +1,22 @@ +/* + ANSI-LLW.c - Output the 32-bit address of LoadLibraryW. + + Jason Hood, 13 November, 2010 (LLA version 5 September, 2010). + + I don't know of a method to retrieve the 32-bit address of a function in + 64-bit code, so this is a simple workaround. + + 18 December, 2010: Initially I used GetProcAddress, but then I thought that + was silly, why don't I just return LoadLibraryW directly? That worked fine + for TDM64 and VC, but MinGW32 would return the address of the jump to the + function, not the function itself. Not so silly after all. +*/ + +#define WIN32_LEAN_AND_MEAN +#include + +int main( void ) +{ + return (DWORD)GetProcAddress( GetModuleHandle( "kernel32.dll" ), + "LoadLibraryW" ); +} diff --git a/ANSI.c b/ANSI.c index 9673d55..dc19cbb 100644 --- a/ANSI.c +++ b/ANSI.c @@ -95,6 +95,9 @@ v1.60, 22 to 24 November, 2012: alternative method to obtain LLW for 64->32 injection; support for VC6 (remove section pragma, rename isdigit to is_digit). + + v1.61, 14 February, 2013: + go back to using ANSI-LLW.exe for 64->32 injection. */ #include "ansicon.h" diff --git a/ansicon.c b/ansicon.c index 51bb144..4725ac1 100644 --- a/ansicon.c +++ b/ansicon.c @@ -71,7 +71,7 @@ write the date if appending to the log. */ -#define PDATE L"24 November, 2012" +#define PDATE L"14 February, 2013" #include "ansicon.h" #include "version.h" diff --git a/injdll32.c b/injdll32.c index c62b10b..738b742 100644 --- a/injdll32.c +++ b/injdll32.c @@ -32,96 +32,9 @@ TWow64SetThreadContext Wow64SetThreadContext; #define GetThreadContext Wow64GetThreadContext #define SetThreadContext Wow64SetThreadContext -#define MakeVA( cast, offset ) (cast)((DWORD_PTR)base + (DWORD)(offset)) - -extern DWORD LLW32; -LPVOID base; - -int export_cmp( const void* a, const void* b ) -{ - return strcmp( (LPCSTR)a, MakeVA( LPCSTR, *(const PDWORD)b ) ); -} - - -/* - Get the address of the 32-bit LoadLibraryW function from 64-bit code. This - was originally done via executing a helper program (ANSI-LLW.exe), but for - some reason, virus scanners really didn't like it (even when I rewrote it in - assembly so it was 1024 bytes and literally two instructions, virustotal - still had three scanners complaining). Now I do it the "hard" way - load the - 32-bit kernel32.dll directly and search the exports. Even worse, it seems - Wow64 loads kernel32.dll at a different base address each boot (at least, I - hope it only changes each boot). Fortunately, loading the DLL as an image in - 64-bit code seems to use the 32-bit address. -*/ -BOOL get_LLW32( void ) -{ - HMODULE kernel32; - TCHAR buf[MAX_PATH]; - UINT len; - PIMAGE_DOS_HEADER pDosHeader; - PIMAGE_NT_HEADERS32 pNTHeader; - PIMAGE_EXPORT_DIRECTORY pExportDir; - PDWORD fun_table, name_table; - PWORD ord_table; - PDWORD pLLW; - - len = GetSystemWow64Directory( buf, MAX_PATH ); - wcscpy( buf + len, L"\\kernel32.dll" ); - // MinGW-w64 has a typo, calling it LINRARY. - kernel32 = LoadLibraryEx( buf, NULL, 0x20/*LOAD_LIBRARY_AS_IMAGE_RESOURCE*/ ); - if (kernel32 == NULL) - { - DEBUGSTR( 1, L"Unable to load 32-bit kernel32.dll!" ); - return FALSE; - } - // The handle uses low bits as flags, so strip 'em off. - base = (LPVOID)((DWORD_PTR)kernel32 & 0xFFFF0000); - - // Tests to make sure we're looking at a module image (the 'MZ' header) - pDosHeader = (PIMAGE_DOS_HEADER)base; - if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) - { - DEBUGSTR( 1, L"Image has no DOS header!" ); - return FALSE; - } - - // The MZ header has a pointer to the PE header - pNTHeader = MakeVA( PIMAGE_NT_HEADERS32, pDosHeader->e_lfanew ); - - // One more test to make sure we're looking at a "PE" image - if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) - { - DEBUGSTR( 1, L"Image has no NT header!" ); - return FALSE; - } - - // We now have a valid pointer to the module's PE header. - // Get a pointer to its exports section. - pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, - pNTHeader->OptionalHeader. - DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. - 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 LoadLibraryW!" ); - return FALSE; - } - LLW32 = MakeVA( DWORD, fun_table[ord_table[pLLW - name_table]] ); - - FreeLibrary( kernel32 ); - return TRUE; -} -#else -DWORD LLW32; +extern #endif +DWORD LLW32; void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) @@ -138,6 +51,22 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) PDWORD pL; } ip; +#ifdef IMPORT_WOW64 + if (Wow64GetThreadContext == 0) + { + #define GETPROC( proc ) proc = (T##proc)GetProcAddress( hKernel, #proc ) + HMODULE hKernel = GetModuleHandle( L"kernel32.dll" ); + GETPROC( Wow64GetThreadContext ); + GETPROC( Wow64SetThreadContext ); + // Assume if one is defined, so is the other. + if (Wow64GetThreadContext == 0) + { + DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext." ); + return; + } + } +#endif + len = TSIZE(lstrlen( dll ) + 1); if (len > TSIZE(MAX_PATH)) return; @@ -145,28 +74,29 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) if (LLW32 == 0) { #ifdef _WIN64 - if (!get_LLW32()) + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + // ...ANSI32.dll\0 + CopyMemory( code, dll, len - TSIZE(7) ); + // ...ANSI-LLW.exe\0 + CopyMemory( code + len - TSIZE(7), L"-LLW.exe", TSIZE(9) ); + if (!CreateProcess( (LPCTSTR)code, NULL, NULL, NULL, FALSE, 0, NULL, NULL, + &si, &pi )) + { + DEBUGSTR( 1, L"Failed to execute \"%s\".", (LPCTSTR)code ); return; + } + WaitForSingleObject( pi.hProcess, INFINITE ); + GetExitCodeProcess( pi.hProcess, &LLW32 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); #else - LLW32 = (DWORD)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), + LLW32 = (DWORD)GetProcAddress( GetModuleHandle( L"kernel32.dll" ), "LoadLibraryW" ); #endif } -#ifdef IMPORT_WOW64 - if (Wow64GetThreadContext == 0) - { - #define GETPROC( proc ) proc = (T##proc)GetProcAddress( hKernel, #proc ) - HMODULE hKernel = GetModuleHandleA( "kernel32.dll" ); - GETPROC( Wow64GetThreadContext ); - GETPROC( Wow64SetThreadContext ); - // Assume if one is defined, so is the other. - if (Wow64GetThreadContext == 0) - { - DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext.\n" ); - return; - } - } -#endif CopyMemory( code + CODESIZE, dll, len ); len += CODESIZE; diff --git a/makefile b/makefile index b12f961..e9c4309 100644 --- a/makefile +++ b/makefile @@ -36,7 +36,7 @@ all: ansicon32 ansicon64 ansicon32: x86 x86/ansicon.exe x86/ANSI32.dll -ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll +ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll x64/ANSI-LLW.exe x86: cmd /c "mkdir x86" @@ -59,6 +59,9 @@ x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o x64/ANSI32.dll: x64/ANSI32.o x64/proctype32.o x86/injdll32.o x86/util.o x86/ansiv.o $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC0000 +x64/ANSI-LLW.exe: ANSI-LLW.c + $(CC) -m32 $(CFLAGS) $< -s -o $@ + x86/ansicon.o: version.h x86/ANSI.o: version.h x64/ansicon.o: version.h diff --git a/makefile.vc b/makefile.vc index 77a8328..5e51425 100644 --- a/makefile.vc +++ b/makefile.vc @@ -63,7 +63,7 @@ X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj all: ansicon$(BITS) -ansicon32: x86 x86\ansicon.exe x86\ANSI32.dll x64\ANSI32.dll +ansicon32: x86 x86\ansicon.exe x86\ANSI32.dll x64 x64\ANSI32.dll x64\ANSI-LLW.exe ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll @@ -88,6 +88,9 @@ x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res x64\ANSI32.dll: x64\ANSI32.obj x64\proctype32.obj x86\injdll32.obj x86\util.obj x86\ansi.res $(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link /base:0xAC0000 /section:.shared,s /filealign:512 +x64\ANSI-LLW.exe: ANSI-LLW.c + $(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? $(LIBS64) + ansicon.c: ansicon.h version.h ansicon.rc: version.h ANSI.c: ansicon.h version.h diff --git a/version.h b/version.h index 189162c..a5dcde9 100644 --- a/version.h +++ b/version.h @@ -2,8 +2,8 @@ version.h - Version defines. */ -#define PVERS L"1.60" // wide string -#define PVERSA "1.60" // ANSI string (windres 2.16.91 didn't like L) -#define PVERE L"160" // wide environment string -#define PVEREA "160" // ANSI environment string -#define PVERB 1,6,0,0 // binary (resource) +#define PVERS L"1.61" // wide string +#define PVERSA "1.61" // ANSI string (windres 2.16.91 didn't like L) +#define PVERE L"161" // wide environment string +#define PVEREA "161" // ANSI environment string +#define PVERB 1,6,1,0 // binary (resource)