 |
Cheat Engine The Official Site of Cheat Engine
|
| View previous topic :: View next topic |
| Author |
Message |
Slugsnack Grandmaster Cheater Supreme
Reputation: 71
Joined: 24 Jan 2007 Posts: 1857
|
Posted: Sun Mar 28, 2010 9:14 pm Post subject: DLL Ejection |
|
|
| I want to add an ejection feature onto my injector. Essentially what this would do is enable the user to eject a module from a remote process. I have no problem doing the ejection itself but if you just unload a module then any threads executing in that module's address space would crash on its next fetch-execute cycle with an access violation. So what I want to do is to be able to find whether a thread is executing within the context of a given module. Only when I have determined that a module is inactive should it be ejected. Creating system snapshots is not particularly helpful since it does not give this information. Anyone have any ideas ?
|
|
| Back to top |
|
 |
Dark Byte Site Admin
Reputation: 474
Joined: 09 May 2003 Posts: 25962 Location: The netherlands
|
Posted: Sun Mar 28, 2010 9:32 pm Post subject: |
|
|
get all threads
for each thread get the context and do a stackwalk to find out if it'll eventually return to the module's memory region
_________________
Tools give you results. Knowledge gives you control.
Like my help? Join me on Patreon so i can keep helping |
|
| Back to top |
|
 |
Slugsnack Grandmaster Cheater Supreme
Reputation: 71
Joined: 24 Jan 2007 Posts: 1857
|
Posted: Mon Mar 29, 2010 6:51 am Post subject: |
|
|
Well I had the same idea yesterday but apparently a thread's stack does not necessarily reside within its corresponding section in the PE. This is an example:
http://img709.imageshack.us/img709/8326/43246624.png
That thread is the one before anything is started so the 'main' one. I dumped the thread data block as you can see using Olly to read off the top/bottom of stack. As you can see, looking at the memory map, the thread's stack does not really seem to correspond to any section.
Also what do I do about the problem of system DLLs ? Let's take kernel32.dll for an example. It is unlikely a thread will be running that 'belongs' to that module yet most threads will eventually try to execute code there so that is not something you want to free.
|
|
| Back to top |
|
 |
Dark Byte Site Admin
Reputation: 474
Joined: 09 May 2003 Posts: 25962 Location: The netherlands
|
Posted: Mon Mar 29, 2010 8:58 am Post subject: |
|
|
The main thread's stack is something you shouldn't be touching anyhow.
with stacktrace I mean use the thread's ESP and EBP to traverse back (look into dbghelp's stackwalk or do it manually)
| Quote: |
Also what do I do about the problem of system DLLs ? Let's take kernel32.dll for an example. It is unlikely a thread will be running that 'belongs' to that module yet most threads will eventually try to execute code there so that is not something you want to free.
|
Since the objective is destroying the threads that belong to a module, and not destroying the module that belongs to a thread, that's no problem
I also doubt other dll's will call your injected dll, unless it has hooked some stuff. (but then you'll have to call a deinitialize routine before freeing anyhow, and don't call wait routines inside your dll hook functions)
_________________
Tools give you results. Knowledge gives you control.
Like my help? Join me on Patreon so i can keep helping |
|
| Back to top |
|
 |
Slugsnack Grandmaster Cheater Supreme
Reputation: 71
Joined: 24 Jan 2007 Posts: 1857
|
Posted: Mon Mar 29, 2010 9:40 am Post subject: |
|
|
I understand you are suggesting to use StackWalk64() and then examine the STACKFRAME64 structure returned ? I'm still unsure how this helps determine which module the thread belongs to.
The second point I raised was regarding freeing of system DLLs that do not have active threads belonging to them but are used as function libraries. Basically my injector enumerates the modules of a process and gives the option to the user to free any of them. I want to ensure that they do not free a system DLL. Freeing an injected DLL is okay because you can kill the threads ( once you find them ) then release the module but a DLL containing functions that are called by other modules would crash as soon as another module made a function call within it. That's why I was using the example of kernel32.dll
|
|
| Back to top |
|
 |
Dark Byte Site Admin
Reputation: 474
Joined: 09 May 2003 Posts: 25962 Location: The netherlands
|
Posted: Mon Mar 29, 2010 12:42 pm Post subject: |
|
|
Stackwalk gives you the return address of the current function, and if you call it again, the return address of that , and then the retrun address of that, etc...
If one of the return addresses is inside the dll, then that means the function the thread is currently in, is called from inside the dll
as for unloading of system dlls:
When your dll unloads the normal way (unload event handled and it also calls freelibrary on all dynamically loaded dll's), it will decrease the reference count of the dll's it has used. So if an injected dll made use of dbk32.dll and only that dll made use of dbk32.dll, it will also unload dbk32.dll
But if your dll made use of a libnrary that is used by other dll's (e.g gdi32.dll) and then unloads, it won't destroy gdi32.dll because the reference count is still higher than 0 (due to the load of other dll's)
Also, while talking about a dll that handles it's unload event properly: Why not let that take care of destroying it's own threads ?
_________________
Tools give you results. Knowledge gives you control.
Like my help? Join me on Patreon so i can keep helping |
|
| Back to top |
|
 |
Slugsnack Grandmaster Cheater Supreme
Reputation: 71
Joined: 24 Jan 2007 Posts: 1857
|
Posted: Mon Mar 29, 2010 6:29 pm Post subject: |
|
|
Hahaha good point about DLLs should handle killing their own threads on unload. I've added some code for a sample DLL to hopefully make it terminate properly when FreeLibrary() is called on it.
WaitForSingleObject() doesn't seem to be doing what I want so I tried GetExitCodeThread() instead. Not much luck still though. Any ideas ?
| Code: | #include "resource.h"
#include <windows.h>
#include <tchar.h>
#include <commctrl.h>
#pragma comment( lib, "comctl32.lib" )
#define JMP( frm, to ) ( ( ( int )to - ( int )frm ) - 5 )
HMODULE hModule;
HWND hwndDlgGlobal;
BOOL bSignalledTerminate = FALSE;
DWORD BoostRet = 0x00692B0C;
DWORD BoostAddr = 0x00692B06;
BYTE BoostOrig[] = { 0xD9, 0x9E, 0xF8, 0x00, 0x00, 0x00 }; // fstp dword ptr [esi+0x000000f8]
void WriteJMP( DWORD from, DWORD to ) {
DWORD flOldProtect;
VirtualProtect( ( LPVOID )from, 5, PAGE_EXECUTE_READWRITE, &flOldProtect );
*( BYTE* )from = 0xE9;
*( DWORD* )( from + 1 ) = JMP( from, to );
VirtualProtect( ( LPVOID )from, 5, flOldProtect, &flOldProtect );
}
__declspec( naked ) void BoostHook() {
__asm {
push dword ptr [esi+0x000000f8]
fstp dword ptr [esi+0x000000f8]
pop dword ptr [esi+0x000000f8]
jmp [BoostRet]
}
}
void ActivateBoost( BOOL bActivate ) {
if( bActivate )
WriteJMP( BoostAddr, ( DWORD )BoostHook );
else {
DWORD flOldProtect;
VirtualProtect( ( LPVOID )BoostAddr, sizeof BoostOrig, PAGE_EXECUTE_READWRITE, &flOldProtect );
RtlMoveMemory( ( LPVOID )BoostAddr, BoostOrig, sizeof BoostOrig );
VirtualProtect( ( LPVOID )BoostAddr, sizeof BoostOrig, flOldProtect, &flOldProtect );
}
}
INT_PTR CALLBACK DlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
switch( uMsg ) {
case WM_INITDIALOG: {
TCHAR szhWnd[15];
DWORD dwProcessId = GetCurrentProcessId();
HWND hWnd = FindWindow( _T("GameFrame"), _T("Need for Speed™ Most Wanted") );
_stprintf_s( szhWnd, _countof( szhWnd ), _T("%08X"), hWnd );
SetDlgItemText( hwndDlg, IDC_EDIT2, szhWnd );
_stprintf_s( szhWnd, _countof( szhWnd ), _T("%08X"), dwProcessId );
SetDlgItemText( hwndDlg, IDC_EDIT1, szhWnd );
RegisterHotKey( hwndDlg, 0, MOD_ALT, '1' );
hwndDlgGlobal = hwndDlg;
return TRUE;
}
case WM_HOTKEY: {
switch( wParam ) {
case 0:
SendMessage( GetDlgItem( hwndDlg, IDC_CHECK1 ), BM_CLICK, NULL, NULL );
break;
default:
return FALSE;
}
return TRUE;
}
case WM_COMMAND: {
if( HIWORD( wParam ) == BN_CLICKED ) {
switch( LOWORD( wParam ) ) {
case IDC_CHECK1:
ActivateBoost( IsDlgButtonChecked( hwndDlg, IDC_CHECK1 ) == BST_CHECKED );
break;
case IDC_BUTTON1:
SendMessage( hwndDlg, WM_CLOSE, NULL, NULL );
break;
default:
return FALSE;
}
return TRUE;
}
break;
}
case WM_CLOSE:
EndDialog( hwndDlg, 0 );
return TRUE;
}
return FALSE;
}
void main() {
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof iccex;
iccex.dwICC = ICC_STANDARD_CLASSES;
InitCommonControlsEx( &iccex );
DialogBoxParam( hModule, MAKEINTRESOURCE( IDD_DIALOG1 ), NULL, DlgProc, NULL );
if( !bSignalledTerminate )
FreeLibraryAndExitThread( hModule, 0 );
}
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) {
if( fdwReason == DLL_PROCESS_ATTACH ) {
hModule = hinstDLL;
CreateThread( NULL, NULL, ( LPTHREAD_START_ROUTINE )main, NULL, NULL, NULL );
}
else if( fdwReason == DLL_PROCESS_DETACH ) {
DWORD dwExitCode;
DWORD dwThreadId = GetWindowThreadProcessId( hwndDlgGlobal, NULL );
HANDLE hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, dwThreadId );
bSignalledTerminate = TRUE;
SendMessage( hwndDlgGlobal, WM_CLOSE, 0, 0 );
// WaitForSingleObject( hThread, INFINITE );
do {
Sleep( 100 );
GetExitCodeThread( hThread, &dwExitCode );
}
while( dwExitCode == STILL_ACTIVE );
CloseHandle( hThread );
}
return TRUE;
} |
Last edited by Slugsnack on Mon Mar 29, 2010 8:57 pm; edited 1 time in total |
|
| Back to top |
|
 |
Dark Byte Site Admin
Reputation: 474
Joined: 09 May 2003 Posts: 25962 Location: The netherlands
|
Posted: Mon Mar 29, 2010 7:02 pm Post subject: |
|
|
you could try using TerminateThread(hThread,0); but that's mostly just a brute force
The window does close at all ? (using the ejector)
FreeLibraryAndExitThread might cause a deadlock situation, but not sure
_________________
Tools give you results. Knowledge gives you control.
Like my help? Join me on Patreon so i can keep helping |
|
| Back to top |
|
 |
Slugsnack Grandmaster Cheater Supreme
Reputation: 71
Joined: 24 Jan 2007 Posts: 1857
|
Posted: Mon Mar 29, 2010 7:45 pm Post subject: |
|
|
I could TerminateThread() but it doesn't give the DLL a chance to clean up. The window does close with the ejector but the process then freezes. I'm guessing it's stuck in the loop. Same as when I used WaitForSingleObject().
FreeLibraryAndExitThread() where it is is fine. The deadlocks tend to occur calling LoadLibrary() within dllmain because of the loader lock.
The source for the injector with ejection function is in :
http://forum.cheatengine.org/viewtopic.php?t=496872
Binaries for the DLL and current version of the injector are here :
http://www.mediafire.com/?tcyxnyzyyy5
//edit :
Oddly, changing the DllMain to this:
| Code: | BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) {
if( fdwReason == DLL_PROCESS_ATTACH ) {
hModule = hinstDLL;
CreateThread( NULL, NULL, ( LPTHREAD_START_ROUTINE )main, NULL, NULL, NULL );
}
else if( fdwReason == DLL_PROCESS_DETACH ) {
DWORD dwExitCode;
DWORD dwThreadId = GetWindowThreadProcessId( hwndDlgGlobal, NULL );
HANDLE hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, dwThreadId );
bSignalledTerminate = TRUE;
SendMessage( hwndDlgGlobal, WM_CLOSE, 0, 0 );
// WaitForSingleObject( hThread, INFINITE );
/*do {
Sleep( 100 );
GetExitCodeThread( hThread, &dwExitCode );
}
while( dwExitCode == STILL_ACTIVE );*/
Sleep(1000);
CloseHandle( hThread );
}
return TRUE;
} |
causes the process to crash straight away. Instead of freezing and becoming unresponsive it dies immediately. Again, an access violation in the memory previously occupied by the dll. Pretty weird, since the WM_CLOSE is clearly being processed as the DLL dialog closes.
I know the ejection itself is working because I tried removing the FreeLibraryAndExitThread() call and then closing the DllMain by clicking on the window. This left the DLL mapped into the target's memory but without any running threads. Ejecting with my injector at that point simply released the DLL with the process still running as normal afterwards..
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|