Work with 64-bit AnyCPU; copy original IDT to IAT; log improvements.
This commit is contained in:
parent
dc7569dc26
commit
db36552c42
204
ANSI.c
204
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) ? "<null>" : lpApplicationName,
|
||||
(lpApplicationName == NULL) ? L"" : L"\"",
|
||||
(lpCommandLine == NULL) ? "<null>" :
|
||||
(*lpCommandLine == '\0') ? "<empty>" : 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"<null>" : lpApplicationName,
|
||||
(lpApplicationName == NULL) ? L"" : L"\"",
|
||||
(lpCommandLine == NULL) ? L"<null>" :
|
||||
(*lpCommandLine == '\0') ? L"<empty>" : 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
|
||||
|
115
LLW.c
115
LLW.c
@ -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
183
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 <attr> as default color\n"
|
||||
|
42
ansicon.h
42
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 <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
|
152
injdll.c
152
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
|
||||
|
@ -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
|
||||
|
10
makefile.vc
10
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$@ $?
|
||||
|
85
procrva.c
Normal file
85
procrva.c
Normal 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;
|
||||
}
|
62
proctype.c
62
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 ) );
|
||||
|
26
util.c
26
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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user