/**
* Usage:
* "kernel32.dll", "Beep", 0, "arg_1", (FARPROC)func, __CDECL, 0 //- intercepts Beep func.
* "", "", 0x12345678, "eax,edx,ecx", (FARPROC)func, __STDCALL, 0 //- intercepts @ 0x12345678 offset of module base
* "some.dll", "", 0x12345678, "eax,edx,ecx", (FARPROC)func, __STDCALL, 0 //- intercepts @ 0x12345678 offset of some.dll base
*
*
* api_intercept.h
*
*
*
*/
#define APII_PRE_BUFFER 128
#define APII_ARG_SEPARATOR ','
#define APII_CONTINUE 0
#define APII_ERROR -1
/// Calling convetion | __cdecl or __stdcall
enum C_CONV { __CDECL, __STDCALL };
typedef struct
{
char m_name[32]; // module name or "" for executable module
char f_name[16]; // function name
DWORD offset; // function offset [IGNORED: if f_name set]
char f_arg[32]; // arg_1,eax,arg_2,ecx | selective function arguments
FARPROC function;
enum C_CONV calling_convention; // name says it all lolz | default __CDECL
FARPROC call; // set to original function call
} apii, *p_apii;
/// Helper dump_log function
#ifdef _DEBUG
void dump_log( char* txt, ... )
{
va_list va;
va_start( va, txt );
char buffer[1024];
wvsprintfA( buffer, txt, va );
OutputDebugStringA( buffer );
va_end( va );
}
#else
#define dump_log
#endif
/// helper defines
#define SAVE_EAX if ( !apii_eax ) { *p_buffer++ = 0x50; apii_eax = TRUE; };
BYTE* make_arg( BYTE* p_buffer, BYTE ss_position )
{
p_buffer -= 5;
memcpy( p_buffer, "\x8B\x44\x24", 3 );
p_buffer += 3;
*p_buffer++ = ss_position; // mov eax, dword ptr ss:[esp + ss_position]
*p_buffer = 0x50; // push eax
return p_buffer - 4;
}
/**
* Name: apii_install
* Desc: installs all hooks
*
*/
int apii_install( p_apii api, int size )
{
if ( api == NULL || !size )
return -1;
int installed = 0;
for(int i=0; i<size; i++, api++)
{
DWORD address;
/// get the module base
address = (DWORD) GetModuleHandleA( api->m_name[0] == '\0' ? NULL : api->m_name );
if ( address == NULL )
{
dump_log( "apii_install(): GetModuleHandleA() failed on \"%s\" [%i. entry]\n", api->m_name, i+1 );
continue;
}
/// get the write address
if ( api->f_name[0] != '\0' )
{
address = (DWORD) GetProcAddress( (HMODULE)address, api->f_name );
if ( address == NULL )
{
dump_log( "apii_install(): GetProcAddress() failed on \"%s\" [%i. entry]\n", api->f_name, i+1 );
continue;
}
} else
address += api->offset;
// post-write-values
DWORD* call_address, *jmp_address;
// pre_buffer
BYTE buffer[ APII_PRE_BUFFER + 1 ] = { 0x60 };
BYTE* p_buffer = buffer + 1; // real buffer
BYTE* r_buffer = buffer + APII_PRE_BUFFER; // reverse buffer | right-2-left alignement
BOOL apii_eax = FALSE; // eax saved ?
int operation= APII_CONTINUE; // operation flow | APII_CONTINUE or APII_ERROR
// count the arguments
int n_arg = 0;
for(char* p = api->f_arg; *p != '\0' ; p++ )
n_arg += *p == APII_ARG_SEPARATOR;
int t_arg = n_arg; // temp arguments
for(char* p = api->f_arg; ; p++ )
{
while( *p == ' ' ) p++; // trim spaces
if ( !memcmp( p, "this", 4) ) // this
{
r_buffer -= 5;
*r_buffer++ = 0x68;
*(DWORD*)r_buffer-- = (DWORD)api;
p += 4;
t_arg--;
} else
if ( !memcmp( p, "arg_", 4) ) // arg_[argument_#]
{
p += 4;
if ( *p >= '0' && *p <= '9' )
{
BYTE ss_position = 0x24 + 0x4 + ( *p++ - '1' ) * 0x4 + t_arg * 0x4; // stack segment position
SAVE_EAX
r_buffer = make_arg( r_buffer, ss_position );
t_arg--;
} else
{
dump_log( "apii_install(): Bad arguments: \"%s\" [%i. entry]\n", api->f_arg, i+1 );
operation = APII_ERROR;
break;
}
}else
if ( !memcmp( p, "caller", 6) )
{
SAVE_EAX
r_buffer = make_arg( r_buffer, 0x20 + 0x4 + t_arg * 0x4 ); // pushad + push eax + pushed_arguments
t_arg--;
p += 6;
} else
{
BYTE reg = 0x50; // push eax
int c_sum = *p << 16 | *(p+1) << 8 | *(p+2);
switch( c_sum )
{
case 0x00656469: // edi
reg++;
case 0x00657369: // esi
reg++;
case 0x00656270: // ebp
reg++;
case 0x00657370: // esp
reg++;
case 0x00656278: // ebx
reg++;
case 0x00656478: // edx
reg++;
case 0x00656378: // ecx
reg++;
case 0x00656178: // eax
break;
default:
dump_log( "apii_install(): Bad arguments: \"%s\" [%i. entry]\n", api->f_arg, i+1 );
operation = APII_ERROR;
break;
}
// don't stop @ failed input
if ( operation == APII_ERROR )
break;
*--r_buffer = reg;
p += 3;
t_arg--;
}
while( *p == ' ' ) p++; // trim spaces
if ( *p == '\0' )
break;
else
if ( *p != APII_ARG_SEPARATOR )
{
dump_log( "apii_install(): Bad arguments: \"%s\" [%i. entry]\n", api->f_arg, i+1 );
operation = APII_ERROR;
break;
}
}
// don't stop @ failed input
if ( operation == APII_ERROR )
continue;
// right to left - alignement
int code_size = (buffer + APII_PRE_BUFFER) - r_buffer;
memcpy( p_buffer, r_buffer, code_size );
p_buffer += code_size;
*p_buffer++ = 0xE8;
call_address = (DWORD*)p_buffer; // post-write-value
p_buffer += 4; // call apii->function
if ( api->calling_convention == __CDECL && n_arg )
{
memcpy( p_buffer, "\x83\xC4", 2 );
p_buffer[2] = (BYTE)n_arg * 4 + 4; // at least one arg must exist
p_buffer += 3;
}
*p_buffer++ = 0x58; // pop eax
*p_buffer++ = 0x61; // popad
api->call = (FARPROC)p_buffer; // for orginal call function | must post change
// rebuild first 5 bytes
memcpy( p_buffer, (LPVOID)address, 5 );
p_buffer += 5;
*p_buffer++ = 0xE9;
jmp_address = (DWORD*)p_buffer; // post-write-value
p_buffer += 4; // jmp back_to_function
// allocate memory
code_size = p_buffer - buffer;
BYTE* p_alloc = (BYTE*) VirtualAlloc( NULL, code_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE );
if ( p_alloc == NULL )
{
dump_log( "apii_install(): VirtualAlloc() failed on %i. entry\n", i+1 );
continue;
}
/// post-write
*call_address = (DWORD)api->function - ((DWORD)p_alloc + ((DWORD)call_address) - (DWORD)buffer + 0x4);
*jmp_address = ((DWORD)address + 0x5) - ((DWORD)p_alloc + ((DWORD)jmp_address) - (DWORD)buffer + 0x4);
api->call = (FARPROC)(p_alloc - ((DWORD)buffer - (DWORD)api->call));
memcpy( p_alloc, buffer, code_size );
DWORD old_protection;
if ( !VirtualProtect( (LPVOID)address, 5, PAGE_READWRITE, &old_protection ) )
{
dump_log( "apii_install(): VirtualProtect() failed on address 0x%.8x [%i. entry]\n", address, i+1 );
VirtualFree( p_alloc, 0, MEM_RELEASE );
continue;
}
*p_buffer = 0xE9;
call_address = (DWORD*)( p_buffer + 1 );
*call_address = (DWORD)p_alloc - ( (DWORD)address + 0x5 ); // jmp [to_allocation]
/// write jmp code
memcpy( (LPVOID)address, p_buffer, 5 );
VirtualProtect( (LPVOID)address, 5, old_protection, &old_protection );
installed++;
}
return installed;
}
////////////////////////////////////////
#include "api_intercept.h"
// "kernel32.dll", "Beep", 0, "arg_1", (FARPROC)func, __CDECL, 0 //- intercepts Beep func.
// "", "", 0x12345678, "eax,edx,ecx", (FARPROC)func, __STDCALL, 0 //- intercepts @ 0x12345678 offset of module base
// "some.dll", "", 0x12345678, "eax,edx,ecx", (FARPROC)func, //- intercepts @ 0x12345678 offset of some.dll base
// posible arguments:
// eax,ebx,ecx... - registers
// arg_# - arguments
// caller - caller address
// this - pointer to apii structure
typedef BOOL(WINAPI* BEEP)(DWORD,DWORD);
void test_beep(apii* api,DWORD arg1,DWORD arg2,DWORD caller_address)
{
((BEEP)api->call)(arg1,arg2); // call it once more
printf("Beep() called from: 0x%.8X\n", caller_address);
}
void WINAPI test_msg( DWORD caller_address, char* caption, char* text )
{
printf("caption: %s | text: %s\n", caption, text);
}
int main()
{
apii api[] = { "kernel32.dll", "Beep", 0, "this,arg_1,arg_2,caller", (FARPROC)test_beep, __CDECL, 0,
"user32.dll", "MessageBoxA", 0, "caller,arg_3,arg_2", (FARPROC)test_msg, __STDCALL, 0 };
apii_install( api, sizeof(api)/sizeof(api[0]) );
Beep(0x1234,1000);
MessageBoxA( NULL,"test-text", "test-caption", 0);
return 0;
}