Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


DLL Ejection

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming
View previous topic :: View next topic  
Author Message
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Sun Mar 28, 2010 9:14 pm    Post subject: DLL Ejection Reply with quote

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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 474

Joined: 09 May 2003
Posts: 25962
Location: The netherlands

PostPosted: Sun Mar 28, 2010 9:32 pm    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Mon Mar 29, 2010 6:51 am    Post subject: Reply with quote

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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 474

Joined: 09 May 2003
Posts: 25962
Location: The netherlands

PostPosted: Mon Mar 29, 2010 8:58 am    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Mon Mar 29, 2010 9:40 am    Post subject: Reply with quote

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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 474

Joined: 09 May 2003
Posts: 25962
Location: The netherlands

PostPosted: Mon Mar 29, 2010 12:42 pm    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Mon Mar 29, 2010 6:29 pm    Post subject: Reply with quote

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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 474

Joined: 09 May 2003
Posts: 25962
Location: The netherlands

PostPosted: Mon Mar 29, 2010 7:02 pm    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Mon Mar 29, 2010 7:45 pm    Post subject: Reply with quote

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
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites