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:
Jason Hood 2014-02-05 00:21:42 +10:00
parent bccf933c0a
commit dc7569dc26
15 changed files with 767 additions and 882 deletions

101
ANSI.c
View File

@ -108,13 +108,15 @@
v1.65, 28 August, 2013: v1.65, 28 August, 2013:
fix \e[K (was using window, not buffer). 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. 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; don't hook ourself from LoadLibrary or LoadLibraryEx;
update the LoadLibraryEx flags that should not cause hooking; update the LoadLibraryEx flags that should not cause hooking;
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" #include "ansicon.h"
@ -135,6 +137,7 @@
// ========== Global variables and constants // ========== Global variables and constants
HANDLE hConOut; // handle to CONOUT$ HANDLE hConOut; // handle to CONOUT$
WORD orgattr; // original attribute
#define ESC '\x1B' // ESCape character #define ESC '\x1B' // ESCape character
#define BEL '\x07' #define BEL '\x07'
@ -253,11 +256,6 @@ SHARED DWORD s_flag;
#define GRM_INIT 1 #define GRM_INIT 1
#define GRM_EXIT 2 #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. // Wait for the child process to finish, then update our GRM to the child's.
DWORD WINAPI UpdateGRM( LPVOID child_pi ) DWORD WINAPI UpdateGRM( LPVOID child_pi )
@ -470,6 +468,7 @@ void InterpretEscSeq( void )
case 7: grm.rvideo = 1; break; case 7: grm.rvideo = 1; break;
case 8: grm.concealed = 1; break; case 8: grm.concealed = 1; break;
case 21: // oops, this actually turns on double underline 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 22: grm.bold = 0; break;
case 25: case 25:
case 24: grm.underline = 0; break; case 24: grm.underline = 0; break;
@ -1049,9 +1048,7 @@ BOOL HookAPIOneMod(
// We now have a valid pointer to the module's PE header. // We now have a valid pointer to the module's PE header.
// Get a pointer to its imports section. // Get a pointer to its imports section.
pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR, pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR,
pNTHeader->OptionalHeader. pNTHeader->IMPORTDIR.VirtualAddress );
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress );
// Bail out if the RVA of the imports section is 0 (it doesn't exist) // Bail out if the RVA of the imports section is 0 (it doesn't exist)
if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader) if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader)
@ -1113,35 +1110,18 @@ BOOL HookAPIOneMod(
} }
if (patch) if (patch)
{ {
DWORD flOldProtect, flNewProtect, flDummy; DWORD pr;
MEMORY_BASIC_INFORMATION mbi;
DEBUGSTR( 3, L" %S", hook->name ); DEBUGSTR( 3, L" %S", hook->name );
// Get the current protection attributes. // Change the access protection on the region of committed pages in
VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) ); // the virtual address space of the current process.
// Take the access protection flags. VirtualProtect( &pThunk->u1.Function, PTRSZ, PAGE_READWRITE, &pr );
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 );
// Overwrite the original address with the address of the new function. // Overwrite the original address with the address of the new function.
if (!WriteProcessMemory( GetCurrentProcess(), pThunk->u1.Function = (DWORD_PTR)patch;
&pThunk->u1.Function,
&patch, sizeof(patch), NULL ))
{
DEBUGSTR( 1, L"Could not patch!" );
return FALSE;
}
// Put the page attributes back the way they were. // Put the page attributes back the way they were.
VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), VirtualProtect( &pThunk->u1.Function, PTRSZ, pr, &pr );
flOldProtect, &flDummy );
} }
} }
pThunk++; // Advance to next imported function address pThunk++; // Advance to next imported function address
@ -1212,10 +1192,11 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) BOOL wide, LPCVOID lpApp, LPCVOID lpCmd )
{ {
int type; int type;
PBYTE base;
BOOL gui; BOOL gui;
type = ProcessType( child_pi, &gui ); type = ProcessType( child_pi, &base, &gui );
if (gui) if (gui && type > 0)
{ {
TCHAR app[MAX_PATH]; TCHAR app[MAX_PATH];
LPTSTR name; LPTSTR name;
@ -1273,20 +1254,20 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
type = 0; type = 0;
} }
} }
if (type != 0) if (type > 0)
{ {
#ifdef _WIN64 #ifdef _WIN64
if (type == 32) if (type == 32)
{ {
hDllNameType[0] = '3'; ansi_bits[0] = '3';
hDllNameType[1] = '2'; ansi_bits[1] = '2';
InjectDLL32( child_pi, hDllName ); InjectDLL32( child_pi, base );
} }
else else
{ {
hDllNameType[0] = '6'; ansi_bits[0] = '6';
hDllNameType[1] = '4'; ansi_bits[1] = '4';
InjectDLL64( child_pi, hDllName ); InjectDLL( child_pi, base );
} }
#else #else
#ifdef W32ON64 #ifdef W32ON64
@ -1313,7 +1294,7 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
} }
else else
#endif #endif
InjectDLL32( child_pi, hDllName ); InjectDLL( child_pi, base );
#endif #endif
if (!gui && !(dwCreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS))) if (!gui && !(dwCreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)))
{ {
@ -1370,7 +1351,7 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName,
&child_pi )) &child_pi ))
return FALSE; return FALSE;
DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", \"%S\"", DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", %S",
child_pi.dwProcessId, child_pi.dwProcessId,
(lpApplicationName == NULL) ? "" : lpApplicationName, (lpApplicationName == NULL) ? "" : lpApplicationName,
(lpCommandLine == NULL) ? "" : lpCommandLine ); (lpCommandLine == NULL) ? "" : lpCommandLine );
@ -1406,7 +1387,7 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName,
&child_pi )) &child_pi ))
return FALSE; return FALSE;
DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", \"%s\"", DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", %s",
child_pi.dwProcessId, child_pi.dwProcessId,
(lpApplicationName == NULL) ? L"" : lpApplicationName, (lpApplicationName == NULL) ? L"" : lpApplicationName,
(lpCommandLine == NULL) ? L"" : lpCommandLine ); (lpCommandLine == NULL) ? L"" : lpCommandLine );
@ -1609,7 +1590,6 @@ WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer,
return WriteConsoleA( hCon, lpBuffer, nNumberOfCharsToWrite, return WriteConsoleA( hCon, lpBuffer, nNumberOfCharsToWrite,
lpNumberOfCharsWritten, lpReserved ); lpNumberOfCharsWritten, lpReserved );
} }
BOOL BOOL
@ -1774,6 +1754,7 @@ void OriginalAttr( void )
NULL, OPEN_EXISTING, 0, 0 ); NULL, OPEN_EXISTING, 0, 0 );
if (!GetConsoleScreenBufferInfo( hConOut, &csbi )) if (!GetConsoleScreenBufferInfo( hConOut, &csbi ))
csbi.wAttributes = 7; csbi.wAttributes = 7;
orgattr = csbi.wAttributes;
CloseHandle( hConOut ); CloseHandle( hConOut );
if (s_flag == GRM_INIT && s_pid == GetCurrentProcessId()) if (s_flag == GRM_INIT && s_pid == GetCurrentProcessId())
@ -1823,7 +1804,8 @@ void OriginalAttr( void )
// and terminated. // 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 WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
{ {
BOOL bResult = TRUE; BOOL bResult = TRUE;
@ -1840,19 +1822,19 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
hDllNameType = hDllName - 6 + hDllNameType = hDllName - 6 +
#endif #endif
GetModuleFileName( hInstance, hDllName, lenof(hDllName) ); GetModuleFileName( hInstance, hDllName, lenof(hDllName) );
#ifdef _WIN64
ansi_bits = (LPSTR)hDllNameType;
#endif
set_ansi_dll( hDllName );
hDllInstance = hInstance; // save Dll instance handle hDllInstance = hInstance; // save Dll instance handle
DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance );
if (LLW32r == 0)
{
if (!get_LLW32r())
return FALSE;
#ifdef _WIN64 #ifdef _WIN64
if (!get_LLW64r()) DEBUGSTR( 1, L"hDllInstance = %.8X_%.8X",
return FALSE; (DWORD)((DWORD_PTR)hDllInstance >> 32),
PtrToUint( hDllInstance ) );
#else
DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance );
#endif #endif
}
// Get the entry points to the original functions. // Get the entry points to the original functions.
hKernel = GetModuleHandleA( APIKernel ); hKernel = GetModuleHandleA( APIKernel );
@ -1869,6 +1851,11 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
{ {
DEBUGSTR( 1, L"Unloading" ); DEBUGSTR( 1, L"Unloading" );
HookAPIAllMod( Hooks, TRUE ); 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 else
{ {

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages warranty. In no event will the author be held liable for any damages

115
LLW.c Normal file
View 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

302
ansicon.c
View File

@ -76,17 +76,30 @@
v1.63, 25 July, 2013: v1.63, 25 July, 2013:
don't write the reset sequence if output is redirected. don't write the reset sequence if output is redirected.
v1.70, 31 January to 3 February, 2014:
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 "ansicon.h"
#include "version.h" #include "version.h"
#include <tlhelp32.h> #include <tlhelp32.h>
#include <ctype.h> #include <ctype.h>
#include <fcntl.h>
#include <io.h> #include <io.h>
#include <locale.h> #include <locale.h>
#ifndef _O_U16TEXT
#define _O_U16TEXT 0x20000
#endif
#ifdef __MINGW32__ #ifdef __MINGW32__
int _CRT_glob = 0; int _CRT_glob = 0;
#endif #endif
@ -99,7 +112,7 @@ int _CRT_glob = 0;
void help( void ); void help( void );
void display( LPCTSTR, BOOL ); void display( LPCTSTR, BOOL );
void print_error( LPCTSTR, ... ); void print_error( LPCTSTR );
LPTSTR skip_spaces( LPTSTR ); LPTSTR skip_spaces( LPTSTR );
void get_arg( LPTSTR, LPTSTR*, LPTSTR* ); void get_arg( LPTSTR, LPTSTR*, LPTSTR* );
void get_file( 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 ); BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR );
// The DLL shares this variable, so injection requires it here. static HANDLE hConOut;
DWORD LLW32r; static WORD wAttr;
#ifdef _WIN64
DWORD LLW64r; void get_original_attr( void )
#endif {
extern LPVOID kernel32_base; 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. // Find the name of the DLL and inject it.
@ -124,11 +174,13 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
DWORD len; DWORD len;
WCHAR dll[MAX_PATH]; WCHAR dll[MAX_PATH];
int type; int type;
PBYTE base;
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId );
type = ProcessType( ppi, gui ); type = ProcessType( ppi, &base, gui );
if (type == 0) if (type <= 0)
{ {
if (type == 0)
fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app ); fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app );
return FALSE; return FALSE;
} }
@ -137,41 +189,94 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
memcpy( dll, prog_path, TSIZE(len) ); memcpy( dll, prog_path, TSIZE(len) );
#ifdef _WIN64 #ifdef _WIN64
wsprintf( dll + len, L"ANSI%d.dll", type ); wsprintf( dll + len, L"ANSI%d.dll", type );
ansi_bits = (LPSTR)(dll + len + 4);
set_ansi_dll( dll );
if (type == 32) if (type == 32)
{ InjectDLL32( ppi, base );
get_LLW32r();
InjectDLL32( ppi, dll );
}
else else
{ InjectDLL( ppi, base );
get_LLW64r();
InjectDLL64( ppi, dll );
}
#else #else
wcscpy( dll + len, L"ANSI32.dll" ); wcscpy( dll + len, L"ANSI32.dll" );
get_LLW32r(); set_ansi_dll( dll );
InjectDLL32( ppi, dll ); InjectDLL( ppi, base );
#endif #endif
return TRUE; return TRUE;
} }
static HANDLE hConOut; // Use CreateRemoteThread to load our DLL in the target process.
static CONSOLE_SCREEN_BUFFER_INFO csbi; void RemoteLoad( LPPROCESS_INFORMATION ppi )
void get_original_attr( void )
{ {
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, HANDLE hSnap;
FILE_SHARE_READ | FILE_SHARE_WRITE, MODULEENTRY32 me;
NULL, OPEN_EXISTING, 0, 0 ); PBYTE LLW;
GetConsoleScreenBufferInfo( hConOut, &csbi ); 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;
} }
len = (DWORD)(prog - prog_path);
void set_original_attr( void ) memcpy( dll, prog_path, TSIZE(len) );
#ifdef _WIN64
if (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64)
{ {
SetConsoleTextAttribute( hConOut, csbi.wAttributes ); wcscpy( dll + len, L"ANSI32.dll" );
CloseHandle( hConOut ); 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; STARTUPINFO si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
LPTSTR argv, arg, cmd; LPTSTR argv, arg, cmd;
TCHAR logstr[4]; TCHAR buf[4];
BOOL installed;
BOOL shell, run, gui; BOOL shell, run, gui;
HMODULE ansi; HMODULE ansi;
DWORD len; DWORD len;
int rc = 0; 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(); argv = GetCommandLine();
len = (DWORD)wcslen( argv ) + 1; len = (DWORD)wcslen( argv ) + 1;
if (len < MAX_PATH) if (len < MAX_PATH)
@ -217,13 +334,9 @@ int main( void )
} }
prog = get_program_name( NULL ); prog = get_program_name( NULL );
*logstr = '\0'; *buf = '\0';
GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) );
log_level = _wtoi( logstr ); log_level = _wtoi( buf );
// Using "" for setlocale uses the system ANSI code page.
sprintf( (LPSTR)logstr, ".%u", GetConsoleOutputCP() );
setlocale( LC_CTYPE, (LPSTR)logstr );
#ifdef _WIN64 #ifdef _WIN64
if (*arg == '-' && arg[1] == 'P') if (*arg == '-' && arg[1] == 'P')
@ -241,19 +354,7 @@ int main( void )
if (log_level) if (log_level)
DEBUGSTR( 1, NULL ); // start a new session 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; shell = run = TRUE;
get_original_attr();
while (*arg == '-') while (*arg == '-')
{ {
@ -277,40 +378,12 @@ int main( void )
case 'p': case 'p':
shell = FALSE; shell = FALSE;
// If it's already installed, there's no need to do anything. if (GetParentProcessInfo( &pi, arg + 3 ))
if (installed)
{ {
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.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId );
SuspendThread( pi.hThread ); SuspendThread( pi.hThread );
// Find the base address of kernel32.dll. RemoteLoad( &pi );
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;
ResumeThread( pi.hThread ); ResumeThread( pi.hThread );
CloseHandle( pi.hThread ); CloseHandle( pi.hThread );
CloseHandle( pi.hProcess ); 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 (run)
{ {
if (*cmd == '\0') if (*cmd == '\0')
@ -392,7 +474,7 @@ arg_out:
} }
else else
{ {
print_error( arg, arg ); print_error( arg );
rc = 1; rc = 1;
} }
} }
@ -404,8 +486,7 @@ arg_out:
print_error( ANSIDLL ); print_error( ANSIDLL );
rc = 1; rc = 1;
} }
else if (*arg == 'e' || *arg == 'E')
if (*arg == 'e' || *arg == 'E')
{ {
cmd += 2; cmd += 2;
if (*cmd == ' ' || *cmd == '\t') if (*cmd == ' ' || *cmd == '\t')
@ -433,14 +514,10 @@ arg_out:
get_file( arg, &argv, &cmd ); get_file( arg, &argv, &cmd );
} while (*arg); } while (*arg);
} }
FreeLibrary( ansi ); FreeLibrary( ansi );
} }
if (run || *arg)
set_original_attr(); set_original_attr();
else
CloseHandle( hConOut );
return rc; 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; LPTSTR errmsg = NULL;
DWORD err = GetLastError(); DWORD err = GetLastError();
va_list arg;
fputws( L"ANSICON: ", stderr );
if (err == ERROR_BAD_EXE_FORMAT) if (err == ERROR_BAD_EXE_FORMAT)
{ {
// This error requires an argument, which is a duplicate of name. // This error requires an argument, which is name.
va_start( arg, name ); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, FORMAT_MESSAGE_ALLOCATE_BUFFER |
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, &arg ); FORMAT_MESSAGE_ARGUMENT_ARRAY,
va_end( arg ); NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, (va_list*)&name );
fwprintf( stderr, L"ANSICON: %s", errmsg ); fputws( errmsg, stderr );
} }
else else
{ {
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, if (FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ); FORMAT_MESSAGE_ALLOCATE_BUFFER |
// Just in case there are other messages requiring args... FORMAT_MESSAGE_IGNORE_INSERTS,
if (errmsg == NULL) NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ))
fwprintf( stderr, L"ANSICON: %s: Error %lu.\n", name, err ); fwprintf( stderr, L"%s: %s", name, errmsg );
else else
fwprintf( stderr, L"ANSICON: %s: %s", name, errmsg ); fwprintf( stderr, L"%s: Error %lu.\n", name, err );
} }
LocalFree( errmsg ); LocalFree( errmsg );
} }
@ -759,7 +836,7 @@ void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
for (path = name = arg; *path != '\0'; ++path) for (path = name = arg; *path != '\0'; ++path)
if (*path == '\\' || *path == '/') if (*path == '\\' || *path == '/')
name = path + 1; name = path + 1;
glob = malloc( (globbed + 1) * sizeof(LPTSTR) + TSIZE(size) ); glob = malloc( (globbed + 1) * PTRSZ + TSIZE(size) );
path = (LPTSTR)(glob + globbed + 1); path = (LPTSTR)(glob + globbed + 1);
globbed = 0; globbed = 0;
fh = FindFirstFile( arg, &fd ); fh = FindFirstFile( arg, &fd );
@ -789,7 +866,7 @@ void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
FindClose( fh ); FindClose( fh );
glob[globbed] = NULL; glob[globbed] = NULL;
qsort( glob, globbed, sizeof(LPTSTR), glob_sort ); qsort( glob, globbed, PTRSZ, glob_sort );
wcscpy( name, glob[0] ); wcscpy( name, glob[0] );
globbed = 1; 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 ) void help( void )
{ {
_putws( _putws(
@ -806,11 +890,7 @@ L"ANSICON by Jason Hood <jadoxa@yahoo.com.au>.\n"
L"Version " PVERS L" (" PDATE L"). Freeware.\n" L"Version " PVERS L" (" PDATE L"). Freeware.\n"
L"http://ansicon.adoxa.vze.com/\n" L"http://ansicon.adoxa.vze.com/\n"
L"\n" L"\n"
#ifdef _WIN64 L"Process ANSI escape sequences in " WINTYPE L" console programs.\n"
L"Process ANSI escape sequences in Windows console programs.\n"
#else
L"Process ANSI escape sequences in Win32 console programs.\n"
#endif
L"\n" L"\n"
L"ansicon [-l<level>] [-i] [-I] [-u] [-U] [-m[<attr>]] [-p]\n" L"ansicon [-l<level>] [-i] [-I] [-u] [-U] [-m[<attr>]] [-p]\n"
L" [-e|E string | -t|T [file(s)] | program [args]]\n" L" [-e|E string | -t|T [file(s)] | program [args]]\n"

View File

@ -31,6 +31,22 @@
#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x20 #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x20
#endif #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 typedef struct
{ {
@ -44,16 +60,23 @@ typedef struct
} GRM, *PGRM; // Graphic Rendition Mode } GRM, *PGRM; // Graphic Rendition Mode
int ProcessType( LPPROCESS_INFORMATION, BOOL* ); int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* );
void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR );
void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR ); void InjectDLL( LPPROCESS_INFORMATION, PBYTE );
BOOL get_LLW32r( void ); void InjectDLL32( LPPROCESS_INFORMATION, PBYTE );
BOOL get_LLW64r( void );
DWORD get_LLW32r( void );
DWORD64 get_LLW64r( void );
extern TCHAR prog_path[MAX_PATH]; extern TCHAR prog_path[MAX_PATH];
extern LPTSTR prog; extern LPTSTR prog;
LPTSTR get_program_name( LPTSTR ); LPTSTR get_program_name( LPTSTR );
extern char ansi_dll[MAX_PATH];
extern DWORD ansi_len;
extern char* ansi_bits;
void set_ansi_dll( LPTSTR );
extern int log_level; extern int log_level;
void DEBUGSTR( int level, LPTSTR szFormat, ... ); void DEBUGSTR( int level, LPTSTR szFormat, ... );

202
injdll.c Normal file
View 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

View File

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

View File

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

View File

@ -16,13 +16,16 @@
# #
# Tested with: # Tested with:
# * MinGW/gcc 4.7.2; # * MinGW/gcc 4.7.2;
# * tdm-gcc-4.7.1-2; # * tdm-gcc-4.8.1-3;
# * tdm64-gcc-4.7.1-3; # * tdm64-gcc-4.8.1-3;
# * MinGW-builds x64-4.8.1-release-posix-seh-rev1. # * MinGW-builds x64-4.8.1-release-posix-seh-rev1.
CC = gcc CC = gcc
CFLAGS = -O2 -Wall 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 = 32
#ARCH = 64 #ARCH = 64
#ARCH = multi #ARCH = multi
@ -41,8 +44,8 @@ endif
endif endif
endif endif
X86OBJS = x86/proctype.o x86/injdll32.o x86/util.o X86OBJS = x86/proctype.o x86/injdll.o x86/util.o
X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/util.o X64OBJS = x64/proctype.o x64/injdll.o x64/util.o
# Determine the appropriate separator to run multiple commands - ";" for sh.exe # Determine the appropriate separator to run multiple commands - ";" for sh.exe
# and "&" for CMD.EXE. $(SHELL) is initially defined to "sh.exe" - if it # and "&" for CMD.EXE. $(SHELL) is initially defined to "sh.exe" - if it
@ -67,7 +70,7 @@ x86/%v.o: %.rc version.h
$(RCmsg)windres -U _WIN64 -F pe-i386 $< $@ $(RCmsg)windres -U _WIN64 -F pe-i386 $< $@
x64/%.o: %.c ansicon.h x64/%.o: %.c ansicon.h
$(CCmsg)$(CC) -m64 -c $(CFLAGS) $< -o $@ $(CCmsg)$(CC) -m64 -g -c $(CFLAGS) $< -o $@
x64/%v.o: %.rc version.h x64/%v.o: %.rc version.h
$(RCmsg)windres -F pe-x86-64 $< $@ $(RCmsg)windres -F pe-x86-64 $< $@
@ -89,8 +92,8 @@ ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll
x86: x86:
cmd /c "mkdir x86" cmd /c "mkdir x86"
x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/LLW.o x86/ansiconv.o
$(LDmsg)$(CC) -m32 $+ -s -o $@ $(LDmsg)$(CC) -m32 $+ -s -o $@ $(IVER)
x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o
$(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC0000 $(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: x64:
cmd /c "mkdir x64" cmd /c "mkdir x64"
x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/LLW.o x64/ansiconv.o
$(LDmsg)$(CC) -m64 $+ -s -o $@ $(LDmsg)$(CC) -m64 $+ -s -o $@ $(IVER)
x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o
$(LDmsg)$(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC000000 $(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 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 $(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll \
-Wl,-shared,--image-base,0xAC0000,--large-address-aware
x86/ansicon.o: version.h x86/ansicon.o: version.h
x86/ANSI.o: version.h x86/ANSI.o: version.h

View File

@ -62,8 +62,11 @@ MT = mt.exe
CFLAGS = /nologo /W3 /O2 $(SHARE) /D_CRT_SECURE_NO_WARNINGS CFLAGS = /nologo /W3 /O2 $(SHARE) /D_CRT_SECURE_NO_WARNINGS
LIBS = advapi32.lib user32.lib $(LIBS64) LIBS = advapi32.lib user32.lib $(LIBS64)
X86OBJS = x86\proctype.obj x86\injdll32.obj x86\util.obj # Identify ansicon.exe using "ANSI" as a version number.
X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj 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) !IF !DEFINED(V)
V = 0 V = 0
@ -90,15 +93,16 @@ ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll
x86: x86:
mkdir x86 mkdir x86
x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\LLW.obj x86\ansicon.res
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) /link /filealign:512 $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512
!IF "$(_NMAKE_VER)" == "9.00.30729.01" !IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1 $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1
@del $@.manifest @del $@.manifest
!ENDIF !ENDIF
x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res 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" !IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
@del $@.manifest @del $@.manifest
@ -107,14 +111,16 @@ x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res
x64: x64:
mkdir x64 mkdir x64
x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\LLW.obj x64\ansicon.res
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK)
x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res
$(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 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 $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \
/base:0xAC0000 /section:.shared,s /filealign:512 /largeaddressaware
!IF "$(_NMAKE_VER)" == "9.00.30729.01" !IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
@del $@.manifest @del $@.manifest
@ -125,12 +131,13 @@ ansicon.rc: version.h
ANSI.c: ansicon.h version.h ANSI.c: ansicon.h version.h
ANSI.rc: version.h ANSI.rc: version.h
util.c: ansicon.h version.h util.c: ansicon.h version.h
injdll32.c: ansicon.h injdll.c: ansicon.h
injdll64.c: ansicon.h
proctype.c: ansicon.h proctype.c: ansicon.h
LLW.c: ansicon.h
x64\ANSI32.obj: ANSI.c x64\ANSI32.obj: ANSI.c
$(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?
x64\proctype32.obj: proctype.c x64\proctype32.obj: proctype.c
$(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?

View File

@ -1,76 +1,76 @@
/* /*
Test for a valid process. This may sometimes detect GUI, even for a console Test for a valid process (i386 for x86; that or AMD64 for x64). We can get
process. I think this is due to a DLL being loaded in the address space that info from the image header, which means getting the process's base
before the main image. Ideally I could just use the base address directly, address (which we need anyway, to modify the imports). The simplest way to
but that doesn't seem easy to do for another process - there doesn't seem to do that is to enumerate the pages, looking for an executable image.
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.
*/ */
#include "ansicon.h" #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; MEMORY_BASIC_INFORMATION minfo;
IMAGE_DOS_HEADER dos_header; IMAGE_DOS_HEADER dos_header;
IMAGE_NT_HEADERS nt_header; IMAGE_NT_HEADERS nt_header;
SIZE_T read;
*pBase = NULL;
*gui = FALSE; *gui = FALSE;
for (ptr = NULL; for (ptr = NULL;
VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) ); VirtualQueryEx( ppi->hProcess, ptr, &minfo, sizeof(minfo) );
ptr += minfo.RegionSize) ptr += minfo.RegionSize)
{ {
if (minfo.BaseAddress == minfo.AllocationBase && if (minfo.BaseAddress == minfo.AllocationBase
ReadProcessMemory( pinfo->hProcess, minfo.AllocationBase, && ReadProcVar( minfo.BaseAddress, &dos_header )
&dos_header, sizeof(dos_header), &read )) && 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 &&
if (ReadProcessMemory( pinfo->hProcess, (char*)minfo.AllocationBase + nt_header.OptionalHeader.MinorImageVersion == 18771)
dos_header.e_lfanew, &nt_header, return -1;
sizeof(nt_header), &read ))
{ *pBase = minfo.BaseAddress;
if (nt_header.Signature == IMAGE_NT_SIGNATURE && if (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)
(nt_header.FileHeader.Characteristics & *gui = TRUE;
(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) if (*gui ||
== IMAGE_FILE_EXECUTABLE_IMAGE) nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
{
*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) if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
{ {
// Microsoft ignores precision on %p.
DEBUGSTR( 1, L" 32-bit %s (base = %.8X)", DEBUGSTR( 1, L" 32-bit %s (base = %.8X)",
(*gui) ? L"GUI" : L"console", (*gui) ? L"GUI" : L"console",
(DWORD)(DWORD_PTR)minfo.AllocationBase ); PtrToUint( minfo.BaseAddress ) );
return 32; return 32;
} }
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
{
#ifdef _WIN64 #ifdef _WIN64
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) DEBUGSTR( 1, L" 64-bit %s (base = %.8X_%.8X)",
{ (*gui) ? L"GUI" : L"console",
DEBUGSTR( 1, L" 64-bit %s (base = %p)", (DWORD)((DWORD_PTR)minfo.BaseAddress >> 32),
(*gui) ? L"GUI" : L"console", minfo.AllocationBase ); PtrToUint( minfo.BaseAddress ) );
return 64; return 64;
}
#elif defined(W32ON64) #elif defined(W32ON64)
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) // Console will log due to -P, but GUI may be ignored (if not,
{ // this'll show up twice).
DEBUGSTR( 1, L" 64-bit %s", if (*gui)
(*gui) ? L"GUI" : L"console" ); DEBUGSTR( 1, L" 64-bit GUI (base = 00000000_%.8X)",
PtrToUint( minfo.BaseAddress ) );
return 64; 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 #endif
}
DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)", DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)",
nt_header.FileHeader.Machine ); nt_header.FileHeader.Machine );
} }
@ -81,19 +81,16 @@ int ProcessType( LPPROCESS_INFORMATION pinfo, BOOL* gui )
} }
return 0; return 0;
} }
}
}
}
#ifndef _WIN64 #ifndef _WIN64
// If a 32-bit process loads a 64-bit one, we may miss the base // If a 32-bit process loads a 64-bit one, we may miss the base
// address. If the pointer overflows, assume 64-bit. // 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 #ifdef W32ON64
DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit console" ); DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit" );
return 64; return 64;
#else #else
DEBUGSTR( 1, L" Ignoring apparent 64-bit process" ); DEBUGSTR( 1, L" Ignoring apparent 64-bit process (use x64\\ansicon)" );
return 0; return 0;
#endif #endif
} }

View File

@ -1,9 +1,9 @@
ANSICON ANSICON
Copyright 2005-2013 Jason Hood Copyright 2005-2014 Jason Hood
Version 1.66. Freeware Version 1.70. Freeware
Description Description
@ -17,7 +17,7 @@ Requirements
============ ============
32-bit: Windows 2000 Professional and later (it won't work with NT or 9X). 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 Installation
@ -93,10 +93,10 @@ Usage
16 Log all imported modules (add to any of the above) 16 Log all imported modules (add to any of the above)
The log option will not work with '-p'; set the environment variable 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 ANSICON_LOG (to the number) instead. The variable is only read once when a
started; changing it won't affect running processes. If you identify a process is started; changing it won't affect running processes. If you
module that causes problems, add it to the ANSICON_EXC environment variable identify a module that causes problems, add it to the ANSICON_EXC environ-
(see ANSICON_API below, but the extension is required). ment variable (see ANSICON_API below, but the extension is required).
E.g.: 'ansicon -l5' will start a new command processor, logging every pro- E.g.: 'ansicon -l5' will start a new command processor, logging every pro-
cess it starts along with their output. cess it starts along with their output.
@ -264,12 +264,26 @@ Limitations
ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll
An application using multiple screen buffers will not have separate
attributes in each buffer.
Version History Version History
=============== ===============
Legend: + added, - bug-fixed, * changed. 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: 1.66 - 20 September, 2013:
- fix 32-bit process trying to detect 64-bit process. - fix 32-bit process trying to detect 64-bit process.
@ -442,12 +456,6 @@ Contact
http://ansicon.adoxa.vze.com/ http://ansicon.adoxa.vze.com/
https://github.com/adoxa/ansicon https://github.com/adoxa/ansicon
Jason Hood
11 Buckle Street
North Rockhampton
Qld 4701
Australia
Distribution Distribution
============ ============
@ -461,5 +469,5 @@ Distribution
in LICENSE.txt. in LICENSE.txt.
=============================== =============================
Jason Hood, 20 September, 2013. Jason Hood, 4 February, 2014.

50
util.c
View File

@ -8,9 +8,14 @@
TCHAR prog_path[MAX_PATH]; TCHAR prog_path[MAX_PATH];
LPTSTR prog; LPTSTR prog;
int log_level; 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". // Get just the name of the program: "C:\path\program.exe" -> "program".
@ -37,8 +42,46 @@ 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, ... ) void DEBUGSTR( int level, LPTSTR szFormat, ... )
{ {
static char tempfile[MAX_PATH];
static DWORD pid;
TCHAR szBuffer[1024], szEscape[1024]; TCHAR szBuffer[1024], szEscape[1024];
va_list pArgList; va_list pArgList;
HANDLE mutex; HANDLE mutex;
@ -55,6 +98,7 @@ void DEBUGSTR( int level, LPTSTR szFormat, ... )
} }
if (szFormat == NULL) if (szFormat == NULL)
{ {
// Explicitly use 't', as _fmode might be binary.
file = fopen( tempfile, (log_level & 8) ? "at" : "wt" ); file = fopen( tempfile, (log_level & 8) ? "at" : "wt" );
if (file != NULL) if (file != NULL)
{ {
@ -116,7 +160,7 @@ void DEBUGSTR( int level, LPTSTR szFormat, ... )
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
wait = WaitForSingleObject( mutex, 500 ); wait = WaitForSingleObject( mutex, 500 );
file = fopen( tempfile, "at" ); // _fmode might be binary file = fopen( tempfile, "at" );
if (file != NULL) if (file != NULL)
{ {
fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat ); fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat );

View File

@ -2,11 +2,11 @@
version.h - Version defines. version.h - Version defines.
*/ */
#define PVERS L"1.67" // wide string #define PVERS L"1.70" // wide string
#define PVERSA "1.67" // ANSI string (windres 2.16.91 didn't like L) #define PVERSA "1.70" // ANSI string (windres 2.16.91 didn't like L)
#define PVERE L"167" // wide environment string #define PVERE L"170" // wide environment string
#define PVEREA "167" // ANSI environment string #define PVEREA "170" // ANSI environment string
#define PVERB 1,6,7,0 // binary (resource) #define PVERB 1,7,0,0 // binary (resource)
#ifdef _WIN64 #ifdef _WIN64
# define BITS L"64" # define BITS L"64"

88
wow64.h
View File

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