Work with 64-bit AnyCPU; copy original IDT to IAT; log improvements.

This commit is contained in:
Jason Hood 2014-02-08 01:10:51 +10:00
parent dc7569dc26
commit db36552c42
10 changed files with 532 additions and 355 deletions

204
ANSI.c
View File

@ -111,12 +111,14 @@
v1.66, 20 & 21 September, 2013: v1.66, 20 & 21 September, 2013:
fix 32-bit process trying to detect 64-bit process. 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; don't hook ourself from LoadLibrary or LoadLibraryEx;
update the LoadLibraryEx flags that should not cause hooking; 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); 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" #include "ansicon.h"
@ -956,10 +958,6 @@ ParseAndPrintString( HANDLE hDev,
// - Matt Pietrek ~ Windows 95 System Programming Secrets. // - Matt Pietrek ~ Windows 95 System Programming Secrets.
// - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. // - 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 APIKernel[] = "kernel32.dll";
const char APIConsole[] = "API-MS-Win-Core-Console-"; const char APIConsole[] = "API-MS-Win-Core-Console-";
const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-"; const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-";
@ -987,9 +985,8 @@ API_DATA APIs[] =
HMODULE hKernel; // Kernel32 module handle HMODULE hKernel; // Kernel32 module handle
HINSTANCE hDllInstance; // Dll instance handle HINSTANCE hDllInstance; // Dll instance handle
TCHAR hDllName[MAX_PATH]; // Dll file name
#if defined(_WIN64) || defined(W32ON64) #if defined(_WIN64) || defined(W32ON64)
LPTSTR hDllNameType; // pointer to process type within above LPTSTR DllNameType; // pointer to process type within DllName
#endif #endif
typedef struct typedef struct
@ -1150,7 +1147,7 @@ BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore )
if (hModuleSnap == INVALID_HANDLE_VALUE) if (hModuleSnap == INVALID_HANDLE_VALUE)
{ {
DEBUGSTR( 1, L"Failed to create snapshot!" ); DEBUGSTR( 1, L"Failed to create snapshot (%lu)!", GetLastError() );
return FALSE; return FALSE;
} }
@ -1186,68 +1183,81 @@ BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore )
// ========== Child process injection // ========== 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. // Inject code into the target process to load our DLL.
void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
LPPROCESS_INFORMATION child_pi, LPPROCESS_INFORMATION child_pi,
BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) BOOL wide, LPCVOID lpApp, LPCVOID lpCmd )
{ {
int type; int type;
PBYTE base; PBYTE base;
BOOL gui; 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 ); type = ProcessType( child_pi, &base, &gui );
if (gui && type > 0) if (gui && type > 0)
{ {
TCHAR app[MAX_PATH]; if (name == NULL)
LPTSTR name; name = get_program( app, wide, lpApp, lpCmd );
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 (!search_env( L"ANSICON_GUI", name )) if (!search_env( L"ANSICON_GUI", name ))
{ {
DEBUGSTR( 1, L" %s", zIgnoring ); DEBUGSTR( 1, L" %s", zIgnoring );
@ -1257,31 +1267,34 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
if (type > 0) if (type > 0)
{ {
#ifdef _WIN64 #ifdef _WIN64
if (type == 32) if (type == 64)
{
ansi_bits[0] = '3';
ansi_bits[1] = '2';
InjectDLL32( child_pi, base );
}
else
{ {
ansi_bits[0] = '6'; ansi_bits[0] = '6';
ansi_bits[1] = '4'; ansi_bits[1] = '4';
InjectDLL( child_pi, base ); 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 #else
#ifdef W32ON64 #ifdef W32ON64
if (type == 64) if (type != 32)
{ {
TCHAR args[64]; TCHAR args[64];
STARTUPINFO si; STARTUPINFO si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
wcscpy( hDllNameType, L"CON.exe" ); wcscpy( DllNameType, L"CON.exe" );
wsprintf( args, L"ansicon -P%lu:%lu", wsprintf( args, L"ansicon -P%ld", child_pi->dwProcessId );
child_pi->dwProcessId, child_pi->dwThreadId );
ZeroMemory( &si, sizeof(si) ); ZeroMemory( &si, sizeof(si) );
si.cb = 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 )) &si, &pi ))
{ {
WaitForSingleObject( pi.hProcess, INFINITE ); WaitForSingleObject( pi.hProcess, INFINITE );
@ -1289,8 +1302,8 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
CloseHandle( pi.hThread ); CloseHandle( pi.hThread );
} }
else else
DEBUGSTR( 1, L"Could not execute \"%s\"", hDllName ); DEBUGSTR(1, L"Could not execute \"%s\" (%lu)", DllName, GetLastError());
wcscpy( hDllNameType, L"32.dll" ); wcscpy( DllNameType, L"32.dll" );
} }
else else
#endif #endif
@ -1339,6 +1352,13 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName,
{ {
PROCESS_INFORMATION child_pi; PROCESS_INFORMATION child_pi;
DEBUGSTR( 1, L"CreateProcessA: %s%S%s, %S",
(lpApplicationName == NULL) ? L"" : L"\"",
(lpApplicationName == NULL) ? "<null>" : lpApplicationName,
(lpApplicationName == NULL) ? L"" : L"\"",
(lpCommandLine == NULL) ? "<null>" :
(*lpCommandLine == '\0') ? "<empty>" : lpCommandLine );
if (!CreateProcessA( lpApplicationName, if (!CreateProcessA( lpApplicationName,
lpCommandLine, lpCommandLine,
lpThreadAttributes, lpThreadAttributes,
@ -1349,12 +1369,11 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName,
lpCurrentDirectory, lpCurrentDirectory,
lpStartupInfo, lpStartupInfo,
&child_pi )) &child_pi ))
{
DEBUGSTR( 1, L" Failed (%lu)", GetLastError() );
return FALSE; return FALSE;
}
DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", %S",
child_pi.dwProcessId,
(lpApplicationName == NULL) ? "" : lpApplicationName,
(lpCommandLine == NULL) ? "" : lpCommandLine );
Inject( dwCreationFlags, lpProcessInformation, &child_pi, Inject( dwCreationFlags, lpProcessInformation, &child_pi,
FALSE, lpApplicationName, lpCommandLine ); FALSE, lpApplicationName, lpCommandLine );
@ -1375,6 +1394,13 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName,
{ {
PROCESS_INFORMATION child_pi; PROCESS_INFORMATION child_pi;
DEBUGSTR( 1, L"CreateProcessW: %s%s%s, %s",
(lpApplicationName == NULL) ? L"" : L"\"",
(lpApplicationName == NULL) ? L"<null>" : lpApplicationName,
(lpApplicationName == NULL) ? L"" : L"\"",
(lpCommandLine == NULL) ? L"<null>" :
(*lpCommandLine == '\0') ? L"<empty>" : lpCommandLine );
if (!CreateProcessW( lpApplicationName, if (!CreateProcessW( lpApplicationName,
lpCommandLine, lpCommandLine,
lpThreadAttributes, lpThreadAttributes,
@ -1385,12 +1411,11 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName,
lpCurrentDirectory, lpCurrentDirectory,
lpStartupInfo, lpStartupInfo,
&child_pi )) &child_pi ))
{
DEBUGSTR( 1, L" Failed (%lu)", GetLastError() );
return FALSE; 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, Inject( dwCreationFlags, lpProcessInformation, &child_pi,
TRUE, lpApplicationName, lpCommandLine ); TRUE, lpApplicationName, lpCommandLine );
@ -1819,13 +1844,10 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
log_level = _wtoi( logstr ); log_level = _wtoi( logstr );
prog = get_program_name( NULL ); prog = get_program_name( NULL );
#if defined(_WIN64) || defined(W32ON64) #if defined(_WIN64) || defined(W32ON64)
hDllNameType = hDllName - 6 + DllNameType = DllName - 6 +
#endif #endif
GetModuleFileName( hInstance, hDllName, lenof(hDllName) ); GetModuleFileName( hInstance, DllName, lenof(DllName) );
#ifdef _WIN64 set_ansi_dll();
ansi_bits = (LPSTR)hDllNameType;
#endif
set_ansi_dll( hDllName );
hDllInstance = hInstance; // save Dll instance handle hDllInstance = hInstance; // save Dll instance handle
#ifdef _WIN64 #ifdef _WIN64

115
LLW.c
View File

@ -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

183
ansicon.c
View File

@ -77,16 +77,17 @@
v1.63, 25 July, 2013: v1.63, 25 July, 2013:
don't write the reset sequence if output is redirected. 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 restore the original (current, not default) attributes if using ansicon.exe
when it's already installed; when it's already installed;
use ANSICON_DEF if defined and -m not given; use ANSICON_DEF if defined and -m not given;
-e and -t will not output anything if the DLL could not load; -e and -t will not output anything if the DLL could not load;
use Unicode output (_O_U16TEXT, for compilers/systems that support it); 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 "ansicon.h"
#include "version.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 ) #define _putws( s ) my_fputws( s L"\n", stdout )
#if defined(_WIN64)
LPTSTR DllNameType;
#endif
// Find the name of the DLL and inject it. // Find the name of the DLL and inject it.
BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app ) BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
{ {
DWORD len; DWORD len;
WCHAR dll[MAX_PATH];
int type; int type;
PBYTE base; PBYTE base;
#ifdef _WIN64
if (app != NULL)
#endif
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId );
type = ProcessType( ppi, &base, gui ); type = ProcessType( ppi, &base, gui );
if (type <= 0) if (type <= 0)
@ -186,92 +193,109 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
} }
len = (DWORD)(prog - prog_path); len = (DWORD)(prog - prog_path);
memcpy( dll, prog_path, TSIZE(len) ); memcpy( DllName, prog_path, TSIZE(len) );
#ifdef _WIN64 #ifdef _WIN64
wsprintf( dll + len, L"ANSI%d.dll", type ); wsprintf( DllName + len, L"ANSI%d.dll", (type == 48) ? 64 : type );
ansi_bits = (LPSTR)(dll + len + 4); DllNameType = DllName + len + 4;
set_ansi_dll( dll ); set_ansi_dll();
if (type == 32) if (type == 64)
InjectDLL32( ppi, base );
else
InjectDLL( ppi, base ); InjectDLL( ppi, base );
else if (type == 32)
InjectDLL32( ppi, base );
else // (type == 48)
InjectDLL64( ppi );
#else #else
wcscpy( dll + len, L"ANSI32.dll" ); wcscpy( DllName + len, L"ANSI32.dll" );
set_ansi_dll( dll ); set_ansi_dll();
InjectDLL( ppi, base ); InjectDLL( ppi, base );
#endif #endif
return TRUE; return TRUE;
} }
// Use CreateRemoteThread to load our DLL in the target process. // Use CreateRemoteThread to load our DLL in the target process.
void RemoteLoad( LPPROCESS_INFORMATION ppi ) void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
{ {
HANDLE hSnap; HANDLE hSnap;
MODULEENTRY32 me; MODULEENTRY32 me;
PBYTE LLW; PBYTE LLW;
BOOL fOk; BOOL fOk;
WCHAR dll[MAX_PATH];
DWORD len; DWORD len;
LPVOID mem; LPVOID mem;
HANDLE thread; HANDLE thread;
DWORD ticks;
#ifdef _WIN64 #ifdef _WIN64
BOOL WOW64; BOOL WOW64;
int type;
#endif #endif
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId );
// Find the base address of kernel32.dll. // Find the base address of kernel32.dll.
LLW = NULL; ticks = GetTickCount();
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, while ((hSnap = CreateToolhelp32Snapshot(
ppi->dwProcessId ); TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ppi->dwProcessId ))
if (hSnap != INVALID_HANDLE_VALUE) == INVALID_HANDLE_VALUE)
{ {
me.dwSize = sizeof(MODULEENTRY32); DWORD err = GetLastError();
for (fOk = Module32First( hSnap, &me ); fOk;
fOk = Module32Next( hSnap, &me ))
{
if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0)
{
LLW = me.modBaseAddr;
break;
}
}
CloseHandle( hSnap );
}
#ifndef _WIN64 #ifndef _WIN64
else if (GetLastError() == ERROR_PARTIAL_COPY) if (err == ERROR_PARTIAL_COPY)
{ {
fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr ); DEBUGSTR( 1, L" Ignoring 64-bit process (use x64\\ansicon)" );
return; fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr );
} return;
}
#endif #endif
if (LLW == NULL) // I really don't think this would happen, but if it does, give up after
{ // two seconds to avoid a potentially infinite loop.
no_llw: 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 ); fputws( L"ANSICON: unable to inject into parent.\n", stderr );
return; 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); len = (DWORD)(prog - prog_path);
memcpy( dll, prog_path, TSIZE(len) ); memcpy( DllName, prog_path, TSIZE(len) );
#ifdef _WIN64 #ifdef _WIN64
if (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) type = (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) ? 32 : 64;
{ wsprintf( DllName + len, L"ANSI%d.dll", type );
wcscpy( dll + len, L"ANSI32.dll" ); LLW += GetProcRVA( L"kernel32.dll", "LoadLibraryW", type );
LLW += get_LLW32r();
}
else
{
wcscpy( dll + len, L"ANSI64.dll" );
LLW += get_LLW64r();
}
#else #else
wcscpy( dll + len, L"ANSI32.dll" ); wcscpy( DllName + len, L"ANSI32.dll" );
LLW += get_LLW32r(); LLW += GetProcRVA( L"kernel32.dll", "LoadLibraryW" );
#endif #endif
if (LLW == me.modBaseAddr) if (LLW == me.modBaseAddr)
goto no_llw; goto no_go;
mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE ); 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, thread = CreateRemoteThread( ppi->hProcess, NULL, 4096,
(LPTHREAD_START_ROUTINE)LLW, mem, 0, NULL ); (LPTHREAD_START_ROUTINE)LLW, mem, 0, NULL );
WaitForSingleObject( thread, INFINITE ); WaitForSingleObject( thread, INFINITE );
@ -341,12 +365,18 @@ int main( void )
#ifdef _WIN64 #ifdef _WIN64
if (*arg == '-' && arg[1] == 'P') 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.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId );
pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); if (pi.hProcess == NULL)
Inject( &pi, &gui, arg ); {
CloseHandle( pi.hThread ); DEBUGSTR( 1, L" Unable to open process %lu (%lu)",
CloseHandle( pi.hProcess ); pi.dwProcessId, GetLastError() );
}
else
{
Inject( &pi, &gui, NULL );
CloseHandle( pi.hProcess );
}
return 0; return 0;
} }
#endif #endif
@ -378,14 +408,10 @@ int main( void )
case 'p': case 'p':
shell = FALSE; shell = FALSE;
if (GetParentProcessInfo( &pi, arg + 3 )) if (GetParentProcessInfo( &pi, arg ))
{ {
pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); RemoteLoad( &pi, arg );
SuspendThread( pi.hThread );
RemoteLoad( &pi );
ResumeThread( pi.hThread );
CloseHandle( pi.hThread );
CloseHandle( pi.hProcess ); CloseHandle( pi.hProcess );
} }
else 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 ) BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
{ {
HANDLE hSnap; HANDLE hSnap;
PROCESSENTRY32 pe; PROCESSENTRY32 pe;
THREADENTRY32 te;
BOOL fOk; BOOL fOk;
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, 0 ); hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if (hSnap == INVALID_HANDLE_VALUE) 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; return FALSE;
} }
te.dwSize = sizeof(te); fOk = (find_proc_id( hSnap, GetCurrentProcessId(), &pe ) &&
for (fOk = Thread32First( hSnap, &te ); fOk; fOk = Thread32Next( hSnap, &te )) find_proc_id( hSnap, pe.th32ParentProcessID, &pe ));
if (te.th32OwnerProcessID == pe.th32ProcessID)
break;
CloseHandle( hSnap ); CloseHandle( hSnap );
if (!fOk)
{
DEBUGSTR( 1, L"Failed to locate parent" );
return FALSE;
}
ppi->dwProcessId = pe.th32ProcessID; ppi->dwProcessId = pe.th32ProcessID;
ppi->dwThreadId = te.th32ThreadID;
wcscpy( name, pe.szExeFile ); wcscpy( name, pe.szExeFile );
return fOk; return fOk;
@ -897,7 +918,7 @@ L" [-e|E string | -t|T [file(s)] | program [args]]\n"
L"\n" L"\n"
L" -l\t\tset the logging level (1=process, 2=module, 3=function,\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" \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" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n"
L" -I -U\t\tuse local machine instead of current user\n" L" -I -U\t\tuse local machine instead of current user\n"
L" -m\t\tuse grey on black (\"monochrome\") or <attr> as default color\n" L" -m\t\tuse grey on black (\"monochrome\") or <attr> as default color\n"

View File

@ -15,15 +15,12 @@
#ifdef _WIN64 #ifdef _WIN64
#define _WIN32_WINNT 0x0600 // MinGW-w64 wants this defined for Wow64 stuff #define _WIN32_WINNT 0x0600 // MinGW-w64 wants this defined for Wow64 stuff
#else #else
#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread #define _WIN32_WINNT 0x0500
#endif #endif
#include <windows.h> #include <windows.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#define lenof(array) (sizeof(array)/sizeof(*(array)))
#define TSIZE(size) ((size) * sizeof(TCHAR))
#ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20 #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
#endif #endif
@ -31,13 +28,19 @@
#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x20 #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x20
#endif #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 EXPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
#define IMPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] #define IMPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
#define BOUNDDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] #define BOUNDDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
#define IATDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] #define IATDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]
#define COMDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] #define COMDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
// Reduce the verbosity of some functions (assuming variable names). // Reduce the verbosity of some functions (assuming variable names).
#define ReadProcVar(a, b) ReadProcMem( a, b, sizeof(*(b)) ) #define ReadProcVar(a, b) ReadProcMem( a, b, sizeof(*(b)) )
#define WriteProcVar(a, b) WriteProcMem( 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 WriteProcMem(a, b, c) WriteProcessMemory( ppi->hProcess, a, b, c, NULL )
#define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr ) #define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr )
#define PTRSZ sizeof(PVOID)
typedef struct typedef struct
{ {
@ -60,24 +61,31 @@ typedef struct
} GRM, *PGRM; // Graphic Rendition Mode } 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 InjectDLL( LPPROCESS_INFORMATION, PBYTE );
void InjectDLL32( 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 TCHAR prog_path[MAX_PATH];
extern LPTSTR prog; extern LPTSTR prog;
LPTSTR get_program_name( LPTSTR ); LPTSTR get_program_name( LPTSTR );
extern char ansi_dll[MAX_PATH]; extern TCHAR DllName[MAX_PATH];
extern DWORD ansi_len; extern LPTSTR DllNameType;
extern char* ansi_bits; extern char ansi_dll[MAX_PATH];
void set_ansi_dll( LPTSTR ); extern DWORD ansi_len;
extern char* ansi_bits;
void set_ansi_dll( void );
extern int log_level; extern int log_level;
void DEBUGSTR( int level, LPTSTR szFormat, ... ); void DEBUGSTR( int level, LPTSTR szFormat, ... );
#endif #endif

152
injdll.c
View File

@ -1,14 +1,17 @@
/* /*
Inject the DLL into the target process by modifying its import descriptor 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" #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.) // 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; MEMORY_BASIC_INFORMATION minfo;
PBYTE ptr; PBYTE ptr;
@ -43,20 +46,20 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
DWORD pr; DWORD pr;
IMAGE_DOS_HEADER DosHeader; IMAGE_DOS_HEADER DosHeader;
IMAGE_NT_HEADERS NTHeader, *pNTHeader; IMAGE_NT_HEADERS NTHeader, *pNTHeader;
PIMAGE_IMPORT_DESCRIPTOR pImports, pANSI_ImportDesc; PIMAGE_IMPORT_DESCRIPTOR pImports;
IMAGE_COR20_HEADER ComHeader, *pComHeader; IMAGE_COR20_HEADER ComHeader, *pComHeader;
union union
{ {
PBYTE pB; PBYTE pB;
PLONG_PTR pL; PLONG_PTR pL;
PIMAGE_IMPORT_DESCRIPTOR pI;
} ip; } ip;
ReadProcVar( pBase, &DosHeader ); ReadProcVar( pBase, &DosHeader );
pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew); pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew);
ReadProcVar( pNTHeader, &NTHeader ); ReadProcVar( pNTHeader, &NTHeader );
len = 4 * PTRSZ + ansi_len len = 4 * PTRSZ + ansi_len + sizeof(*pImports) + NTHeader.IMPORTDIR.Size;
+ sizeof(*pANSI_ImportDesc) + NTHeader.IMPORTDIR.Size;
pImports = malloc( len ); pImports = malloc( len );
if (pImports == NULL) if (pImports == NULL)
{ {
@ -79,24 +82,23 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
*ip.pL++ = 0; *ip.pL++ = 0;
memcpy( ip.pB, ansi_dll, ansi_len ); memcpy( ip.pB, ansi_dll, ansi_len );
ip.pB += ansi_len; ip.pB += ansi_len;
pANSI_ImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ip.pB; ip.pI->OriginalFirstThunk = rva + 2 * PTRSZ;
pANSI_ImportDesc->OriginalFirstThunk = rva + 2 * PTRSZ; ip.pI->TimeDateStamp = 0;
pANSI_ImportDesc->TimeDateStamp = 0; ip.pI->ForwarderChain = 0;
pANSI_ImportDesc->ForwarderChain = 0; ip.pI->Name = rva + 4 * PTRSZ;
pANSI_ImportDesc->Name = rva + 4 * PTRSZ; ip.pI->FirstThunk = rva;
pANSI_ImportDesc->FirstThunk = rva;
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress, ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress,
pANSI_ImportDesc + 1, NTHeader.IMPORTDIR.Size ); ip.pI + 1, NTHeader.IMPORTDIR.Size );
WriteProcMem( pMem, pImports, len ); WriteProcMem( pMem, pImports, len );
free( pImports ); free( pImports );
NTHeader.IMPORTDIR.VirtualAddress = rva + 4 * PTRSZ + ansi_len; // If there's no IAT, copy the original IDT (to allow writable ".idata").
NTHeader.IMPORTDIR.Size += sizeof(*pANSI_ImportDesc);
// If there's no IAT, copy the IDT.
if (NTHeader.IATDIR.VirtualAddress == 0) if (NTHeader.IATDIR.VirtualAddress == 0)
NTHeader.IATDIR = NTHeader.IMPORTDIR; 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. // Remove bound imports, so the updated import table is used.
NTHeader.BOUNDDIR.VirtualAddress = 0; NTHeader.BOUNDDIR.VirtualAddress = 0;
NTHeader.BOUNDDIR.Size = 0; NTHeader.BOUNDDIR.Size = 0;
@ -130,19 +132,20 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
DWORD pr; DWORD pr;
IMAGE_DOS_HEADER DosHeader; IMAGE_DOS_HEADER DosHeader;
IMAGE_NT_HEADERS32 NTHeader, *pNTHeader; IMAGE_NT_HEADERS32 NTHeader, *pNTHeader;
PIMAGE_IMPORT_DESCRIPTOR pImports, pANSI_ImportDesc; PIMAGE_IMPORT_DESCRIPTOR pImports;
IMAGE_COR20_HEADER ComHeader, *pComHeader; IMAGE_COR20_HEADER ComHeader, *pComHeader;
union union
{ {
PBYTE pB; PBYTE pB;
PLONG pL; PLONG pL;
PIMAGE_IMPORT_DESCRIPTOR pI;
} ip; } ip;
ReadProcVar( pBase, &DosHeader ); ReadProcVar( pBase, &DosHeader );
pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew); pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew);
ReadProcVar( pNTHeader, &NTHeader ); 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 ); pImports = malloc( len );
if (pImports == NULL) if (pImports == NULL)
{ {
@ -165,21 +168,20 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
*ip.pL++ = 0; *ip.pL++ = 0;
memcpy( ip.pB, ansi_dll, ansi_len ); memcpy( ip.pB, ansi_dll, ansi_len );
ip.pB += ansi_len; ip.pB += ansi_len;
pANSI_ImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ip.pB; ip.pI->OriginalFirstThunk = rva + 8;
pANSI_ImportDesc->OriginalFirstThunk = rva + 8; ip.pI->TimeDateStamp = 0;
pANSI_ImportDesc->TimeDateStamp = 0; ip.pI->ForwarderChain = 0;
pANSI_ImportDesc->ForwarderChain = 0; ip.pI->Name = rva + 16;
pANSI_ImportDesc->Name = rva + 16; ip.pI->FirstThunk = rva;
pANSI_ImportDesc->FirstThunk = rva;
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress, ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress,
pANSI_ImportDesc + 1, NTHeader.IMPORTDIR.Size ); ip.pI + 1, NTHeader.IMPORTDIR.Size );
WriteProcMem( pMem, pImports, len ); WriteProcMem( pMem, pImports, len );
free( pImports ); free( pImports );
NTHeader.IMPORTDIR.VirtualAddress = rva + 16 + ansi_len;
NTHeader.IMPORTDIR.Size += sizeof(*pANSI_ImportDesc);
if (NTHeader.IATDIR.VirtualAddress == 0) if (NTHeader.IATDIR.VirtualAddress == 0)
NTHeader.IATDIR = NTHeader.IMPORTDIR; NTHeader.IATDIR = NTHeader.IMPORTDIR;
NTHeader.IMPORTDIR.VirtualAddress = rva + 16 + ansi_len;
NTHeader.IMPORTDIR.Size += sizeof(*pImports);
NTHeader.BOUNDDIR.VirtualAddress = 0; NTHeader.BOUNDDIR.VirtualAddress = 0;
NTHeader.BOUNDDIR.Size = 0; NTHeader.BOUNDDIR.Size = 0;
VirtProtVar( pNTHeader, PAGE_READWRITE ); 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 #endif

View File

@ -44,8 +44,8 @@ endif
endif endif
endif endif
X86OBJS = x86/proctype.o x86/injdll.o x86/util.o X86OBJS = x86/injdll.o x86/proctype.o x86/util.o
X64OBJS = x64/proctype.o x64/injdll.o x64/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 # Determine the appropriate separator to run multiple commands - ";" for sh.exe
# and "&" for CMD.EXE. $(SHELL) is initially defined to "sh.exe" - if it # 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: x86:
cmd /c "mkdir 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) $(LDmsg)$(CC) -m32 $+ -s -o $@ $(IVER)
x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o 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: x64:
cmd /c "mkdir 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) $(LDmsg)$(CC) -m64 $+ -s -o $@ $(IVER)
x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o

View File

@ -65,8 +65,8 @@ LIBS = advapi32.lib user32.lib $(LIBS64)
# Identify ansicon.exe using "ANSI" as a version number. # Identify ansicon.exe using "ANSI" as a version number.
LINK = /link /version:20033.18771 LINK = /link /version:20033.18771
X86OBJS = x86\proctype.obj x86\injdll.obj x86\util.obj X86OBJS = x86\injdll.obj x86\proctype.obj x86\util.obj
X64OBJS = x64\proctype.obj x64\injdll.obj x64\util.obj X64OBJS = x64\injdll.obj x64\proctype.obj x64\util.obj x64\procrva.obj
!IF !DEFINED(V) !IF !DEFINED(V)
V = 0 V = 0
@ -93,7 +93,7 @@ ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll
x86: x86:
mkdir 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 $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512
!IF "$(_NMAKE_VER)" == "9.00.30729.01" !IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1 $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1
@ -111,7 +111,7 @@ x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res
x64: x64:
mkdir 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) $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK)
x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res
@ -133,7 +133,7 @@ ANSI.rc: version.h
util.c: ansicon.h version.h util.c: ansicon.h version.h
injdll.c: ansicon.h injdll.c: ansicon.h
proctype.c: ansicon.h proctype.c: ansicon.h
LLW.c: ansicon.h procrva.c: ansicon.h
x64\ANSI32.obj: ANSI.c x64\ANSI32.obj: ANSI.c
$(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?

85
procrva.c Normal file
View File

@ -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;
}

View File

@ -2,12 +2,44 @@
Test for a valid process (i386 for x86; that or AMD64 for x64). We can get 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 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 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" #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 ) int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
{ {
PBYTE ptr; PBYTE ptr;
@ -43,6 +75,34 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
{ {
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) 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)", DEBUGSTR( 1, L" 32-bit %s (base = %.8X)",
(*gui) ? L"GUI" : L"console", (*gui) ? L"GUI" : L"console",
PtrToUint( minfo.BaseAddress ) ); PtrToUint( minfo.BaseAddress ) );

26
util.c
View File

@ -11,6 +11,7 @@ LPTSTR prog;
int log_level; int log_level;
TCHAR DllName[MAX_PATH]; // Dll file name
char ansi_dll[MAX_PATH]; char ansi_dll[MAX_PATH];
DWORD ansi_len; DWORD ansi_len;
#ifdef _WIN64 #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, // 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 // just use the name and hope it's on the PATH.
// path/name, including padding to make it dword-aligned. The 64-bit version void set_ansi_dll( void )
// expects ansi_bits to point to the size within dll on entry.
void set_ansi_dll( LPTSTR dll )
{ {
BOOL bad; 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 ); NULL, 0, NULL, &bad );
if (bad || ansi_len > MAX_PATH) if (bad || ansi_len > MAX_PATH)
{ {
ansi_len = 12;
memcpy( ansi_dll, "ANSI32.dll\0", 12 );
#ifdef _WIN64 #ifdef _WIN64
if (*ansi_bits == '6')
{
ansi_dll[4] = '6';
ansi_dll[5] = '4';
}
ansi_bits = ansi_dll + 4; ansi_bits = ansi_dll + 4;
if (*DllNameType == '6')
memcpy( ansi_dll, "ANSI64.dll\0", 12 );
else
#endif #endif
memcpy( ansi_dll, "ANSI32.dll\0", 12 );
ansi_len = 12;
} }
else 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 ); ansi_dll, MAX_PATH, NULL, NULL );
#ifdef _WIN64 #ifdef _WIN64
ansi_bits = ansi_dll + ansi_len - 7; ansi_bits = ansi_dll + ansi_len - 7;
@ -104,6 +101,9 @@ void DEBUGSTR( int level, LPTSTR szFormat, ... )
{ {
SYSTEMTIME now; SYSTEMTIME now;
GetLocalTime( &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 " fprintf( file, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d) started "
"%d-%.2d-%.2d %d:%.2d:%.2d\n", "%d-%.2d-%.2d %d:%.2d:%.2d\n",
log_level, log_level,