Injector.cpp
上传用户:jstlsd
上传日期:2007-01-13
资源大小:186k
文件大小:14k
源码类别:

钩子与API截获

开发平台:

Visual C++

  1. //---------------------------------------------------------------------------
  2. //
  3. // Injector.cpp
  4. //
  5. // SUBSYSTEM: 
  6. // API hooking system
  7. // MODULE:    
  8. //              Implements injection mechanism
  9. //
  10. // DESCRIPTION:
  11. //              
  12. //
  13. // AUTHOR: Ivo Ivanov (ivopi@hotmail.com)
  14. // DATE: 2001 November 13,  version 1.0 
  15. //                                                                         
  16. // FIXES:
  17. //              - 2002 November 20
  18. //                Added a mechanism for handling reference count of the HookTool.dll 
  19. //                instances. This allows us to fix a problem caused by the asynchronous 
  20. //                behavior of ::UnhookWindowsHookEx() API                
  21. //
  22. //---------------------------------------------------------------------------
  23. #include "Injector.h"
  24. #include "..CommonModuleInstance.h"
  25. #include "..CommonSysUtils.h"
  26. #include "..CommonIniFile.h"
  27. #include "NtInjectorThread.h"
  28. //---------------------------------------------------------------------------
  29. //
  30. // Thread function prototype
  31. //
  32. //---------------------------------------------------------------------------
  33. typedef unsigned (__stdcall *PTHREAD_START)(void *);
  34. //---------------------------------------------------------------------------
  35. //
  36. // External declarations
  37. //
  38. //---------------------------------------------------------------------------
  39. extern LRESULT CALLBACK GetMsgProc(
  40. int code,       // hook code
  41. WPARAM wParam,  // removal option
  42. LPARAM lParam   // message
  43. );
  44. //---------------------------------------------------------------------------
  45. //
  46. // class CInjector  
  47. //
  48. //---------------------------------------------------------------------------
  49. CInjector::CInjector(BOOL bServerInstance):
  50. m_bServerInstance(bServerInstance),
  51. m_bHookAllEnabledInitialized(FALSE),
  52. m_bHookAllEnabled(FALSE)
  53. {
  54. }
  55. CInjector::~CInjector()
  56. {
  57. }
  58. //
  59. // examines whether a process should be hooked up by the DLL
  60. //
  61. BOOL CInjector::IsProcessForHooking(PSTR pszExaminedProcessName)
  62. {
  63. BOOL  bHoolAll = GetHookAllEnabled();
  64. BOOL  bProcessProtected = FALSE;
  65. char  szProcessName[MAX_PATH];
  66. DWORD dwStartPos = 0;
  67. long  nCommaPos;
  68. if (bHoolAll)
  69. {
  70. while ( (dwStartPos < strlen(m_szProcessesForHooking)) &&
  71.         GetNextCommaSeparatedString(
  72. &m_szProcessesForHooking[dwStartPos],
  73. szProcessName,
  74. sizeof(szProcessName),
  75. &nCommaPos
  76.       )
  77. {
  78. strcat(szProcessName, ".exe");
  79. if (0 == stricmp(szProcessName, pszExaminedProcessName))
  80. {
  81. bProcessProtected = TRUE;
  82. return FALSE;
  83. } // if
  84. dwStartPos += nCommaPos + 1;
  85. } // while
  86. } // if
  87. if (!bHoolAll)
  88. {
  89. dwStartPos = 0;
  90. while ( (dwStartPos < strlen(m_szProcessesForHooking)) &&
  91.         GetNextCommaSeparatedString(
  92. &m_szProcessesForHooking[dwStartPos],
  93. szProcessName,
  94. sizeof(szProcessName),
  95. &nCommaPos
  96.       )
  97. {
  98. strcat(szProcessName, ".exe");
  99. if (0 == stricmp(szProcessName, pszExaminedProcessName))
  100. return TRUE;
  101. dwStartPos += nCommaPos + 1;
  102. } // while
  103. return FALSE;
  104. } // if
  105. return TRUE;
  106. }
  107. //
  108. // Return the name of the INI file
  109. //
  110. void CInjector::GetIniFile(char* pszIniFile)
  111. {
  112. char  *pdest;
  113. ::GetModuleFileName(
  114. ModuleFromAddress(GetMsgProc), 
  115. pszIniFile, 
  116. MAX_PATH
  117. );
  118. pdest = &pszIniFile[strlen(pszIniFile) - 4];
  119. strcpy(pdest, ".ini");
  120. }
  121. //
  122. // Get the value of [Scope] / HookAll from the INI file
  123. //
  124. BOOL CInjector::GetHookAllEnabled()
  125. {
  126. if (!m_bHookAllEnabledInitialized)
  127. {
  128. char szIniFile[MAX_PATH];
  129. GetIniFile(szIniFile);
  130. CIniFile iniFile(szIniFile);
  131. m_bHookAllEnabled = iniFile.ReadBool(
  132. "Scope",
  133. "HookAll",
  134. TRUE
  135. );
  136. m_bHookAllEnabledInitialized = TRUE;
  137. strcpy(m_szProcessesForHooking, "");
  138. strcpy(m_szProtectedProcesses, "");
  139. //
  140. // Should we process the two lists with processes for hooking
  141. // and ones that shouldn't be hooked up at all
  142. //
  143. if (!m_bHookAllEnabled)
  144. iniFile.ReadString(
  145. "Scope",
  146. "Hook",
  147. "",
  148. m_szProcessesForHooking
  149. );
  150. else
  151. iniFile.ReadString(
  152. "Scope",
  153. "Protect",
  154. "",
  155. m_szProtectedProcesses
  156. );
  157. } // if
  158. return m_bHookAllEnabled;
  159. }
  160. //---------------------------------------------------------------------------
  161. //
  162. // class CWinHookInjector  
  163. //
  164. //---------------------------------------------------------------------------
  165. HHOOK* CWinHookInjector::sm_pHook = NULL;
  166. CWinHookInjector::CWinHookInjector(BOOL bServerInstance, HHOOK* pHook):
  167. CInjector(bServerInstance)
  168. {
  169. sm_pHook = pHook;
  170. }
  171. //
  172. // Inject the DLL into all running processes
  173. //
  174. BOOL CWinHookInjector::InjectModuleIntoAllProcesses()
  175. {
  176. *sm_pHook = ::SetWindowsHookEx(
  177. WH_GETMESSAGE,
  178. (HOOKPROC)(GetMsgProc),
  179. ModuleFromAddress(GetMsgProc), 
  180. 0
  181. );
  182. return (NULL != *sm_pHook);
  183. }
  184. //
  185. // Eject the DLL from all processes if it has been injected before 
  186. //
  187. BOOL CWinHookInjector::EjectModuleFromAllProcesses(HANDLE hWaitOn)
  188. {
  189. BOOL bResult;
  190. if (NULL != *sm_pHook)
  191. {
  192. bResult = ::UnhookWindowsHookEx(*sm_pHook);
  193. // Wait on the reference counter event handle 
  194. // until all the instances of the DLL get unloaded
  195. ::WaitForSingleObject(hWaitOn, INFINITE);
  196. }
  197. else 
  198. bResult = TRUE;
  199. return bResult;
  200. }
  201. //---------------------------------------------------------------------------
  202. //
  203. // class CRemThreadInjector  
  204. //
  205. //---------------------------------------------------------------------------
  206. CCSWrapper  CRemThreadInjector::sm_CritSecInjector;
  207. //
  208. //
  209. //
  210. CRemThreadInjector::CRemThreadInjector(BOOL bServerInstance):
  211. CInjector(bServerInstance),
  212. m_pNtInjectorThread(NULL)
  213. {
  214. if (bServerInstance)
  215. m_pNtInjectorThread = new CNtInjectorThread(this);
  216. }
  217. //
  218. //
  219. //
  220. CRemThreadInjector::~CRemThreadInjector()
  221. {
  222. if (m_bServerInstance)
  223. {
  224. m_pNtInjectorThread->SetActive(FALSE);
  225. delete m_pNtInjectorThread;
  226. } // if
  227. }
  228. //
  229. // Attempts to enable SeDebugPrivilege. This is required by use of
  230. // CreateRemoteThread() under NT/2K
  231. //
  232. BOOL CRemThreadInjector::EnableDebugPrivilege()
  233. {
  234. HANDLE           hToken;
  235. LUID             sedebugnameValue;
  236. TOKEN_PRIVILEGES tp;
  237. if ( !::OpenProcessToken( 
  238. GetCurrentProcess(),
  239. TOKEN_ADJUST_PRIVILEGES | // to adjust privileges
  240. TOKEN_QUERY,              // to get old privileges setting
  241. &hToken 
  242. ) )
  243. //
  244. // OpenProcessToken() failed
  245. //
  246. return FALSE;
  247. //
  248. // Given a privilege's name SeDebugPrivilege, we should locate its local LUID mapping.
  249. //
  250. if ( !::LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) )
  251. {
  252. //
  253. // LookupPrivilegeValue() failed
  254. //
  255. ::CloseHandle( hToken );
  256. return FALSE;
  257. }
  258. tp.PrivilegeCount = 1;
  259. tp.Privileges[0].Luid = sedebugnameValue;
  260. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  261. if ( !::AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(tp), NULL, NULL ) )
  262. {
  263. //
  264. // AdjustTokenPrivileges() failed
  265. //
  266. ::CloseHandle( hToken );
  267. return FALSE;
  268. }
  269. ::CloseHandle( hToken );
  270. return TRUE;
  271. }
  272. //
  273. // Inject the DLL into all running processes
  274. //
  275. BOOL CRemThreadInjector::InjectModuleIntoAllProcesses()
  276. {
  277. BOOL                bResult = FALSE;
  278. if (m_bServerInstance)
  279. {
  280. CTaskManager        taskManager;
  281. CExeModuleInstance* pProcess; 
  282. taskManager.Populate(FALSE);
  283. EnableDebugPrivilege();
  284. for (long i = 0; i < taskManager.GetProcessCount(); i++)
  285. {
  286. pProcess = taskManager.GetProcessByIndex(i);
  287. bResult = InjectModuleInto( pProcess->Get_ProcessId() ) || bResult; 
  288. } // for
  289. m_pNtInjectorThread->SetActive(TRUE);
  290. } // if
  291. return bResult;
  292. }
  293. //
  294. // Eject the DLL from all processes if it has been injected before 
  295. //
  296. BOOL CRemThreadInjector::EjectModuleFromAllProcesses(HANDLE hWaitOn)
  297. {
  298. BOOL                bResult = FALSE;
  299. if (m_bServerInstance)
  300. {
  301. CTaskManager        taskManager;
  302. CExeModuleInstance* pProcess; 
  303. m_pNtInjectorThread->SetActive(FALSE);
  304. taskManager.Populate(FALSE);
  305. for (long i = 0; i < taskManager.GetProcessCount(); i++)
  306. {
  307. pProcess = taskManager.GetProcessByIndex(i);
  308. bResult = EjectModuleFrom( pProcess->Get_ProcessId() ) || bResult; 
  309. } // for
  310. } // if
  311. return bResult;
  312. }
  313. //
  314. // Inject the DLL into address space of a specific external process
  315. //
  316. BOOL CRemThreadInjector::InjectModuleInto(DWORD dwProcessId)
  317. {
  318. CLockMgr<CCSWrapper> lockMgr(sm_CritSecInjector, TRUE);
  319. BOOL                 bResult  = FALSE; 
  320. //
  321. // We shouldn't inject the dll into the address space of the
  322. // server
  323. //
  324. if (dwProcessId != ::GetCurrentProcessId())
  325. {
  326. CTaskManager         taskManager;
  327. taskManager.PopulateProcess(dwProcessId, TRUE);
  328. CExeModuleInstance  *pProcess = taskManager.GetProcessById(dwProcessId);
  329. if (TRUE == IsProcessForHooking(pProcess->GetBaseName()))
  330. bResult = DoInjectModuleInto(pProcess);
  331. } // if
  332. return bResult;
  333. }
  334. //
  335. // Eject the DLL from the address space of an external process
  336. //
  337. BOOL CRemThreadInjector::EjectModuleFrom(DWORD dwProcessId)
  338. {
  339. CLockMgr<CCSWrapper> lockMgr(sm_CritSecInjector, TRUE);
  340. BOOL   bResult  = FALSE;
  341. //
  342. // We shouldn't eject the dll into the address space of the
  343. // server
  344. //
  345. if (dwProcessId != ::GetCurrentProcessId())
  346. {
  347. CTaskManager         taskManager;
  348. taskManager.PopulateProcess(dwProcessId, TRUE);
  349. CExeModuleInstance  *pProcess = taskManager.GetProcessById(dwProcessId);
  350. if (pProcess)
  351. bResult  = DoEjectModuleFrom(*pProcess);
  352. } // if
  353. return bResult;
  354. }
  355. //
  356. // Execute injection mechanism for NT/2K systems
  357. //
  358. BOOL CRemThreadInjector::DoInjectModuleInto(CExeModuleInstance *pProcess)
  359. {
  360. BOOL   bResult          = FALSE; 
  361. HANDLE hProcess         = NULL;
  362. HANDLE hThread          = NULL;
  363. PSTR   pszLibFileRemote = NULL;
  364. __try 
  365. {
  366. if (TRUE == IsWindows9x())
  367. __leave;
  368. if (NULL == pProcess)
  369. __leave;
  370. char szLibFile[MAX_PATH];
  371. ::GetModuleFileNameA(
  372. ModuleFromAddress(GetMsgProc), 
  373. szLibFile, 
  374. MAX_PATH
  375. );
  376. BOOL bFound = FALSE;
  377. CModuleInstance* pModuleInstance;
  378. for (long i = 0; i < pProcess->GetModuleCount(); i++)
  379. {
  380. pModuleInstance = pProcess->GetModuleByIndex(i);
  381. if ( 0 == stricmp(pModuleInstance->Get_Name(), szLibFile) )
  382. {
  383. bFound = TRUE;
  384. break;
  385. } // if
  386. } // for
  387. if (bFound) 
  388. __leave;
  389. // Get a handle for the process we want to inject into
  390. hProcess = ::OpenProcess(
  391. PROCESS_ALL_ACCESS, // Specifies all possible access flags
  392. FALSE, 
  393. pProcess->Get_ProcessId()
  394. );
  395. if (hProcess == NULL) 
  396. __leave;
  397. // Calculate the number of bytes needed for the DLL's pathname
  398. int cch = 1 + strlen(szLibFile);
  399. // Allocate space in the remote process for the pathname
  400. pszLibFileRemote = (PSTR)::VirtualAllocEx(
  401. hProcess, 
  402. NULL, 
  403. cch, 
  404. MEM_COMMIT, 
  405. PAGE_READWRITE
  406. );
  407. if (pszLibFileRemote == NULL) 
  408. __leave;
  409. // Copy the DLL's pathname to the remote process's address space
  410. if (!::WriteProcessMemory(
  411. hProcess, 
  412. (PVOID)pszLibFileRemote, 
  413. (PVOID)szLibFile, 
  414. cch, 
  415. NULL)) 
  416. __leave;
  417. // Get the real address of LoadLibraryW in Kernel32.dll
  418. PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
  419. ::GetProcAddress(::GetModuleHandle("Kernel32"), "LoadLibraryA");
  420. if (pfnThreadRtn == NULL) 
  421. __leave;
  422. // Create a remote thread that calls LoadLibraryW(DLLPathname)
  423. hThread = ::CreateRemoteThread(
  424. hProcess, 
  425. NULL, 
  426. 0, 
  427. pfnThreadRtn, 
  428. (PVOID)pszLibFileRemote, 
  429. 0, 
  430. NULL
  431. );
  432. if (hThread == NULL) 
  433. __leave;
  434. // Wait for the remote thread to terminate
  435. ::WaitForSingleObject(hThread, INFINITE);
  436. bResult = TRUE; 
  437. }
  438. __finally 
  439. // Free the remote memory that contained the DLL's pathname
  440. if (pszLibFileRemote != NULL) 
  441. ::VirtualFreeEx(hProcess, (PVOID)pszLibFileRemote, 0, MEM_RELEASE);
  442. if (hThread  != NULL) 
  443. ::CloseHandle(hThread);
  444. if (hProcess != NULL) 
  445. ::CloseHandle(hProcess);
  446. }
  447. return bResult;
  448. }
  449. //
  450. // Perform actual ejection of the DLL from the address space of an external process
  451. //
  452. BOOL CRemThreadInjector::DoEjectModuleFrom(CExeModuleInstance& process)
  453. {
  454. BOOL   bResult  = FALSE; 
  455. HANDLE hProcess = NULL; 
  456. HANDLE hThread  = NULL;
  457. __try 
  458. {
  459. if (TRUE == IsWindows9x())
  460. __leave;
  461. //
  462. // Do not force the server to eject the DLL
  463. //
  464. if (process.Get_ProcessId() == ::GetCurrentProcessId())
  465. __leave;
  466. char szLibFile[MAX_PATH];
  467. ::GetModuleFileNameA(
  468. ModuleFromAddress(GetMsgProc), 
  469. szLibFile, 
  470. MAX_PATH
  471. );
  472. BOOL bFound = FALSE;
  473. CModuleInstance* pModuleInstance;
  474. for (long i = 0; i < process.GetModuleCount(); i++)
  475. {
  476. pModuleInstance = process.GetModuleByIndex(i);
  477. if ( 0 == stricmp(pModuleInstance->Get_Name(), szLibFile) )
  478. {
  479. bFound = TRUE;
  480. break;
  481. } // if
  482. } // for
  483. if (!bFound) 
  484. __leave;
  485. // Get a handle for the target process.
  486. hProcess = ::OpenProcess(
  487. PROCESS_ALL_ACCESS, // Specifies all possible access flags
  488. FALSE, 
  489. process.Get_ProcessId()
  490. );
  491. if (hProcess == NULL) 
  492. __leave;
  493. // Get the real address of LoadLibraryW in Kernel32.dll
  494. PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
  495. ::GetProcAddress(::GetModuleHandle("Kernel32"), "FreeLibrary");
  496. if (pfnThreadRtn == NULL) 
  497. __leave;
  498. // Create a remote thread that calls LoadLibraryW(DLLPathname)
  499. hThread = ::CreateRemoteThread(
  500. hProcess, 
  501. NULL, 
  502. 0, 
  503. pfnThreadRtn, 
  504. pModuleInstance->Get_Module(), 
  505. 0, 
  506. NULL
  507. );
  508. if (hThread == NULL) 
  509. __leave;
  510. // Wait for the remote thread to terminate
  511. ::WaitForSingleObject(hThread, INFINITE);
  512. bResult = TRUE; 
  513. }
  514. __finally 
  515. if (hThread != NULL) 
  516. ::CloseHandle(hThread);
  517. if (hProcess != NULL) 
  518. ::CloseHandle(hProcess);
  519. }
  520. return bResult;
  521. }
  522. //----------------------------End of the file -------------------------------