hlsdk

Untitled

Jul 9th, 2010
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.69 KB | None | 0 0
  1. /*
  2. ** VAC encrypted code dumper via WOW64
  3. ** OR: a good use for TIB syscall hooking
  4. **
  5. ** This is for use with the VAC DLL with the following checksums:
  6. ** CRC32: 768D96EF
  7. ** MD5 : 28AB71A9216C0697027218BBF0023E81
  8. **
  9. ** You should inject this *after* VAC has loaded. (ie, launch the game first and
  10. ** then inject this into Steam.exe)
  11. */
  12.  
  13. #include <stdio.h>
  14. #include <windows.h>
  15. #include <conio.h>
  16. #include <Tlhelp32.h>
  17.  
  18.  
  19. // some declarations...
  20. void HookSyscall();
  21.  
  22. /*********************** Return address tables ****************************
  23. These are return addresses that are searched for on the stack whenever
  24. VirtualProtect is called. If one is found, the stack is rewritten so the
  25. code returns into our dumper.
  26. *************************************************************************/
  27.  
  28. // All of the addresses in vac_return_addresses will use this variable
  29. // to determine where the code flow is. It's just the VAC module's handle
  30. // pulled from a snapshot.
  31. static HMODULE vac_base = NULL;
  32.  
  33. #define VAC_RETURN_ADDRESS_COUNT 14
  34.  
  35. static UINT32 vac_return_addresses[ 14 ] = {
  36. 0x14A9,
  37. 0x1E31,
  38. 0x2171,
  39. 0x2497,
  40. 0x29B0,
  41. 0x3586,
  42. 0x4546,
  43. 0x585B,
  44. 0x6611,
  45. 0x6FFA,
  46. 0x7313,
  47. 0x7DA2,
  48. 0x89D3,
  49. 0xF5E0,
  50. };
  51.  
  52. // This is the return address from VirtualProtect() in the decryption routine.
  53. static UINT32 vac_virtualprotect_ra = 0x28F8;
  54. static UINT32 vac_createthread_ra = 0x6E36;
  55.  
  56. // These are set up whenever NtProtectVirtualMemory is called.
  57. static UINT32 vac_last_return_address = 0;
  58. static UINT32 vac_decrypt_address = 0;
  59. static UINT32 vac_decrypt_size = 0;
  60.  
  61. /*****************************************************************************
  62. CreateHijacker allocates some code off in memory that will change fs:0xC0 to
  63. our custom syscall filter when we point stuff to it.
  64. ****************************************************************************/
  65.  
  66. UINT8* CreateHijacker( UINT32 resumeAddress ) {
  67.  
  68. // Get both the hook address and the current program counter
  69. UINT8* hijacker = new UINT8[25];
  70. UINT32 syscall_hook_address = (UINT32)HookSyscall;
  71. UINT32 cur_pc = resumeAddress;
  72. UINT32 cur_pc_in_hijacker = (UINT32) hijacker + 19;
  73.  
  74. // Load some code into the hijacker. This is plain x86 bytecode and is a bit messy,
  75. // not to mention hilariously inefficient.
  76. hijacker[0] = 0x50; // push eax
  77. hijacker[1] = 0xA1; // mov eax, HookSyscall
  78. hijacker[2] = syscall_hook_address & 0xff;
  79. hijacker[3] = (syscall_hook_address & 0x0000ff00) >> 8;
  80. hijacker[4] = (syscall_hook_address & 0x00ff0000) >> 16;
  81. hijacker[5] = (syscall_hook_address & 0xff000000) >> 24;
  82. hijacker[6] = 0x64; // mov fs:0xC0, eax
  83. hijacker[7] = 0xa3;
  84. hijacker[8] = 0xc0;
  85. hijacker[9] = 0x00;
  86. hijacker[10] = 0x00;
  87. hijacker[11] = 0x00;
  88. hijacker[12] = 0x58; // pop eax
  89. hijacker[13] = 0xff; // jmp old_eip
  90. hijacker[14] = 0x25;
  91. hijacker[15] = cur_pc_in_hijacker & 0xff;
  92. hijacker[16] = (cur_pc_in_hijacker & 0xff00) >> 8;
  93. hijacker[17] = (cur_pc_in_hijacker & 0xff0000) >> 16;
  94. hijacker[18] = (cur_pc_in_hijacker & 0xff000000) >> 24;
  95. hijacker[19] = cur_pc & 0xff;
  96. hijacker[20] = (cur_pc & 0xff00) >> 8;
  97. hijacker[21] = (cur_pc & 0xff0000) >> 16;
  98. hijacker[22] = (cur_pc & 0xff000000) >> 24;
  99.  
  100.  
  101. return hijacker;
  102. }
  103.  
  104.  
  105. /*************************************************************************
  106. The code dumper itself. VAC doesn't run scans too often so I won't assume
  107. that there will be conflicts with multiple scans running at the same time.
  108. *************************************************************************/
  109.  
  110. void __stdcall WriteDecryptedCode( UINT32 address, UINT32 size ) {
  111. FILE* dumpFile = NULL;
  112.  
  113. char stringBuffer[ 1000 ];
  114.  
  115. sprintf(stringBuffer, "VACDump_%p.bin", address);
  116.  
  117. dumpFile = fopen(stringBuffer, "wb");
  118. if (dumpFile) {
  119. fwrite( (void*)address, size, 1, dumpFile);
  120. cprintf("Dumped encrypted code at %p OK!\n", address);
  121. } else {
  122. cprintf("Couldn't dump encrypted code at %p\n", address);
  123. }
  124. }
  125.  
  126.  
  127. void __declspec(naked) HookVacReturnAddress() {
  128. __asm {
  129. // Push all registers onto the stack so we don't end up corrupting them.
  130. pushad
  131.  
  132. // Write the file using the arguments passed to NtProtectVirtualMemory earlier.
  133. mov eax, vac_decrypt_size
  134. push eax
  135. mov eax, vac_decrypt_address
  136. push eax
  137. call WriteDecryptedCode
  138.  
  139. // Pull registers and return back to the freshly decrypted code.
  140. popad
  141. push vac_last_return_address
  142. retn
  143. };
  144. }
  145.  
  146. /*************************************************************************
  147. Here's the TIB syscall hook. This is attached to every thread when this code
  148. is injected.
  149.  
  150. Also included in this block:
  151.  
  152. FuckStack - modifies stack on NtProtectVirtualMemory.
  153. HijackVirginThread - changes fs:0xC0 to our filter on new threads.
  154. *************************************************************************/
  155.  
  156. // real syscall address
  157. static UINT32 real_syscall = 0;
  158.  
  159. void __stdcall FuckStack() {
  160.  
  161. UINT32* _sp;
  162. __asm mov _sp, esp;
  163.  
  164. int found_arguments = 0;
  165.  
  166. cprintf("NtProtectVirtualMemory was called. Looking for the cause...\n");
  167.  
  168. for (int i = 0; i < 3000; i++) {
  169.  
  170. // We check for return addresses after the VAC decrypter is called,
  171. // and rewrite them so they go into the dumper. However, if we don't have
  172. // arguments from VirtualProtect, we don't even bother with this so the code
  173. // dumper doesn't crash.
  174. for (int x = 0; x < VAC_RETURN_ADDRESS_COUNT; x++) {
  175. if ((found_arguments) && (_sp[i] == vac_return_addresses[x])) {
  176. vac_last_return_address = _sp[i];
  177. _sp[i] = (UINT32)HookVacReturnAddress;
  178. }
  179. }
  180.  
  181. // What we also check for is a return from VirtualProtect so we can
  182. // grab the arguments passed to it.
  183. if (_sp[i] == vac_virtualprotect_ra) {
  184. vac_decrypt_address = _sp[i + 1];
  185. vac_decrypt_size = _sp[i + 2];
  186. found_arguments = 1;
  187. }
  188. }
  189. }
  190.  
  191.  
  192.  
  193. // NewThreadHijacker: Code that executes before CreateThread() returns to VAC.
  194. void __declspec(naked) NewThreadHijacker() {
  195. HANDLE newThreadHandle;
  196.  
  197. __asm pushad;
  198.  
  199. // get the new thread handle
  200. __asm mov newThreadHandle, eax
  201.  
  202. // STOP! IN THE NAME OF LOOOOOOOOOOOOOOOOOVE
  203. SuspendThread( newThreadHandle );
  204.  
  205. CONTEXT ctx;
  206. GetThreadContext( newThreadHandle, &ctx );
  207.  
  208. UINT8* hijacker;
  209.  
  210. hijacker = CreateHijacker( ctx.Eip );
  211.  
  212. ctx.Eip = (DWORD)hijacker;
  213. SetThreadContext( newThreadHandle, &ctx );
  214. ResumeThread( newThreadHandle );
  215.  
  216. __asm {
  217. popad
  218. jmp vac_createthread_ra
  219. };
  220. }
  221.  
  222.  
  223. void __stdcall HijackVirginThread() {
  224. UINT32* _sp;
  225. __asm mov _sp, esp;
  226.  
  227.  
  228. cprintf("New thread was created. Sending our thread hijacker after it.\n");
  229.  
  230. for (int i = 0; i < 3000; i++) {
  231. if ( _sp[i] == vac_createthread_ra ) {
  232. // Rewrite it to return into new code
  233. _sp[i] = (UINT32)NewThreadHijacker;
  234. }
  235. }
  236. }
  237.  
  238. void __declspec(naked) HookSyscall() {
  239. __asm {
  240. cmp eax, 0x4D // NtProtectVirtualMemory on Windows 7
  241. jz do_stack_crawl
  242.  
  243. cmp eax, 0x4B // NtCreateThread
  244. jz hijack_virgin_thread
  245. cmp eax, 0xA5 // NtCreateThreadEx
  246. jz hijack_virgin_thread
  247.  
  248. continue_syscall:
  249. jmp real_syscall
  250.  
  251. do_stack_crawl:
  252. pushad
  253. call FuckStack
  254. popad
  255. jmp continue_syscall
  256.  
  257. hijack_virgin_thread:
  258. pushad
  259. call HijackVirginThread
  260. popad
  261. jmp continue_syscall
  262.  
  263. };
  264. }
  265.  
  266. /*************************************************************************
  267. And finally, the startup code!
  268. *************************************************************************/
  269.  
  270. void StartDumper() {
  271. AllocConsole();
  272.  
  273. cprintf("We're benign. Don't call us a hack.\n");
  274. cprintf("Looking for VAC... (you'll probably want to start your game now)\n");
  275.  
  276.  
  277. // Grab the original syscall pointer (it's the same for all threads)
  278. __asm {
  279. push eax
  280. mov eax, fs:0xC0
  281. mov real_syscall, eax
  282. pop eax
  283. };
  284.  
  285. // First loop here goes and hunts down the VAC module.
  286. while (!vac_base) {
  287.  
  288. MODULEENTRY32 module;
  289. // Take a fresh snapshot. If we can't take it, die.
  290. HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
  291. if (!snapshot) {
  292. cprintf("Snapshot couldn't be taken.\n");
  293. return;
  294. }
  295.  
  296. module.dwSize = sizeof( MODULEENTRY32 );
  297.  
  298. // Grab the first module
  299. BOOL moduleFound = Module32First( snapshot, &module );
  300.  
  301. while ( moduleFound ) {
  302. if ( strstr(module.szModule, ".tmp") ) {
  303. cprintf("VAC found in module list as %s\n", module.szModule);
  304.  
  305. // Grab VAC's hModule and rebase our pointers.
  306. vac_base = module.hModule;
  307.  
  308. for (int i = 0; i < VAC_RETURN_ADDRESS_COUNT; i++)
  309. vac_return_addresses[i] += (UINT32)vac_base;
  310. vac_virtualprotect_ra += (UINT32)vac_base;
  311. vac_createthread_ra += (UINT32)vac_base;
  312. }
  313.  
  314. moduleFound = Module32Next( snapshot, &module );
  315. }
  316. }
  317.  
  318. // Here is the actual hook. We'll enumerate through threads and change fs:0xC0 so syscalls
  319. // redirect into our filter.
  320. cprintf("Attaching stack patcher to threads...\n");
  321.  
  322. UINT32 ourProcess = GetCurrentProcessId();
  323. THREADENTRY32 thread;
  324. HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  325.  
  326. if (!snapshot) {
  327. cprintf("Snapshot failed. Can't attach patcher to threads.\n");
  328. return;
  329. }
  330.  
  331. thread.dwSize = sizeof(THREADENTRY32);
  332. BOOL haveThread = Thread32First( snapshot, &thread );
  333.  
  334. while ( haveThread ) {
  335.  
  336. // If this thread isn't in our process we shouldn't even care about it.
  337. if ( thread.th32OwnerProcessID != ourProcess) {
  338. haveThread = Thread32Next( snapshot, &thread);
  339. continue;
  340. }
  341.  
  342. // If the thread is this thread we'll just freeze like a dumbass.
  343. if (thread.th32ThreadID == GetCurrentThreadId()) {
  344. haveThread = Thread32Next( snapshot, &thread);
  345. continue;
  346. }
  347.  
  348. // Open the thread and attempt to pause it. If we can't open it just
  349. // go on to the next thread.
  350. HANDLE hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, thread.th32ThreadID);
  351. if (!hThread) {
  352. cprintf("WARNING: Couldn't open thread %d.\n", thread.th32ThreadID);
  353. haveThread = Thread32Next( snapshot, &thread);
  354. }
  355.  
  356. CONTEXT ctx;
  357.  
  358. // Now pause the thread and grab its context. If we can't get the context, resume
  359. // it and move onto the next one.
  360. SuspendThread( hThread );
  361. BOOL gotCtx = GetThreadContext( hThread, &ctx );
  362. if (!gotCtx) {
  363. cprintf("Couldn't get thread context. Last Windows error %d\n", GetLastError());
  364. ResumeThread(hThread);
  365. haveThread = Thread32Next( snapshot, &thread);
  366. continue;
  367. }
  368.  
  369. // Allocate the thread hijacker.
  370. UINT8* hijacker = CreateHijacker( ctx.Eip );
  371.  
  372. // Change the program counter to go into the hijacker, then resume the thread.
  373. ctx.Eip = (DWORD)hijacker;
  374.  
  375. if ( SetThreadContext( hThread, &ctx ) ) {
  376. cprintf("Assigned context OK!\n");
  377. } else cprintf("Couldn't assign context. Windows error %d\n",GetLastError());
  378.  
  379.  
  380. ResumeThread( hThread );
  381.  
  382. // Wait 100 ms then free the hijacker.
  383. //Sleep(10);
  384. //free(hijacker);
  385.  
  386. cprintf("Thread %d was hijacked!\n", thread.th32ThreadID);
  387.  
  388. // Move on to the next thread.
  389. haveThread = Thread32Next( snapshot, &thread );
  390. }
  391.  
  392. cprintf("Startup went OK. Go play and I'll dump shit (or maybe just crash)\n");
  393. }
  394.  
  395.  
  396. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
  397.  
  398. if (fdwReason == DLL_PROCESS_ATTACH) {
  399. CreateThread( NULL, 100000, (LPTHREAD_START_ROUTINE)StartDumper,NULL,NULL,NULL);
  400. }
  401.  
  402. return TRUE;
  403. }
Advertisement
Add Comment
Please, Sign In to add comment