//
demonstrates getting the
command
line of another process
//
requires PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION,
//
PROCESS_VM_WRITE, PROCESS_VM_READ to the target, or the
//
SeDebugPrivilege
//
*** You *must* remove
"/GZ"
from the debug build settings ***
//
(If you use my (felixk's) project
file
, this has already happened)
typedef const wchar_t *(__stdcall *tGCLW)( void );
const DWORD MAXINJECTSIZE = 4096;
struct Common
{
DWORD gle;
//
last error from remote thread
tGCLW pGCLW;
//
address of GetCommandLineW()
wchar_t str[2000];
//
the
command
line buffer
};
//
try to
enable
SeDebugPrivilege
void EnableDebugPriv( void );
//
inject
function
bem() into target process
bool Bugger( HANDLE h, wstring &str );
//
and this is the code we are injecting
DWORD __stdcall bem( Common *c );
int wmain( int argc, wchar_t *argv[] )
{
int i;
DWORD pid;
HANDLE h;
wstring str;
if
( argc == 1 )
{
wcout << endl << L
"usage: remthread pid [pid ...]"
<< endl;
wcout << L
"Reports the command lines of the specified processes."
<< endl;
wcout << L
"Use \"-1\" to have the program look at itself."
<< endl;
return
1;
}
EnableDebugPriv();
for
( i = 1; i < argc; ++ i )
{
wstringstream( argv[i] ) >> pid;
if
( pid == (DWORD) -1 )
pid = GetCurrentProcessId();
wcout << L
"pid "
<< pid << L
": "
;
h = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid );
if
( h != 0 )
{
if
( Bugger( h, str ) )
wcout << endl << L
" \""
<< str.c_str() << L
"\""
;
CloseHandle( h );
wcout << endl;
}
else
wcout << L
"open failed, gle = "
<< GetLastError() << endl;
}
return
0;
}
void EnableDebugPriv( void )
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if
( ! OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
{
wcout << L
"OPT() failed, gle = "
<< GetLastError() <<
L
" SeDebugPrivilege is not available."
<< endl;
return
;
}
if
( ! LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) )
{
wcout << L
"LPV() failed, gle = "
<< GetLastError() <<
L
" SeDebugPrivilege is not available."
<< endl;
CloseHandle( hToken );
return
;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if
( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) )
wcout << L
"ATP() failed, gle = "
<< GetLastError() <<
L
" SeDebugPrivilege is not available."
<< endl;
CloseHandle( hToken );
}
bool Bugger( HANDLE h, wstring &str )
{
HANDLE ht = 0;
void *p = 0;
Common *c = 0;
bool result =
false
;
DWORD rc;
HMODULE hk32 = 0;
Common localCopy;
//
allocate memory
p = VirtualAllocEx( h, 0, MAXINJECTSIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if
( p == 0 )
{
wcout << L
"VAE() failed, gle = "
<< GetLastError();
goto cleanup;
}
c = (Common *) VirtualAllocEx( h, 0, sizeof Common, MEM_COMMIT, PAGE_READWRITE );
if
( c == 0 )
{
wcout << L
"VAE() failed, gle = "
<< GetLastError();
goto cleanup;
}
//
copy
function
there
if
( ! WriteProcessMemory( h, p, &bem, MAXINJECTSIZE, 0 ) )
{
wcout << L
"WPM() failed, gle = "
<< GetLastError();
goto cleanup;
}
//
initialize data area
for
remote guy
hk32 = LoadLibrary( L
"kernel32.dll"
);
if
( hk32 == 0 )
{
wcout << L
"LL() failed, gle = "
<< GetLastError();
goto cleanup;
}
localCopy.gle = 0;
localCopy.pGCLW = (tGCLW) GetProcAddress( hk32,
"GetCommandLineW"
);
if
( localCopy.pGCLW == 0 )
{
wcout << L
"GPA() failed, gle = "
<< GetLastError();
goto cleanup;
}
FreeLibrary( hk32 );
hk32 = 0;
localCopy.str[0] = L
'\0'
;
if
( ! WriteProcessMemory( h, c, &localCopy, sizeof localCopy, 0 ) )
{
wcout << L
"WPM() failed, gle = "
<< GetLastError();
goto cleanup;
}
//
CreateRemoteThread()
ht = CreateRemoteThread( h, 0, 0, (DWORD (__stdcall *)( void *)) p, c, 0, &rc );
if
( ht == NULL )
{
wcout << L
"CRT() failed, gle = "
<< GetLastError();
goto cleanup;
}
rc = WaitForSingleObject( ht, 3000 );
switch ( rc )
{
case
WAIT_TIMEOUT:
wcout << L
"WFSO() timed out. Odd!"
;
goto cleanup;
case
WAIT_FAILED:
wcout << L
"WFSO() failed, gle = "
<< GetLastError();
goto cleanup;
case
WAIT_OBJECT_0:
//
this might just have worked, pick up the result!
if
( ! ReadProcessMemory( h, c, &localCopy, sizeof localCopy, 0 ) )
{
wcout << L
"RPM() failed, gle = "
<< GetLastError();
goto cleanup;
}
if
( localCopy.gle == 0 )
{
str = localCopy.str;
result =
true
;
wcout << L
"successfully buggered."
;
}
else
wcout << L
"Remote thread failed, gle = "
<< localCopy.gle;
break
;
default:
wcout << L
"WFSO() returned a surprise! rc = "
<<
rc << L
", gle = "
<< GetLastError();
break
;
}
cleanup:
CloseHandle( ht );
if
( p != 0 )
VirtualFreeEx( h, p, 0, MEM_RELEASE );
if
( c != 0 )
VirtualFreeEx( h, c, 0, MEM_RELEASE );
if
( hk32 != 0 )
FreeLibrary( hk32 );
return
result;
}
//
The whole shebang makes a number of assumptions:
//
-- target process is a Win32 process
//
-- kernel32.dll loaded at same address
in
each process (safe)
//
-- bem() shorter than MAXINJECTSIZE
//
-- bem() does not rely on the C
/C
++ runtime
//
--
/GZ
is _not_ used. (If it is, the compiler generates calls
//
to functions
which
are not injected into the target. Oops!
DWORD __stdcall bem( Common *c )
{
const wchar_t *src;
wchar_t *tgt, *end;
//
__asm int 3;
src = c->pGCLW();
tgt = &c->str[0];
end = &c->str[lenof( c->str ) - 1];
if
( src == 0 || tgt == 0 || end == 0 )
{
c->gle = 998;
return
0;
}
for
( ; *src != L
'\0'
&& tgt < end; ++ src, ++ tgt )
*tgt = *src;
*tgt = L
'\0'
;
c->gle = 0;
return
0;
}