Inject by adding to the Import Directory Table.
-p uses CreateRemoteThread, determining kernel32.dll & LLW dynamically. Loading via LoadLibrary will remember the current attributes, restoring them on unload. Tweaked log output (remove quotes around CreateProcess command line; add an underscore to 64-bit addresses). ansicon.exe will really output (to the console) strings as Unicode. Fixed ansicon.exe, if installed, restoring the default attributes, not current. ansicon.exe will start with ANSICON_DEF (if defined and -m not used).
This commit is contained in:
parent
bccf933c0a
commit
dc7569dc26
105
ANSI.c
105
ANSI.c
@ -108,13 +108,15 @@
|
||||
v1.65, 28 August, 2013:
|
||||
fix \e[K (was using window, not buffer).
|
||||
|
||||
v.166, 20 & 21 September, 2013:
|
||||
v1.66, 20 & 21 September, 2013:
|
||||
fix 32-bit process trying to detect 64-bit process.
|
||||
|
||||
v1.67, 25 to 27 January, 2014:
|
||||
v1.70, 25 January to 4 February, 2014:
|
||||
don't hook ourself from LoadLibrary or LoadLibraryEx;
|
||||
update the LoadLibraryEx flags that should not cause hooking;
|
||||
always find the base address of kernel32.dll.
|
||||
inject by manipulating the import directory table;
|
||||
restore original attribute on detach (for LoadLibrary/FreeLibrary usage);
|
||||
log: remove the quotes around the CreateProcess command line string.
|
||||
*/
|
||||
|
||||
#include "ansicon.h"
|
||||
@ -135,6 +137,7 @@
|
||||
// ========== Global variables and constants
|
||||
|
||||
HANDLE hConOut; // handle to CONOUT$
|
||||
WORD orgattr; // original attribute
|
||||
|
||||
#define ESC '\x1B' // ESCape character
|
||||
#define BEL '\x07'
|
||||
@ -253,11 +256,6 @@ SHARED DWORD s_flag;
|
||||
#define GRM_INIT 1
|
||||
#define GRM_EXIT 2
|
||||
|
||||
SHARED DWORD LLW32r;
|
||||
#ifdef _WIN64
|
||||
SHARED DWORD LLW64r;
|
||||
#endif
|
||||
|
||||
|
||||
// Wait for the child process to finish, then update our GRM to the child's.
|
||||
DWORD WINAPI UpdateGRM( LPVOID child_pi )
|
||||
@ -470,6 +468,7 @@ void InterpretEscSeq( void )
|
||||
case 7: grm.rvideo = 1; break;
|
||||
case 8: grm.concealed = 1; break;
|
||||
case 21: // oops, this actually turns on double underline
|
||||
// but xterm turns off bold too, so that's alright
|
||||
case 22: grm.bold = 0; break;
|
||||
case 25:
|
||||
case 24: grm.underline = 0; break;
|
||||
@ -1049,9 +1048,7 @@ BOOL HookAPIOneMod(
|
||||
// We now have a valid pointer to the module's PE header.
|
||||
// Get a pointer to its imports section.
|
||||
pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR,
|
||||
pNTHeader->OptionalHeader.
|
||||
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
|
||||
VirtualAddress );
|
||||
pNTHeader->IMPORTDIR.VirtualAddress );
|
||||
|
||||
// Bail out if the RVA of the imports section is 0 (it doesn't exist)
|
||||
if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader)
|
||||
@ -1113,35 +1110,18 @@ BOOL HookAPIOneMod(
|
||||
}
|
||||
if (patch)
|
||||
{
|
||||
DWORD flOldProtect, flNewProtect, flDummy;
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
DWORD pr;
|
||||
|
||||
DEBUGSTR( 3, L" %S", hook->name );
|
||||
// Get the current protection attributes.
|
||||
VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) );
|
||||
// Take the access protection flags.
|
||||
flNewProtect = mbi.Protect;
|
||||
// Remove ReadOnly and ExecuteRead flags.
|
||||
flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ);
|
||||
// Add on ReadWrite flag
|
||||
flNewProtect |= (PAGE_READWRITE);
|
||||
// Change the access protection on the region of committed pages in the
|
||||
// virtual address space of the current process.
|
||||
VirtualProtect( &pThunk->u1.Function, sizeof(PVOID),
|
||||
flNewProtect, &flOldProtect );
|
||||
// Change the access protection on the region of committed pages in
|
||||
// the virtual address space of the current process.
|
||||
VirtualProtect( &pThunk->u1.Function, PTRSZ, PAGE_READWRITE, &pr );
|
||||
|
||||
// Overwrite the original address with the address of the new function.
|
||||
if (!WriteProcessMemory( GetCurrentProcess(),
|
||||
&pThunk->u1.Function,
|
||||
&patch, sizeof(patch), NULL ))
|
||||
{
|
||||
DEBUGSTR( 1, L"Could not patch!" );
|
||||
return FALSE;
|
||||
}
|
||||
pThunk->u1.Function = (DWORD_PTR)patch;
|
||||
|
||||
// Put the page attributes back the way they were.
|
||||
VirtualProtect( &pThunk->u1.Function, sizeof(PVOID),
|
||||
flOldProtect, &flDummy );
|
||||
VirtualProtect( &pThunk->u1.Function, PTRSZ, pr, &pr );
|
||||
}
|
||||
}
|
||||
pThunk++; // Advance to next imported function address
|
||||
@ -1211,11 +1191,12 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
|
||||
LPPROCESS_INFORMATION child_pi,
|
||||
BOOL wide, LPCVOID lpApp, LPCVOID lpCmd )
|
||||
{
|
||||
int type;
|
||||
BOOL gui;
|
||||
int type;
|
||||
PBYTE base;
|
||||
BOOL gui;
|
||||
|
||||
type = ProcessType( child_pi, &gui );
|
||||
if (gui)
|
||||
type = ProcessType( child_pi, &base, &gui );
|
||||
if (gui && type > 0)
|
||||
{
|
||||
TCHAR app[MAX_PATH];
|
||||
LPTSTR name;
|
||||
@ -1273,20 +1254,20 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
|
||||
type = 0;
|
||||
}
|
||||
}
|
||||
if (type != 0)
|
||||
if (type > 0)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
if (type == 32)
|
||||
{
|
||||
hDllNameType[0] = '3';
|
||||
hDllNameType[1] = '2';
|
||||
InjectDLL32( child_pi, hDllName );
|
||||
ansi_bits[0] = '3';
|
||||
ansi_bits[1] = '2';
|
||||
InjectDLL32( child_pi, base );
|
||||
}
|
||||
else
|
||||
{
|
||||
hDllNameType[0] = '6';
|
||||
hDllNameType[1] = '4';
|
||||
InjectDLL64( child_pi, hDllName );
|
||||
ansi_bits[0] = '6';
|
||||
ansi_bits[1] = '4';
|
||||
InjectDLL( child_pi, base );
|
||||
}
|
||||
#else
|
||||
#ifdef W32ON64
|
||||
@ -1313,7 +1294,7 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
|
||||
}
|
||||
else
|
||||
#endif
|
||||
InjectDLL32( child_pi, hDllName );
|
||||
InjectDLL( child_pi, base );
|
||||
#endif
|
||||
if (!gui && !(dwCreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)))
|
||||
{
|
||||
@ -1370,7 +1351,7 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName,
|
||||
&child_pi ))
|
||||
return FALSE;
|
||||
|
||||
DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", \"%S\"",
|
||||
DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", %S",
|
||||
child_pi.dwProcessId,
|
||||
(lpApplicationName == NULL) ? "" : lpApplicationName,
|
||||
(lpCommandLine == NULL) ? "" : lpCommandLine );
|
||||
@ -1406,7 +1387,7 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName,
|
||||
&child_pi ))
|
||||
return FALSE;
|
||||
|
||||
DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", \"%s\"",
|
||||
DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", %s",
|
||||
child_pi.dwProcessId,
|
||||
(lpApplicationName == NULL) ? L"" : lpApplicationName,
|
||||
(lpCommandLine == NULL) ? L"" : lpCommandLine );
|
||||
@ -1609,7 +1590,6 @@ WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer,
|
||||
|
||||
return WriteConsoleA( hCon, lpBuffer, nNumberOfCharsToWrite,
|
||||
lpNumberOfCharsWritten, lpReserved );
|
||||
|
||||
}
|
||||
|
||||
BOOL
|
||||
@ -1774,6 +1754,7 @@ void OriginalAttr( void )
|
||||
NULL, OPEN_EXISTING, 0, 0 );
|
||||
if (!GetConsoleScreenBufferInfo( hConOut, &csbi ))
|
||||
csbi.wAttributes = 7;
|
||||
orgattr = csbi.wAttributes;
|
||||
CloseHandle( hConOut );
|
||||
|
||||
if (s_flag == GRM_INIT && s_pid == GetCurrentProcessId())
|
||||
@ -1823,7 +1804,8 @@ void OriginalAttr( void )
|
||||
// and terminated.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) // just to stop MinGW exporting everything
|
||||
// Need to export something for static loading to work, this is as good as any.
|
||||
__declspec(dllexport)
|
||||
BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
|
||||
{
|
||||
BOOL bResult = TRUE;
|
||||
@ -1840,19 +1822,19 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
|
||||
hDllNameType = hDllName - 6 +
|
||||
#endif
|
||||
GetModuleFileName( hInstance, hDllName, lenof(hDllName) );
|
||||
#ifdef _WIN64
|
||||
ansi_bits = (LPSTR)hDllNameType;
|
||||
#endif
|
||||
set_ansi_dll( hDllName );
|
||||
|
||||
hDllInstance = hInstance; // save Dll instance handle
|
||||
DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance );
|
||||
|
||||
if (LLW32r == 0)
|
||||
{
|
||||
if (!get_LLW32r())
|
||||
return FALSE;
|
||||
#ifdef _WIN64
|
||||
if (!get_LLW64r())
|
||||
return FALSE;
|
||||
DEBUGSTR( 1, L"hDllInstance = %.8X_%.8X",
|
||||
(DWORD)((DWORD_PTR)hDllInstance >> 32),
|
||||
PtrToUint( hDllInstance ) );
|
||||
#else
|
||||
DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get the entry points to the original functions.
|
||||
hKernel = GetModuleHandleA( APIKernel );
|
||||
@ -1869,6 +1851,11 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
|
||||
{
|
||||
DEBUGSTR( 1, L"Unloading" );
|
||||
HookAPIAllMod( Hooks, TRUE );
|
||||
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, 0 );
|
||||
SetConsoleTextAttribute( hConOut, orgattr );
|
||||
CloseHandle( hConOut );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (C) 2005-2013 Jason Hood
|
||||
Copyright (C) 2005-2014 Jason Hood
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
|
115
LLW.c
Normal file
115
LLW.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
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
|
314
ansicon.c
314
ansicon.c
@ -76,17 +76,30 @@
|
||||
|
||||
v1.63, 25 July, 2013:
|
||||
don't write the reset sequence if output is redirected.
|
||||
|
||||
v1.70, 31 January to 3 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.
|
||||
*/
|
||||
|
||||
#define PDATE L"27 January, 2014"
|
||||
#define PDATE L"4 February, 2014"
|
||||
|
||||
#include "ansicon.h"
|
||||
#include "version.h"
|
||||
#include <tlhelp32.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <locale.h>
|
||||
|
||||
#ifndef _O_U16TEXT
|
||||
#define _O_U16TEXT 0x20000
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
int _CRT_glob = 0;
|
||||
#endif
|
||||
@ -99,7 +112,7 @@ int _CRT_glob = 0;
|
||||
void help( void );
|
||||
|
||||
void display( LPCTSTR, BOOL );
|
||||
void print_error( LPCTSTR, ... );
|
||||
void print_error( LPCTSTR );
|
||||
LPTSTR skip_spaces( LPTSTR );
|
||||
void get_arg( LPTSTR, LPTSTR*, LPTSTR* );
|
||||
void get_file( LPTSTR, LPTSTR*, LPTSTR* );
|
||||
@ -110,12 +123,49 @@ BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe );
|
||||
BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR );
|
||||
|
||||
|
||||
// The DLL shares this variable, so injection requires it here.
|
||||
DWORD LLW32r;
|
||||
#ifdef _WIN64
|
||||
DWORD LLW64r;
|
||||
#endif
|
||||
extern LPVOID kernel32_base;
|
||||
static HANDLE hConOut;
|
||||
static WORD wAttr;
|
||||
|
||||
void get_original_attr( void )
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, 0 );
|
||||
GetConsoleScreenBufferInfo( hConOut, &csbi );
|
||||
wAttr = csbi.wAttributes;
|
||||
}
|
||||
|
||||
|
||||
void set_original_attr( void )
|
||||
{
|
||||
SetConsoleTextAttribute( hConOut, wAttr );
|
||||
CloseHandle( hConOut );
|
||||
}
|
||||
|
||||
|
||||
// The fputws function in MSVCRT.DLL (Windows 7 x64) is broken for Unicode
|
||||
// output (it just writes the first character). VC6 & 7 don't support Unicode
|
||||
// output at all (just converting to ANSI) and even when it is supported, it
|
||||
// just writes single characters (as does _putws & fwprintf). So what the
|
||||
// heck, DIY.
|
||||
int my_fputws( const wchar_t* s, FILE* f )
|
||||
{
|
||||
if (_isatty( _fileno( f ) ))
|
||||
{
|
||||
DWORD written;
|
||||
WriteConsole( hConOut, s, (DWORD)wcslen( s ), &written, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
fputws( s, f );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define fputws my_fputws
|
||||
#define _putws( s ) my_fputws( s L"\n", stdout )
|
||||
|
||||
|
||||
// Find the name of the DLL and inject it.
|
||||
@ -124,12 +174,14 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
|
||||
DWORD len;
|
||||
WCHAR dll[MAX_PATH];
|
||||
int type;
|
||||
PBYTE base;
|
||||
|
||||
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId );
|
||||
type = ProcessType( ppi, gui );
|
||||
if (type == 0)
|
||||
type = ProcessType( ppi, &base, gui );
|
||||
if (type <= 0)
|
||||
{
|
||||
fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app );
|
||||
if (type == 0)
|
||||
fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -137,41 +189,94 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
|
||||
memcpy( dll, 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)
|
||||
{
|
||||
get_LLW32r();
|
||||
InjectDLL32( ppi, dll );
|
||||
}
|
||||
InjectDLL32( ppi, base );
|
||||
else
|
||||
{
|
||||
get_LLW64r();
|
||||
InjectDLL64( ppi, dll );
|
||||
}
|
||||
InjectDLL( ppi, base );
|
||||
#else
|
||||
wcscpy( dll + len, L"ANSI32.dll" );
|
||||
get_LLW32r();
|
||||
InjectDLL32( ppi, dll );
|
||||
set_ansi_dll( dll );
|
||||
InjectDLL( ppi, base );
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static HANDLE hConOut;
|
||||
static CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
void get_original_attr( void )
|
||||
// Use CreateRemoteThread to load our DLL in the target process.
|
||||
void RemoteLoad( LPPROCESS_INFORMATION ppi )
|
||||
{
|
||||
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, 0 );
|
||||
GetConsoleScreenBufferInfo( hConOut, &csbi );
|
||||
}
|
||||
HANDLE hSnap;
|
||||
MODULEENTRY32 me;
|
||||
PBYTE LLW;
|
||||
BOOL fOk;
|
||||
WCHAR dll[MAX_PATH];
|
||||
DWORD len;
|
||||
LPVOID mem;
|
||||
HANDLE thread;
|
||||
#ifdef _WIN64
|
||||
BOOL WOW64;
|
||||
#endif
|
||||
|
||||
// Find the base address of kernel32.dll.
|
||||
LLW = NULL;
|
||||
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
|
||||
ppi->dwProcessId );
|
||||
if (hSnap != 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 );
|
||||
}
|
||||
#ifndef _WIN64
|
||||
else if (GetLastError() == ERROR_PARTIAL_COPY)
|
||||
{
|
||||
fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (LLW == NULL)
|
||||
{
|
||||
no_llw:
|
||||
fputws( L"ANSICON: unable to inject into parent.\n", stderr );
|
||||
return;
|
||||
}
|
||||
|
||||
void set_original_attr( void )
|
||||
{
|
||||
SetConsoleTextAttribute( hConOut, csbi.wAttributes );
|
||||
CloseHandle( hConOut );
|
||||
len = (DWORD)(prog - prog_path);
|
||||
memcpy( dll, 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();
|
||||
}
|
||||
#else
|
||||
wcscpy( dll + len, L"ANSI32.dll" );
|
||||
LLW += get_LLW32r();
|
||||
#endif
|
||||
if (LLW == me.modBaseAddr)
|
||||
goto no_llw;
|
||||
mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE );
|
||||
WriteProcMem( mem, dll, TSIZE(len + 11) );
|
||||
thread = CreateRemoteThread( ppi->hProcess, NULL, 4096,
|
||||
(LPTHREAD_START_ROUTINE)LLW, mem, 0, NULL );
|
||||
WaitForSingleObject( thread, INFINITE );
|
||||
CloseHandle( thread );
|
||||
VirtualFreeEx( ppi->hProcess, mem, 0, MEM_RELEASE );
|
||||
}
|
||||
|
||||
|
||||
@ -186,13 +291,25 @@ int main( void )
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
LPTSTR argv, arg, cmd;
|
||||
TCHAR logstr[4];
|
||||
BOOL installed;
|
||||
TCHAR buf[4];
|
||||
BOOL shell, run, gui;
|
||||
HMODULE ansi;
|
||||
DWORD len;
|
||||
int rc = 0;
|
||||
|
||||
// Convert wide strings using the current code page.
|
||||
sprintf( (LPSTR)buf, ".%u", GetConsoleOutputCP() );
|
||||
setlocale( LC_CTYPE, (LPSTR)buf );
|
||||
|
||||
// Switch console output to Unicode.
|
||||
if (_isatty( 1 ))
|
||||
_setmode( 1, _O_U16TEXT);
|
||||
if (_isatty( 2 ))
|
||||
_setmode( 2, _O_U16TEXT);
|
||||
|
||||
// Create a console handle and store the current attribute.
|
||||
get_original_attr();
|
||||
|
||||
argv = GetCommandLine();
|
||||
len = (DWORD)wcslen( argv ) + 1;
|
||||
if (len < MAX_PATH)
|
||||
@ -217,20 +334,16 @@ int main( void )
|
||||
}
|
||||
|
||||
prog = get_program_name( NULL );
|
||||
*logstr = '\0';
|
||||
GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) );
|
||||
log_level = _wtoi( logstr );
|
||||
|
||||
// Using "" for setlocale uses the system ANSI code page.
|
||||
sprintf( (LPSTR)logstr, ".%u", GetConsoleOutputCP() );
|
||||
setlocale( LC_CTYPE, (LPSTR)logstr );
|
||||
*buf = '\0';
|
||||
GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) );
|
||||
log_level = _wtoi( buf );
|
||||
|
||||
#ifdef _WIN64
|
||||
if (*arg == '-' && arg[1] == 'P')
|
||||
{
|
||||
swscanf( arg + 2, L"%u:%u", &pi.dwProcessId, &pi.dwThreadId );
|
||||
pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
|
||||
pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId );
|
||||
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 );
|
||||
@ -241,19 +354,7 @@ int main( void )
|
||||
if (log_level)
|
||||
DEBUGSTR( 1, NULL ); // start a new session
|
||||
|
||||
installed = (GetEnvironmentVariable( L"ANSICON_VER", NULL, 0 ) != 0);
|
||||
// If it's already installed, remove it. This serves two purposes: preserves
|
||||
// the parent's GRM; and unconditionally injects into GUI, without having to
|
||||
// worry about ANSICON_GUI.
|
||||
if (installed)
|
||||
{
|
||||
if (_isatty( 1 ))
|
||||
fputws( L"\33[m", stdout );
|
||||
FreeLibrary( GetModuleHandle( ANSIDLL ) );
|
||||
}
|
||||
|
||||
shell = run = TRUE;
|
||||
get_original_attr();
|
||||
|
||||
while (*arg == '-')
|
||||
{
|
||||
@ -277,40 +378,12 @@ int main( void )
|
||||
|
||||
case 'p':
|
||||
shell = FALSE;
|
||||
// If it's already installed, there's no need to do anything.
|
||||
if (installed)
|
||||
if (GetParentProcessInfo( &pi, arg + 3 ))
|
||||
{
|
||||
DEBUGSTR( 1, L"Already installed" );
|
||||
}
|
||||
else if (GetParentProcessInfo( &pi, arg ))
|
||||
{
|
||||
HANDLE hSnap;
|
||||
MODULEENTRY32 me;
|
||||
BOOL fOk;
|
||||
|
||||
pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
|
||||
pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId );
|
||||
SuspendThread( pi.hThread );
|
||||
// Find the base address of kernel32.dll.
|
||||
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE |
|
||||
TH32CS_SNAPMODULE32,
|
||||
pi.dwProcessId );
|
||||
if (hSnap != 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)
|
||||
{
|
||||
kernel32_base = me.modBaseAddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseHandle( hSnap );
|
||||
}
|
||||
if (!Inject( &pi, &gui, arg ))
|
||||
rc = 1;
|
||||
RemoteLoad( &pi );
|
||||
ResumeThread( pi.hThread );
|
||||
CloseHandle( pi.hThread );
|
||||
CloseHandle( pi.hProcess );
|
||||
@ -360,6 +433,15 @@ arg_out:
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the default attributes are the current attributes.
|
||||
if (GetEnvironmentVariable( L"ANSICON_DEF", buf, lenof(buf) ) != 0)
|
||||
{
|
||||
int a = wcstol( buf, NULL, 16 );
|
||||
if (a < 0)
|
||||
a = ((-a >> 4) & 15) | ((-a & 15) << 4);
|
||||
SetConsoleTextAttribute( hConOut, (WORD)a );
|
||||
}
|
||||
|
||||
if (run)
|
||||
{
|
||||
if (*cmd == '\0')
|
||||
@ -392,7 +474,7 @@ arg_out:
|
||||
}
|
||||
else
|
||||
{
|
||||
print_error( arg, arg );
|
||||
print_error( arg );
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
@ -404,8 +486,7 @@ arg_out:
|
||||
print_error( ANSIDLL );
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
if (*arg == 'e' || *arg == 'E')
|
||||
else if (*arg == 'e' || *arg == 'E')
|
||||
{
|
||||
cmd += 2;
|
||||
if (*cmd == ' ' || *cmd == '\t')
|
||||
@ -433,14 +514,10 @@ arg_out:
|
||||
get_file( arg, &argv, &cmd );
|
||||
} while (*arg);
|
||||
}
|
||||
|
||||
FreeLibrary( ansi );
|
||||
}
|
||||
|
||||
if (run || *arg)
|
||||
set_original_attr();
|
||||
else
|
||||
CloseHandle( hConOut );
|
||||
set_original_attr();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -495,30 +572,30 @@ void display( LPCTSTR name, BOOL title )
|
||||
}
|
||||
|
||||
|
||||
void print_error( LPCTSTR name, ... )
|
||||
void print_error( LPCTSTR name )
|
||||
{
|
||||
LPTSTR errmsg = NULL;
|
||||
DWORD err = GetLastError();
|
||||
va_list arg;
|
||||
DWORD err = GetLastError();
|
||||
|
||||
fputws( L"ANSICON: ", stderr );
|
||||
if (err == ERROR_BAD_EXE_FORMAT)
|
||||
{
|
||||
// This error requires an argument, which is a duplicate of name.
|
||||
va_start( arg, name );
|
||||
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, &arg );
|
||||
va_end( arg );
|
||||
fwprintf( stderr, L"ANSICON: %s", errmsg );
|
||||
// This error requires an argument, which is name.
|
||||
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||||
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, (va_list*)&name );
|
||||
fputws( errmsg, stderr );
|
||||
}
|
||||
else
|
||||
{
|
||||
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL );
|
||||
// Just in case there are other messages requiring args...
|
||||
if (errmsg == NULL)
|
||||
fwprintf( stderr, L"ANSICON: %s: Error %lu.\n", name, err );
|
||||
if (FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ))
|
||||
fwprintf( stderr, L"%s: %s", name, errmsg );
|
||||
else
|
||||
fwprintf( stderr, L"ANSICON: %s: %s", name, errmsg );
|
||||
fwprintf( stderr, L"%s: Error %lu.\n", name, err );
|
||||
}
|
||||
LocalFree( errmsg );
|
||||
}
|
||||
@ -759,7 +836,7 @@ void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
|
||||
for (path = name = arg; *path != '\0'; ++path)
|
||||
if (*path == '\\' || *path == '/')
|
||||
name = path + 1;
|
||||
glob = malloc( (globbed + 1) * sizeof(LPTSTR) + TSIZE(size) );
|
||||
glob = malloc( (globbed + 1) * PTRSZ + TSIZE(size) );
|
||||
path = (LPTSTR)(glob + globbed + 1);
|
||||
globbed = 0;
|
||||
fh = FindFirstFile( arg, &fd );
|
||||
@ -789,7 +866,7 @@ void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
|
||||
FindClose( fh );
|
||||
glob[globbed] = NULL;
|
||||
|
||||
qsort( glob, globbed, sizeof(LPTSTR), glob_sort );
|
||||
qsort( glob, globbed, PTRSZ, glob_sort );
|
||||
|
||||
wcscpy( name, glob[0] );
|
||||
globbed = 1;
|
||||
@ -799,6 +876,13 @@ void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
|
||||
}
|
||||
|
||||
|
||||
// VC macros don't like preprocessor statements mixed with strings.
|
||||
#ifdef _WIN64
|
||||
#define WINTYPE L"Windows"
|
||||
#else
|
||||
#define WINTYPE L"Win32"
|
||||
#endif
|
||||
|
||||
void help( void )
|
||||
{
|
||||
_putws(
|
||||
@ -806,11 +890,7 @@ L"ANSICON by Jason Hood <jadoxa@yahoo.com.au>.\n"
|
||||
L"Version " PVERS L" (" PDATE L"). Freeware.\n"
|
||||
L"http://ansicon.adoxa.vze.com/\n"
|
||||
L"\n"
|
||||
#ifdef _WIN64
|
||||
L"Process ANSI escape sequences in Windows console programs.\n"
|
||||
#else
|
||||
L"Process ANSI escape sequences in Win32 console programs.\n"
|
||||
#endif
|
||||
L"Process ANSI escape sequences in " WINTYPE L" console programs.\n"
|
||||
L"\n"
|
||||
L"ansicon [-l<level>] [-i] [-I] [-u] [-U] [-m[<attr>]] [-p]\n"
|
||||
L" [-e|E string | -t|T [file(s)] | program [args]]\n"
|
||||
|
33
ansicon.h
33
ansicon.h
@ -31,6 +31,22 @@
|
||||
#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x20
|
||||
#endif
|
||||
|
||||
#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)) )
|
||||
#define ReadProcMem(a, b, c) ReadProcessMemory( 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 PTRSZ sizeof(PVOID)
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -44,16 +60,23 @@ typedef struct
|
||||
} GRM, *PGRM; // Graphic Rendition Mode
|
||||
|
||||
|
||||
int ProcessType( LPPROCESS_INFORMATION, BOOL* );
|
||||
void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR );
|
||||
void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR );
|
||||
BOOL get_LLW32r( void );
|
||||
BOOL get_LLW64r( void );
|
||||
int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* );
|
||||
|
||||
void InjectDLL( LPPROCESS_INFORMATION, PBYTE );
|
||||
void InjectDLL32( LPPROCESS_INFORMATION, PBYTE );
|
||||
|
||||
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 int log_level;
|
||||
void DEBUGSTR( int level, LPTSTR szFormat, ... );
|
||||
|
||||
|
202
injdll.c
Normal file
202
injdll.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
Inject the DLL into the target process by modifying its import descriptor
|
||||
table. The target process must have been created suspended.
|
||||
*/
|
||||
|
||||
#include "ansicon.h"
|
||||
|
||||
|
||||
// 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 )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION minfo;
|
||||
PBYTE ptr;
|
||||
PVOID mem;
|
||||
|
||||
for (ptr = base;
|
||||
VirtualQueryEx( hProcess, ptr, &minfo, sizeof(minfo) );
|
||||
ptr += minfo.RegionSize)
|
||||
{
|
||||
if ((minfo.State & MEM_FREE) && minfo.RegionSize >= len)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
if ((PBYTE)minfo.BaseAddress - base > 0xFfFfFfFf - len)
|
||||
return NULL;
|
||||
#endif
|
||||
mem = VirtualAllocEx( hProcess, (PVOID)
|
||||
(((DWORD_PTR)minfo.BaseAddress + 0xFFFF) & ~0xFFFF),
|
||||
len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
|
||||
if (mem != NULL)
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
{
|
||||
DWORD rva;
|
||||
PVOID pMem;
|
||||
DWORD len;
|
||||
DWORD pr;
|
||||
IMAGE_DOS_HEADER DosHeader;
|
||||
IMAGE_NT_HEADERS NTHeader, *pNTHeader;
|
||||
PIMAGE_IMPORT_DESCRIPTOR pImports, pANSI_ImportDesc;
|
||||
IMAGE_COR20_HEADER ComHeader, *pComHeader;
|
||||
union
|
||||
{
|
||||
PBYTE pB;
|
||||
PLONG_PTR pL;
|
||||
} 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;
|
||||
pImports = malloc( len );
|
||||
if (pImports == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate memory." );
|
||||
return;
|
||||
}
|
||||
pMem = FindMem( ppi->hProcess, pBase, len );
|
||||
if (pMem == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate virtual memory." );
|
||||
free( pImports );
|
||||
return;
|
||||
}
|
||||
rva = (DWORD)((PBYTE)pMem - pBase);
|
||||
|
||||
ip.pL = (PLONG_PTR)pImports;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG + 1;
|
||||
*ip.pL++ = 0;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG + 1;
|
||||
*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;
|
||||
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress,
|
||||
pANSI_ImportDesc + 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 (NTHeader.IATDIR.VirtualAddress == 0)
|
||||
NTHeader.IATDIR = NTHeader.IMPORTDIR;
|
||||
|
||||
// Remove bound imports, so the updated import table is used.
|
||||
NTHeader.BOUNDDIR.VirtualAddress = 0;
|
||||
NTHeader.BOUNDDIR.Size = 0;
|
||||
|
||||
VirtProtVar( pNTHeader, PAGE_READWRITE );
|
||||
WriteProcVar( pNTHeader, &NTHeader );
|
||||
VirtProtVar( pNTHeader, pr );
|
||||
|
||||
// Remove the IL-only flag on a managed process.
|
||||
if (NTHeader.COMDIR.VirtualAddress != 0 && NTHeader.COMDIR.Size != 0)
|
||||
{
|
||||
pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress);
|
||||
ReadProcVar( pComHeader, &ComHeader );
|
||||
if (ComHeader.Flags & COMIMAGE_FLAGS_ILONLY)
|
||||
{
|
||||
ComHeader.Flags &= ~COMIMAGE_FLAGS_ILONLY;
|
||||
VirtProtVar( pComHeader, PAGE_READWRITE );
|
||||
WriteProcVar( pComHeader, &ComHeader );
|
||||
VirtProtVar( pComHeader, pr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN64
|
||||
void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
||||
{
|
||||
DWORD rva;
|
||||
PVOID pMem;
|
||||
DWORD len;
|
||||
DWORD pr;
|
||||
IMAGE_DOS_HEADER DosHeader;
|
||||
IMAGE_NT_HEADERS32 NTHeader, *pNTHeader;
|
||||
PIMAGE_IMPORT_DESCRIPTOR pImports, pANSI_ImportDesc;
|
||||
IMAGE_COR20_HEADER ComHeader, *pComHeader;
|
||||
union
|
||||
{
|
||||
PBYTE pB;
|
||||
PLONG pL;
|
||||
} 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;
|
||||
pImports = malloc( len );
|
||||
if (pImports == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate memory." );
|
||||
return;
|
||||
}
|
||||
pMem = FindMem( ppi->hProcess, pBase, len );
|
||||
if (pMem == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L" Failed to allocate virtual memory." );
|
||||
free( pImports );
|
||||
return;
|
||||
}
|
||||
rva = (DWORD)((PBYTE)pMem - pBase);
|
||||
|
||||
ip.pL = (PLONG)pImports;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1;
|
||||
*ip.pL++ = 0;
|
||||
*ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1;
|
||||
*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;
|
||||
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress,
|
||||
pANSI_ImportDesc + 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.BOUNDDIR.VirtualAddress = 0;
|
||||
NTHeader.BOUNDDIR.Size = 0;
|
||||
VirtProtVar( pNTHeader, PAGE_READWRITE );
|
||||
WriteProcVar( pNTHeader, &NTHeader );
|
||||
VirtProtVar( pNTHeader, pr );
|
||||
|
||||
if (NTHeader.COMDIR.VirtualAddress != 0 && NTHeader.COMDIR.Size != 0)
|
||||
{
|
||||
pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress);
|
||||
ReadProcVar( pComHeader, &ComHeader );
|
||||
if (ComHeader.Flags & COMIMAGE_FLAGS_ILONLY)
|
||||
{
|
||||
ComHeader.Flags &= ~COMIMAGE_FLAGS_ILONLY;
|
||||
VirtProtVar( pComHeader, PAGE_READWRITE );
|
||||
WriteProcVar( pComHeader, &ComHeader );
|
||||
VirtProtVar( pComHeader, pr );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
251
injdll32.c
251
injdll32.c
@ -1,251 +0,0 @@
|
||||
/*
|
||||
Inject code into the target process to load our DLL. The target thread
|
||||
should be suspended on entry; it remains suspended on exit.
|
||||
|
||||
Initially I used the "stack" method of injection. However, this fails
|
||||
when DEP is active, since that doesn't allow code to execute in the stack.
|
||||
To overcome this I used the "CreateRemoteThread" method. However, this
|
||||
would fail with Wselect, a program to assist batch files. Wselect runs,
|
||||
but it has no output. As it turns out, removing the suspended flag would
|
||||
make Wselect work, but it caused problems with everything else. So now I
|
||||
allocate a section of memory and change the context to run from there. At
|
||||
first I had an event to signal when the library was loaded, then the memory
|
||||
was released. However, that wouldn't work with -p and CMD.EXE (4NT v8
|
||||
worked fine). Since it's possible the DLL might start a process suspended,
|
||||
I've decided to simply keep the memory.
|
||||
*/
|
||||
|
||||
#include "ansicon.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#ifndef WOW64_CONTEXT_ALL
|
||||
#include "wow64.h"
|
||||
|
||||
TWow64GetThreadContext Wow64GetThreadContext;
|
||||
TWow64SetThreadContext Wow64SetThreadContext;
|
||||
#define IMPORT_WOW64
|
||||
#endif
|
||||
|
||||
#define CONTEXT WOW64_CONTEXT
|
||||
#undef CONTEXT_CONTROL
|
||||
#define CONTEXT_CONTROL WOW64_CONTEXT_CONTROL
|
||||
#define GetThreadContext Wow64GetThreadContext
|
||||
#define SetThreadContext Wow64SetThreadContext
|
||||
#endif
|
||||
|
||||
extern DWORD LLW32r;
|
||||
LPVOID kernel32_base;
|
||||
PIMAGE_DOS_HEADER pDosHeader;
|
||||
|
||||
#define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset))
|
||||
|
||||
int export_cmp( const void* a, const void* b )
|
||||
{
|
||||
return strcmp( (LPCSTR)a, MakeVA( LPCSTR, *(const PDWORD)b ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get the relative address of LoadLibraryW direct from kernel32.dll.
|
||||
*/
|
||||
BOOL 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;
|
||||
|
||||
#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 FALSE;
|
||||
}
|
||||
// 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->OptionalHeader.
|
||||
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
|
||||
VirtualAddress );
|
||||
|
||||
fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions );
|
||||
name_table = MakeVA( PDWORD, pExportDir->AddressOfNames );
|
||||
ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals );
|
||||
|
||||
pLLW = bsearch( "LoadLibraryW", name_table, pExportDir->NumberOfNames,
|
||||
sizeof(DWORD), export_cmp );
|
||||
if (pLLW == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L"Could not find LoadLibraryW!" );
|
||||
FreeLibrary( kernel32 );
|
||||
return FALSE;
|
||||
}
|
||||
LLW32r = fun_table[ord_table[pLLW - name_table]];
|
||||
|
||||
FreeLibrary( kernel32 );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
|
||||
{
|
||||
CONTEXT context;
|
||||
DWORD ep;
|
||||
BOOL eip;
|
||||
LPVOID mem;
|
||||
DWORD mem32;
|
||||
DWORD pr;
|
||||
DWORD LLW;
|
||||
|
||||
DWORD len;
|
||||
#define CODESIZE 20
|
||||
BYTE code[CODESIZE+TSIZE(MAX_PATH)];
|
||||
union
|
||||
{
|
||||
PBYTE pB;
|
||||
PDWORD pL;
|
||||
} ip;
|
||||
|
||||
struct unicode_string
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
DWORD Buffer;
|
||||
};
|
||||
struct ldr_module // incomplete definition
|
||||
{
|
||||
DWORD next, prev;
|
||||
DWORD baseAddress;
|
||||
DWORD entryPoint;
|
||||
DWORD sizeOfImage;
|
||||
struct unicode_string fullDllName;
|
||||
struct unicode_string baseDllName;
|
||||
} ldr;
|
||||
WCHAR basename[MAX_PATH];
|
||||
|
||||
#ifdef IMPORT_WOW64
|
||||
if (Wow64GetThreadContext == 0)
|
||||
{
|
||||
#define GETPROC( proc ) proc = (T##proc)GetProcAddress( hKernel, #proc )
|
||||
HMODULE hKernel = GetModuleHandle( L"kernel32.dll" );
|
||||
GETPROC( Wow64GetThreadContext );
|
||||
GETPROC( Wow64SetThreadContext );
|
||||
// Assume if one is defined, so is the other.
|
||||
if (Wow64GetThreadContext == 0)
|
||||
{
|
||||
DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext." );
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
len = TSIZE(lstrlen( dll ) + 1);
|
||||
if (len > TSIZE(MAX_PATH))
|
||||
return;
|
||||
CopyMemory( code + CODESIZE, dll, len );
|
||||
len += CODESIZE;
|
||||
|
||||
context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
|
||||
GetThreadContext( ppi->hThread, &context );
|
||||
mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT,
|
||||
PAGE_READWRITE );
|
||||
mem32 = (DWORD)(DWORD_PTR)mem;
|
||||
|
||||
ip.pB = code;
|
||||
|
||||
// Determine the base address of kernel32.dll. If injecting into the parent
|
||||
// process, the base has already been determined. Otherwise, use the PEB to
|
||||
// walk the loaded modules.
|
||||
if (kernel32_base != 0)
|
||||
{
|
||||
ep = context.Eip;
|
||||
eip = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When a process is created suspended, EAX has the entry point and EBX
|
||||
// points to the PEB.
|
||||
if (!ReadProcessMemory( ppi->hProcess, UIntToPtr( context.Ebx + 0x0C ),
|
||||
ip.pL, 4, NULL ))
|
||||
{
|
||||
DEBUGSTR( 1, L"Failed to read Ldr from PEB." );
|
||||
return;
|
||||
}
|
||||
ep = context.Eax;
|
||||
eip = FALSE;
|
||||
// In case we're a bit slow (which seems to be unlikely), set up an
|
||||
// infinite loop as the entry point.
|
||||
WriteProcessMemory( ppi->hProcess, mem, "\xEB\xFE", 2, NULL );
|
||||
FlushInstructionCache( ppi->hProcess, mem, 2 );
|
||||
context.Eax = mem32;
|
||||
SetThreadContext( ppi->hThread, &context );
|
||||
VirtualProtectEx( ppi->hProcess, mem, len, PAGE_EXECUTE, &pr );
|
||||
// Now resume the thread, as the PEB hasn't even been created yet.
|
||||
ResumeThread( ppi->hThread );
|
||||
while (*ip.pL == 0)
|
||||
{
|
||||
Sleep( 0 );
|
||||
ReadProcessMemory( ppi->hProcess, UIntToPtr( context.Ebx + 0x0C ),
|
||||
ip.pL, 4, NULL );
|
||||
}
|
||||
// Read PEB_LDR_DATA.InInitializationOrderModuleList.Flink.
|
||||
ReadProcessMemory( ppi->hProcess, UIntToPtr( *ip.pL + 0x1c ),
|
||||
&ip.pL[1], 4, NULL );
|
||||
// Sometimes we're so quick ntdll.dll is the only one present, so keep
|
||||
// looping until kernel32.dll shows up.
|
||||
for (;;)
|
||||
{
|
||||
ldr.next = ip.pL[1];
|
||||
do
|
||||
{
|
||||
ReadProcessMemory( ppi->hProcess, UIntToPtr( ldr.next ),
|
||||
&ldr, sizeof(ldr), NULL );
|
||||
ReadProcessMemory( ppi->hProcess, UIntToPtr( ldr.baseDllName.Buffer ),
|
||||
basename, ldr.baseDllName.MaximumLength, NULL );
|
||||
if (_wcsicmp( basename, L"kernel32.dll" ) == 0)
|
||||
{
|
||||
kernel32_base = UIntToPtr( ldr.baseAddress );
|
||||
goto gotit;
|
||||
}
|
||||
} while (ldr.next != *ip.pL + 0x1c);
|
||||
}
|
||||
gotit:
|
||||
SuspendThread( ppi->hThread );
|
||||
VirtualProtectEx( ppi->hProcess, mem, len, pr, &pr );
|
||||
}
|
||||
LLW = PtrToUint( kernel32_base ) + LLW32r;
|
||||
kernel32_base = 0;
|
||||
|
||||
*ip.pB++ = 0x68; // push ep
|
||||
*ip.pL++ = ep;
|
||||
*ip.pB++ = 0x9c; // pushf
|
||||
*ip.pB++ = 0x60; // pusha
|
||||
*ip.pB++ = 0x68; // push L"path\to\ANSI32.dll"
|
||||
*ip.pL++ = mem32 + CODESIZE;
|
||||
*ip.pB++ = 0xe8; // call LoadLibraryW
|
||||
*ip.pL++ = LLW - (mem32 + (DWORD)(ip.pB+4 - code));
|
||||
*ip.pB++ = 0x61; // popa
|
||||
*ip.pB++ = 0x9d; // popf
|
||||
*ip.pB++ = 0xc3; // ret
|
||||
|
||||
WriteProcessMemory( ppi->hProcess, mem, code, len, NULL );
|
||||
FlushInstructionCache( ppi->hProcess, mem, len );
|
||||
VirtualProtectEx( ppi->hProcess, mem, len, PAGE_EXECUTE, &pr );
|
||||
|
||||
if (eip)
|
||||
{
|
||||
context.Eip = mem32;
|
||||
SetThreadContext( ppi->hThread, &context );
|
||||
}
|
||||
}
|
243
injdll64.c
243
injdll64.c
@ -1,243 +0,0 @@
|
||||
/*
|
||||
Inject code into the target process to load our DLL. The target thread
|
||||
should be suspended on entry; it remains suspended on exit.
|
||||
|
||||
Initially I used the "stack" method of injection. However, this fails
|
||||
when DEP is active, since that doesn't allow code to execute in the stack.
|
||||
To overcome this I used the "CreateRemoteThread" method. However, this
|
||||
would fail with Wselect, a program to assist batch files. Wselect runs,
|
||||
but it has no output. As it turns out, removing the suspended flag would
|
||||
make Wselect work, but it caused problems with everything else. So now I
|
||||
allocate a section of memory and change the context to run from there. At
|
||||
first I had an event to signal when the library was loaded, then the memory
|
||||
was released. However, that wouldn't work with -p and CMD.EXE (4NT v8
|
||||
worked fine). Since it's possible the DLL might start a process suspended,
|
||||
I've decided to simply keep the memory.
|
||||
*/
|
||||
|
||||
#include "ansicon.h"
|
||||
|
||||
extern DWORD LLW64r;
|
||||
extern LPVOID kernel32_base;
|
||||
extern PIMAGE_DOS_HEADER pDosHeader;
|
||||
|
||||
#define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset))
|
||||
|
||||
extern int export_cmp( const void* a, const void* b );
|
||||
|
||||
|
||||
/*
|
||||
Get the relative address of LoadLibraryW direct from kernel32.dll.
|
||||
*/
|
||||
BOOL 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;
|
||||
|
||||
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 FALSE;
|
||||
}
|
||||
// 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->OptionalHeader.
|
||||
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
|
||||
VirtualAddress );
|
||||
|
||||
fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions );
|
||||
name_table = MakeVA( PDWORD, pExportDir->AddressOfNames );
|
||||
ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals );
|
||||
|
||||
pLLW = bsearch( "LoadLibraryW", name_table, pExportDir->NumberOfNames,
|
||||
sizeof(DWORD), export_cmp );
|
||||
if (pLLW == NULL)
|
||||
{
|
||||
DEBUGSTR( 1, L"Could not find LoadLibraryW!" );
|
||||
FreeLibrary( kernel32 );
|
||||
return FALSE;
|
||||
}
|
||||
LLW64r = fun_table[ord_table[pLLW - name_table]];
|
||||
|
||||
FreeLibrary( kernel32 );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
|
||||
{
|
||||
CONTEXT context;
|
||||
DWORD64 ep;
|
||||
BOOL rip;
|
||||
LPVOID mem;
|
||||
DWORD pr;
|
||||
DWORD64 LLW;
|
||||
|
||||
union
|
||||
{
|
||||
PBYTE pB;
|
||||
PDWORD64 pL;
|
||||
} ip;
|
||||
|
||||
struct unicode_string
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
DWORD64 Buffer;
|
||||
};
|
||||
struct ldr_module // incomplete definition
|
||||
{
|
||||
DWORD64 next, prev;
|
||||
DWORD64 baseAddress;
|
||||
DWORD64 entryPoint;
|
||||
DWORD64 sizeOfImage;
|
||||
struct unicode_string fullDllName;
|
||||
struct unicode_string baseDllName;
|
||||
} ldr;
|
||||
WCHAR basename[MAX_PATH];
|
||||
|
||||
DWORD len;
|
||||
#define CODESIZE 92
|
||||
static BYTE code[CODESIZE+TSIZE(MAX_PATH)] = {
|
||||
0,0,0,0,0,0,0,0, // original rip
|
||||
0,0,0,0,0,0,0,0, // LoadLibraryW
|
||||
0x9C, // pushfq
|
||||
0x50, // push rax
|
||||
0x51, // push rcx
|
||||
0x52, // push rdx
|
||||
0x53, // push rbx
|
||||
0x55, // push rbp
|
||||
0x56, // push rsi
|
||||
0x57, // push rdi
|
||||
0x41,0x50, // push r8
|
||||
0x41,0x51, // push r9
|
||||
0x41,0x52, // push r10
|
||||
0x41,0x53, // push r11
|
||||
0x41,0x54, // push r12
|
||||
0x41,0x55, // push r13
|
||||
0x41,0x56, // push r14
|
||||
0x41,0x57, // push r15
|
||||
0x48,0x83,0xEC,0x28, // sub rsp, 40
|
||||
0x48,0x8D,0x0D,41,0,0,0, // lea ecx, L"path\to\ANSI64.dll"
|
||||
0xFF,0x15,-49,-1,-1,-1, // call LoadLibraryW
|
||||
0x48,0x83,0xC4,0x28, // add rsp, 40
|
||||
0x41,0x5F, // pop r15
|
||||
0x41,0x5E, // pop r14
|
||||
0x41,0x5D, // pop r13
|
||||
0x41,0x5C, // pop r12
|
||||
0x41,0x5B, // pop r11
|
||||
0x41,0x5A, // pop r10
|
||||
0x41,0x59, // pop r9
|
||||
0x41,0x58, // pop r8
|
||||
0x5F, // pop rdi
|
||||
0x5E, // pop rsi
|
||||
0x5D, // pop rbp
|
||||
0x5B, // pop rbx
|
||||
0x5A, // pop rdx
|
||||
0x59, // pop rcx
|
||||
0x58, // pop rax
|
||||
0x9D, // popfq
|
||||
0xFF,0x25,-91,-1,-1,-1, // jmp original Rip
|
||||
0, // dword alignment for LLW, fwiw
|
||||
};
|
||||
|
||||
len = TSIZE(lstrlen( dll ) + 1);
|
||||
if (len > TSIZE(MAX_PATH))
|
||||
return;
|
||||
CopyMemory( code + CODESIZE, dll, len );
|
||||
len += CODESIZE;
|
||||
|
||||
context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
|
||||
GetThreadContext( ppi->hThread, &context );
|
||||
mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT,
|
||||
PAGE_READWRITE );
|
||||
|
||||
ip.pB = code;
|
||||
|
||||
// Determine the base address of kernel32.dll. If injecting into the parent
|
||||
// process, the base has already been determined. Otherwise, use the PEB to
|
||||
// walk the loaded modules.
|
||||
if (kernel32_base != 0)
|
||||
{
|
||||
ep = context.Rip;
|
||||
rip = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When a process is created suspended, RCX has the entry point and RDX
|
||||
// points to the PEB.
|
||||
if (!ReadProcessMemory( ppi->hProcess, (LPVOID)(context.Rdx + 0x18),
|
||||
ip.pL, 8, NULL ))
|
||||
{
|
||||
DEBUGSTR( 1, L"Failed to read Ldr from PEB." );
|
||||
return;
|
||||
}
|
||||
ep = context.Rcx;
|
||||
rip = FALSE;
|
||||
// In case we're a bit slow (which seems to be unlikely), set up an
|
||||
// infinite loop as the entry point.
|
||||
WriteProcessMemory( ppi->hProcess, (PBYTE)mem + 16, "\xEB\xFE", 2, NULL );
|
||||
FlushInstructionCache( ppi->hProcess, (PBYTE)mem + 16, 2 );
|
||||
context.Rcx = (DWORD64)mem + 16;
|
||||
SetThreadContext( ppi->hThread, &context );
|
||||
VirtualProtectEx( ppi->hProcess, mem, len, PAGE_EXECUTE, &pr );
|
||||
// Now resume the thread, as the PEB hasn't even been created yet.
|
||||
ResumeThread( ppi->hThread );
|
||||
while (*ip.pL == 0)
|
||||
{
|
||||
Sleep( 0 );
|
||||
ReadProcessMemory( ppi->hProcess, (LPVOID)(context.Rdx + 0x18),
|
||||
ip.pL, 8, NULL );
|
||||
}
|
||||
// Read PEB_LDR_DATA.InInitializationOrderModuleList.Flink.
|
||||
ReadProcessMemory( ppi->hProcess, (LPVOID)(*ip.pL + 0x30),
|
||||
&ip.pL[1], 8, NULL );
|
||||
// Sometimes we're so quick ntdll.dll is the only one present, so keep
|
||||
// looping until kernel32.dll shows up.
|
||||
for (;;)
|
||||
{
|
||||
ldr.next = ip.pL[1];
|
||||
do
|
||||
{
|
||||
ReadProcessMemory( ppi->hProcess, (LPVOID)ldr.next,
|
||||
&ldr, sizeof(ldr), NULL );
|
||||
ReadProcessMemory( ppi->hProcess, (LPVOID)ldr.baseDllName.Buffer,
|
||||
basename, ldr.baseDllName.MaximumLength, NULL );
|
||||
if (_wcsicmp( basename, L"kernel32.dll" ) == 0)
|
||||
{
|
||||
kernel32_base = (LPVOID)ldr.baseAddress;
|
||||
goto gotit;
|
||||
}
|
||||
} while (ldr.next != *ip.pL + 0x30);
|
||||
}
|
||||
gotit:
|
||||
SuspendThread( ppi->hThread );
|
||||
VirtualProtectEx( ppi->hProcess, mem, len, pr, &pr );
|
||||
}
|
||||
LLW = (DWORD64)kernel32_base + LLW64r;
|
||||
kernel32_base = 0;
|
||||
|
||||
*ip.pL++ = ep;
|
||||
*ip.pL++ = LLW;
|
||||
|
||||
WriteProcessMemory( ppi->hProcess, mem, code, len, NULL );
|
||||
FlushInstructionCache( ppi->hProcess, mem, len );
|
||||
VirtualProtectEx( ppi->hProcess, mem, len, PAGE_EXECUTE, &pr );
|
||||
|
||||
if (rip)
|
||||
{
|
||||
context.Rip = (DWORD64)mem + 16;
|
||||
SetThreadContext( ppi->hThread, &context );
|
||||
}
|
||||
}
|
26
makefile.gcc
26
makefile.gcc
@ -16,13 +16,16 @@
|
||||
#
|
||||
# Tested with:
|
||||
# * MinGW/gcc 4.7.2;
|
||||
# * tdm-gcc-4.7.1-2;
|
||||
# * tdm64-gcc-4.7.1-3;
|
||||
# * tdm-gcc-4.8.1-3;
|
||||
# * tdm64-gcc-4.8.1-3;
|
||||
# * MinGW-builds x64-4.8.1-release-posix-seh-rev1.
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall
|
||||
|
||||
# Identify ansicon.exe using "ANSI" as a version number.
|
||||
IVER = -Wl,--major-image-version,20033,--minor-image-version,18771
|
||||
|
||||
#ARCH = 32
|
||||
#ARCH = 64
|
||||
#ARCH = multi
|
||||
@ -41,8 +44,8 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
X86OBJS = x86/proctype.o x86/injdll32.o x86/util.o
|
||||
X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/util.o
|
||||
X86OBJS = x86/proctype.o x86/injdll.o x86/util.o
|
||||
X64OBJS = x64/proctype.o x64/injdll.o x64/util.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
|
||||
@ -67,7 +70,7 @@ x86/%v.o: %.rc version.h
|
||||
$(RCmsg)windres -U _WIN64 -F pe-i386 $< $@
|
||||
|
||||
x64/%.o: %.c ansicon.h
|
||||
$(CCmsg)$(CC) -m64 -c $(CFLAGS) $< -o $@
|
||||
$(CCmsg)$(CC) -m64 -g -c $(CFLAGS) $< -o $@
|
||||
|
||||
x64/%v.o: %.rc version.h
|
||||
$(RCmsg)windres -F pe-x86-64 $< $@
|
||||
@ -89,8 +92,8 @@ ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll
|
||||
x86:
|
||||
cmd /c "mkdir x86"
|
||||
|
||||
x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o
|
||||
$(LDmsg)$(CC) -m32 $+ -s -o $@
|
||||
x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/LLW.o x86/ansiconv.o
|
||||
$(LDmsg)$(CC) -m32 $+ -s -o $@ $(IVER)
|
||||
|
||||
x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o
|
||||
$(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC0000
|
||||
@ -98,14 +101,15 @@ x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o
|
||||
x64:
|
||||
cmd /c "mkdir x64"
|
||||
|
||||
x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o
|
||||
$(LDmsg)$(CC) -m64 $+ -s -o $@
|
||||
x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/LLW.o x64/ansiconv.o
|
||||
$(LDmsg)$(CC) -m64 $+ -s -o $@ $(IVER)
|
||||
|
||||
x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o
|
||||
$(LDmsg)$(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC000000
|
||||
|
||||
x64/ANSI32.dll: x64/ANSI32.o x64/proctype32.o x86/injdll32.o x86/util.o x86/ansiv.o
|
||||
$(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC0000
|
||||
x64/ANSI32.dll: x64/ANSI32.o x64/proctype32.o x86/injdll.o x86/util.o x86/ansiv.o
|
||||
$(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll \
|
||||
-Wl,-shared,--image-base,0xAC0000,--large-address-aware
|
||||
|
||||
x86/ansicon.o: version.h
|
||||
x86/ANSI.o: version.h
|
||||
|
31
makefile.vc
31
makefile.vc
@ -62,8 +62,11 @@ MT = mt.exe
|
||||
CFLAGS = /nologo /W3 /O2 $(SHARE) /D_CRT_SECURE_NO_WARNINGS
|
||||
LIBS = advapi32.lib user32.lib $(LIBS64)
|
||||
|
||||
X86OBJS = x86\proctype.obj x86\injdll32.obj x86\util.obj
|
||||
X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj
|
||||
# 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
|
||||
|
||||
!IF !DEFINED(V)
|
||||
V = 0
|
||||
@ -90,15 +93,16 @@ ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll
|
||||
x86:
|
||||
mkdir x86
|
||||
|
||||
x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) /link /filealign:512
|
||||
x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\LLW.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
|
||||
@del $@.manifest
|
||||
!ENDIF
|
||||
|
||||
x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link /base:0xAC0000 /section:.shared,s /filealign:512
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \
|
||||
/base:0xAC0000 /section:.shared,s /filealign:512
|
||||
!IF "$(_NMAKE_VER)" == "9.00.30729.01"
|
||||
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
|
||||
@del $@.manifest
|
||||
@ -107,14 +111,16 @@ x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res
|
||||
x64:
|
||||
mkdir x64
|
||||
|
||||
x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS)
|
||||
x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\LLW.obj x64\ansicon.res
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK)
|
||||
|
||||
x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link /base:0xAC000000 /section:.shared,s
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \
|
||||
/base:0xAC000000 /section:.shared,s
|
||||
|
||||
x64\ANSI32.dll: x64\ANSI32.obj x64\proctype32.obj x86\injdll32.obj x86\util.obj x86\ansi.res
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link /base:0xAC0000 /section:.shared,s /filealign:512
|
||||
x64\ANSI32.dll: x64\ANSI32.obj x64\proctype32.obj x86\injdll.obj x86\util.obj x86\ansi.res
|
||||
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \
|
||||
/base:0xAC0000 /section:.shared,s /filealign:512 /largeaddressaware
|
||||
!IF "$(_NMAKE_VER)" == "9.00.30729.01"
|
||||
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
|
||||
@del $@.manifest
|
||||
@ -125,12 +131,13 @@ ansicon.rc: version.h
|
||||
ANSI.c: ansicon.h version.h
|
||||
ANSI.rc: version.h
|
||||
util.c: ansicon.h version.h
|
||||
injdll32.c: ansicon.h
|
||||
injdll64.c: ansicon.h
|
||||
injdll.c: ansicon.h
|
||||
proctype.c: ansicon.h
|
||||
LLW.c: ansicon.h
|
||||
|
||||
x64\ANSI32.obj: ANSI.c
|
||||
$(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?
|
||||
|
||||
x64\proctype32.obj: proctype.c
|
||||
$(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?
|
||||
|
||||
|
133
proctype.c
133
proctype.c
@ -1,99 +1,96 @@
|
||||
/*
|
||||
Test for a valid process. This may sometimes detect GUI, even for a console
|
||||
process. I think this is due to a DLL being loaded in the address space
|
||||
before the main image. Ideally I could just use the base address directly,
|
||||
but that doesn't seem easy to do for another process - there doesn't seem to
|
||||
be a GetModuleHandle for another process. The CreateRemoteThread trick won't
|
||||
work with 64-bit (exit code is DWORD) and setting it up to make it work
|
||||
hardly seems worth it. There's GetModuleInformation, but passing in NULL just
|
||||
returns a base of NULL, so that's no help. Since 64/32 is sufficient, let
|
||||
ansicon.exe handle the difference between console/GUI.
|
||||
|
||||
Update: ignore images characterised as DLL.
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "ansicon.h"
|
||||
|
||||
|
||||
int ProcessType( LPPROCESS_INFORMATION pinfo, BOOL* gui )
|
||||
int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
|
||||
{
|
||||
char* ptr;
|
||||
PBYTE ptr;
|
||||
MEMORY_BASIC_INFORMATION minfo;
|
||||
IMAGE_DOS_HEADER dos_header;
|
||||
IMAGE_NT_HEADERS nt_header;
|
||||
SIZE_T read;
|
||||
|
||||
*pBase = NULL;
|
||||
*gui = FALSE;
|
||||
|
||||
for (ptr = NULL;
|
||||
VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) );
|
||||
VirtualQueryEx( ppi->hProcess, ptr, &minfo, sizeof(minfo) );
|
||||
ptr += minfo.RegionSize)
|
||||
{
|
||||
if (minfo.BaseAddress == minfo.AllocationBase &&
|
||||
ReadProcessMemory( pinfo->hProcess, minfo.AllocationBase,
|
||||
&dos_header, sizeof(dos_header), &read ))
|
||||
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))
|
||||
{
|
||||
if (dos_header.e_magic == IMAGE_DOS_SIGNATURE)
|
||||
// Don't load into ansicon.exe, it wants to do that itself.
|
||||
if (nt_header.OptionalHeader.MajorImageVersion == 20033 &&
|
||||
nt_header.OptionalHeader.MinorImageVersion == 18771)
|
||||
return -1;
|
||||
|
||||
*pBase = minfo.BaseAddress;
|
||||
if (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)
|
||||
*gui = TRUE;
|
||||
if (*gui ||
|
||||
nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
|
||||
{
|
||||
if (ReadProcessMemory( pinfo->hProcess, (char*)minfo.AllocationBase +
|
||||
dos_header.e_lfanew, &nt_header,
|
||||
sizeof(nt_header), &read ))
|
||||
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
|
||||
{
|
||||
if (nt_header.Signature == IMAGE_NT_SIGNATURE &&
|
||||
(nt_header.FileHeader.Characteristics &
|
||||
(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL))
|
||||
== IMAGE_FILE_EXECUTABLE_IMAGE)
|
||||
{
|
||||
*gui = (nt_header.OptionalHeader.Subsystem
|
||||
== IMAGE_SUBSYSTEM_WINDOWS_GUI);
|
||||
if (nt_header.OptionalHeader.Subsystem ==
|
||||
IMAGE_SUBSYSTEM_WINDOWS_CUI || *gui)
|
||||
{
|
||||
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
|
||||
{
|
||||
// Microsoft ignores precision on %p.
|
||||
DEBUGSTR( 1, L" 32-bit %s (base = %.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
(DWORD)(DWORD_PTR)minfo.AllocationBase );
|
||||
return 32;
|
||||
}
|
||||
#ifdef _WIN64
|
||||
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
|
||||
{
|
||||
DEBUGSTR( 1, L" 64-bit %s (base = %p)",
|
||||
(*gui) ? L"GUI" : L"console", minfo.AllocationBase );
|
||||
return 64;
|
||||
}
|
||||
#elif defined(W32ON64)
|
||||
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
|
||||
{
|
||||
DEBUGSTR( 1, L" 64-bit %s",
|
||||
(*gui) ? L"GUI" : L"console" );
|
||||
return 64;
|
||||
}
|
||||
#endif
|
||||
DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)",
|
||||
nt_header.FileHeader.Machine );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)",
|
||||
nt_header.OptionalHeader.Subsystem );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
DEBUGSTR( 1, L" 32-bit %s (base = %.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
return 32;
|
||||
}
|
||||
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
|
||||
{
|
||||
#ifdef _WIN64
|
||||
DEBUGSTR( 1, L" 64-bit %s (base = %.8X_%.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
(DWORD)((DWORD_PTR)minfo.BaseAddress >> 32),
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
return 64;
|
||||
#elif defined(W32ON64)
|
||||
// Console will log due to -P, but GUI may be ignored (if not,
|
||||
// this'll show up twice).
|
||||
if (*gui)
|
||||
DEBUGSTR( 1, L" 64-bit GUI (base = 00000000_%.8X)",
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
return 64;
|
||||
#else
|
||||
DEBUGSTR( 1, L" 64-bit %s (base = 00000000_%.8X)",
|
||||
(*gui) ? L"GUI" : L"console",
|
||||
PtrToUint( minfo.BaseAddress ) );
|
||||
DEBUGSTR( 1, L" Unsupported (use x64\\ansicon)" );
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)",
|
||||
nt_header.FileHeader.Machine );
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)",
|
||||
nt_header.OptionalHeader.Subsystem );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#ifndef _WIN64
|
||||
// If a 32-bit process loads a 64-bit one, we may miss the base
|
||||
// address. If the pointer overflows, assume 64-bit.
|
||||
if (((DWORD)ptr >> 12) + ((DWORD)minfo.RegionSize >> 12) > 0x80000)
|
||||
if (((DWORD)ptr >> 12) + ((DWORD)minfo.RegionSize >> 12) >= 0x100000)
|
||||
{
|
||||
#ifdef W32ON64
|
||||
DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit console" );
|
||||
DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit" );
|
||||
return 64;
|
||||
#else
|
||||
DEBUGSTR( 1, L" Ignoring apparent 64-bit process" );
|
||||
DEBUGSTR( 1, L" Ignoring apparent 64-bit process (use x64\\ansicon)" );
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
38
readme.txt
38
readme.txt
@ -1,9 +1,9 @@
|
||||
|
||||
ANSICON
|
||||
|
||||
Copyright 2005-2013 Jason Hood
|
||||
Copyright 2005-2014 Jason Hood
|
||||
|
||||
Version 1.66. Freeware
|
||||
Version 1.70. Freeware
|
||||
|
||||
|
||||
Description
|
||||
@ -17,7 +17,7 @@ Requirements
|
||||
============
|
||||
|
||||
32-bit: Windows 2000 Professional and later (it won't work with NT or 9X).
|
||||
64-bit: Vista and later (it won't work with XP64).
|
||||
64-bit: AMD64 (IA64 could work with a little modification).
|
||||
|
||||
|
||||
Installation
|
||||
@ -93,10 +93,10 @@ Usage
|
||||
16 Log all imported modules (add to any of the above)
|
||||
|
||||
The log option will not work with '-p'; set the environment variable
|
||||
ANSICON_LOG instead. The variable is only read once when a new process is
|
||||
started; changing it won't affect running processes. If you identify a
|
||||
module that causes problems, add it to the ANSICON_EXC environment variable
|
||||
(see ANSICON_API below, but the extension is required).
|
||||
ANSICON_LOG (to the number) instead. The variable is only read once when a
|
||||
process is started; changing it won't affect running processes. If you
|
||||
identify a module that causes problems, add it to the ANSICON_EXC environ-
|
||||
ment variable (see ANSICON_API below, but the extension is required).
|
||||
|
||||
E.g.: 'ansicon -l5' will start a new command processor, logging every pro-
|
||||
cess it starts along with their output.
|
||||
@ -264,12 +264,26 @@ Limitations
|
||||
|
||||
ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll
|
||||
|
||||
An application using multiple screen buffers will not have separate
|
||||
attributes in each buffer.
|
||||
|
||||
|
||||
Version History
|
||||
===============
|
||||
|
||||
Legend: + added, - bug-fixed, * changed.
|
||||
|
||||
1.70 - 4 February, 2014:
|
||||
- don't hook again if using LoadLibrary or LoadLibraryEx;
|
||||
- update the LoadLibraryEx flags that shouldn't hook;
|
||||
- restore original attributes on detach (for LoadLibrary/FreeLibrary usage);
|
||||
- ansicon.exe will start with ANSICON_DEF (if defined and -m not used);
|
||||
- an installed ansicon.exe will restore current (not default) attributes;
|
||||
* inject into a created process by modifying the import descriptor table
|
||||
(use CreateRemoteThread for -p);
|
||||
* log: remove the quotes around the CreateProcess command line;
|
||||
add an underscore in 64-bit addresses to distinguish 8-digit groups.
|
||||
|
||||
1.66 - 20 September, 2013:
|
||||
- fix 32-bit process trying to detect 64-bit process.
|
||||
|
||||
@ -442,12 +456,6 @@ Contact
|
||||
http://ansicon.adoxa.vze.com/
|
||||
https://github.com/adoxa/ansicon
|
||||
|
||||
Jason Hood
|
||||
11 Buckle Street
|
||||
North Rockhampton
|
||||
Qld 4701
|
||||
Australia
|
||||
|
||||
|
||||
Distribution
|
||||
============
|
||||
@ -461,5 +469,5 @@ Distribution
|
||||
in LICENSE.txt.
|
||||
|
||||
|
||||
===============================
|
||||
Jason Hood, 20 September, 2013.
|
||||
=============================
|
||||
Jason Hood, 4 February, 2014.
|
||||
|
58
util.c
58
util.c
@ -8,9 +8,14 @@
|
||||
|
||||
TCHAR prog_path[MAX_PATH];
|
||||
LPTSTR prog;
|
||||
|
||||
int log_level;
|
||||
char tempfile[MAX_PATH];
|
||||
DWORD pid;
|
||||
|
||||
char ansi_dll[MAX_PATH];
|
||||
DWORD ansi_len;
|
||||
#ifdef _WIN64
|
||||
char* ansi_bits;
|
||||
#endif
|
||||
|
||||
|
||||
// Get just the name of the program: "C:\path\program.exe" -> "program".
|
||||
@ -37,13 +42,51 @@ 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 )
|
||||
{
|
||||
BOOL bad;
|
||||
|
||||
ansi_len = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, dll, -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;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, dll, -1,
|
||||
ansi_dll, MAX_PATH, NULL, NULL );
|
||||
#ifdef _WIN64
|
||||
ansi_bits = ansi_dll + ansi_len - 7;
|
||||
#endif
|
||||
ansi_len = (ansi_len + 3) & ~3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DEBUGSTR( int level, LPTSTR szFormat, ... )
|
||||
{
|
||||
TCHAR szBuffer[1024], szEscape[1024];
|
||||
static char tempfile[MAX_PATH];
|
||||
static DWORD pid;
|
||||
|
||||
TCHAR szBuffer[1024], szEscape[1024];
|
||||
va_list pArgList;
|
||||
HANDLE mutex;
|
||||
DWORD wait;
|
||||
FILE* file;
|
||||
HANDLE mutex;
|
||||
DWORD wait;
|
||||
FILE* file;
|
||||
|
||||
if ((log_level & 3) < level && !(level & 4 & log_level))
|
||||
return;
|
||||
@ -55,6 +98,7 @@ void DEBUGSTR( int level, LPTSTR szFormat, ... )
|
||||
}
|
||||
if (szFormat == NULL)
|
||||
{
|
||||
// Explicitly use 't', as _fmode might be binary.
|
||||
file = fopen( tempfile, (log_level & 8) ? "at" : "wt" );
|
||||
if (file != NULL)
|
||||
{
|
||||
@ -116,7 +160,7 @@ void DEBUGSTR( int level, LPTSTR szFormat, ... )
|
||||
|
||||
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
|
||||
wait = WaitForSingleObject( mutex, 500 );
|
||||
file = fopen( tempfile, "at" ); // _fmode might be binary
|
||||
file = fopen( tempfile, "at" );
|
||||
if (file != NULL)
|
||||
{
|
||||
fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat );
|
||||
|
10
version.h
10
version.h
@ -2,11 +2,11 @@
|
||||
version.h - Version defines.
|
||||
*/
|
||||
|
||||
#define PVERS L"1.67" // wide string
|
||||
#define PVERSA "1.67" // ANSI string (windres 2.16.91 didn't like L)
|
||||
#define PVERE L"167" // wide environment string
|
||||
#define PVEREA "167" // ANSI environment string
|
||||
#define PVERB 1,6,7,0 // binary (resource)
|
||||
#define PVERS L"1.70" // wide string
|
||||
#define PVERSA "1.70" // ANSI string (windres 2.16.91 didn't like L)
|
||||
#define PVERE L"170" // wide environment string
|
||||
#define PVEREA "170" // ANSI environment string
|
||||
#define PVERB 1,7,0,0 // binary (resource)
|
||||
|
||||
#ifdef _WIN64
|
||||
# define BITS L"64"
|
||||
|
88
wow64.h
88
wow64.h
@ -1,88 +0,0 @@
|
||||
/*
|
||||
wow64.h - Definitions for Wow64.
|
||||
|
||||
The 2003 Platform SDK does not include these Wow64 definitions.
|
||||
*/
|
||||
|
||||
#ifndef WOW64_H
|
||||
#define WOW64_H
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#define WOW64_CONTEXT_i386 0x00010000
|
||||
|
||||
#define WOW64_CONTEXT_CONTROL (WOW64_CONTEXT_i386 | 0x00000001L)
|
||||
#define WOW64_CONTEXT_INTEGER (WOW64_CONTEXT_i386 | 0x00000002L)
|
||||
#define WOW64_CONTEXT_SEGMENTS (WOW64_CONTEXT_i386 | 0x00000004L)
|
||||
#define WOW64_CONTEXT_FLOATING_POINT (WOW64_CONTEXT_i386 | 0x00000008L)
|
||||
#define WOW64_CONTEXT_DEBUG_REGISTERS (WOW64_CONTEXT_i386 | 0x00000010L)
|
||||
#define WOW64_CONTEXT_EXTENDED_REGISTERS (WOW64_CONTEXT_i386 | 0x00000020L)
|
||||
|
||||
#define WOW64_CONTEXT_FULL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS)
|
||||
|
||||
#define WOW64_CONTEXT_ALL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS | \
|
||||
WOW64_CONTEXT_FLOATING_POINT | WOW64_CONTEXT_DEBUG_REGISTERS | \
|
||||
WOW64_CONTEXT_EXTENDED_REGISTERS)
|
||||
|
||||
#define WOW64_SIZE_OF_80387_REGISTERS 80
|
||||
|
||||
#define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512
|
||||
|
||||
typedef struct _WOW64_FLOATING_SAVE_AREA {
|
||||
DWORD ControlWord;
|
||||
DWORD StatusWord;
|
||||
DWORD TagWord;
|
||||
DWORD ErrorOffset;
|
||||
DWORD ErrorSelector;
|
||||
DWORD DataOffset;
|
||||
DWORD DataSelector;
|
||||
BYTE RegisterArea[WOW64_SIZE_OF_80387_REGISTERS];
|
||||
DWORD Cr0NpxState;
|
||||
} WOW64_FLOATING_SAVE_AREA;
|
||||
|
||||
typedef WOW64_FLOATING_SAVE_AREA *PWOW64_FLOATING_SAVE_AREA;
|
||||
|
||||
typedef struct _WOW64_CONTEXT {
|
||||
|
||||
DWORD ContextFlags;
|
||||
|
||||
DWORD Dr0;
|
||||
DWORD Dr1;
|
||||
DWORD Dr2;
|
||||
DWORD Dr3;
|
||||
DWORD Dr6;
|
||||
DWORD Dr7;
|
||||
|
||||
WOW64_FLOATING_SAVE_AREA FloatSave;
|
||||
|
||||
DWORD SegGs;
|
||||
DWORD SegFs;
|
||||
DWORD SegEs;
|
||||
DWORD SegDs;
|
||||
|
||||
DWORD Edi;
|
||||
DWORD Esi;
|
||||
DWORD Ebx;
|
||||
DWORD Edx;
|
||||
DWORD Ecx;
|
||||
DWORD Eax;
|
||||
|
||||
DWORD Ebp;
|
||||
DWORD Eip;
|
||||
DWORD SegCs;
|
||||
DWORD EFlags;
|
||||
DWORD Esp;
|
||||
DWORD SegSs;
|
||||
|
||||
BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION];
|
||||
|
||||
} WOW64_CONTEXT;
|
||||
|
||||
typedef WOW64_CONTEXT *PWOW64_CONTEXT;
|
||||
|
||||
|
||||
typedef BOOL (WINAPI *TWow64GetThreadContext)( HANDLE hThread, PWOW64_CONTEXT lpContext );
|
||||
typedef BOOL (WINAPI *TWow64SetThreadContext)( HANDLE hThread, CONST WOW64_CONTEXT *lpContext );
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user