Utils.cpp
上传用户:kittypts
上传日期:2018-02-11
资源大小:241k
文件大小:12k
源码类别:

PlugIns编程

开发平台:

Visual C++

  1. /*
  2. *
  3. *  This file is
  4. *    Copyright (C) 2006-2008 Nektra S.A.
  5. *  
  6. *  This program is free software; you can redistribute it and/or modify
  7. *  it under the terms of the GNU Lesser General Public License as published by
  8. *  the Free Software Foundation; either version 2, or (at your option)
  9. *  any later version.
  10. *  
  11. *  This program is distributed in the hope that it will be useful,
  12. *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. *  GNU General Public License for more details.
  15. *  
  16. */
  17. #include "utils.h"
  18. #include <Windows.h>
  19. #include <Shlwapi.h>
  20. #include <TlHelp32.h>
  21. #include <distorm.h>
  22. #include "NtApi.h"
  23. #include "base64.h"
  24. //Macros:
  25. #define strcmp_WS(wstr, char_ptr) (strcmp((char*)wstr.p, char_ptr) == 0)
  26. #define WS_TO_STRING(wstr) (std::string((char*)wstr.p))
  27. #define INSPECT_SIZE (0xF)
  28. #define INST_MAXIMUM_SIZE (15) /* From: Distorm -> x86defs.h */
  29. template NktIdList;
  30. const char* RelativeInstructions[3] = { "e9", "e8", "eb" };
  31. //Helpers:
  32. BOOL IsInstructionRelative(const std::string& inst);
  33. INT_PTR RawCallFunction(LPCVOID f, NktCallingConvention cc, const NktCallContext&, SIZE_T szBytes, SIZE_T cleanSize);
  34. ///
  35. /// Utils::DbgPrintRegisters
  36. ///
  37. void NktUtils::DbgPrintRegisters(NktRegisters* reg)
  38. {
  39. char buffer[256] = {0};
  40. sprintf_s(buffer, "Registers - tid: %d# ", NktNtApi::GetCurrentThreadId());
  41. OutputDebugStringA(buffer);
  42. char* names[] = { "lastError", "EFLAGS", "EDI", "ESI", "EBP", "ESP", "EBX", "EDX", "ECX", "EAX" };
  43. for (int i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
  44. {
  45. sprintf_s(buffer, "%s: %X -", names[i], reg[i]);
  46. OutputDebugStringA(buffer);
  47. }
  48. OutputDebugStringA("n");
  49. }
  50. ///
  51. /// Utils::GetMinJmpSize
  52. ///
  53. const SIZE_T NktUtils::GetMinJmpSize(const DV_PTR code, SIZE_T jmp_size)
  54. {
  55. SIZE_T count = 0;
  56. _OffsetType offset = 0;
  57. _DecodeType dt = Decode32Bits;
  58. _DecodedInst* decodedInstructions = new _DecodedInst[INSPECT_SIZE];
  59. unsigned int dc = 0;
  60. //Decode instructions
  61. distorm_decode(offset, (const unsigned char*)code, INSPECT_SIZE, dt, decodedInstructions, INSPECT_SIZE, &dc);
  62. bool seek_min = true;
  63. unsigned int i = 0;
  64. while (seek_min)
  65. {
  66. count += decodedInstructions[i].size;
  67. i++;
  68. seek_min = (count < jmp_size) && (i < dc);
  69. }
  70. delete[] decodedInstructions;
  71. return count;
  72. }
  73. ///
  74. /// Utils::SimpleCallFunction
  75. ///
  76. INT_PTR NktUtils::SimpleCallFunction(const NktFunctionWrapper& f, LPCVOID pms, SIZE_T szBytes, LPCVOID pThis)
  77. {
  78. NktCallingConvention cc = static_cast<NktCallingConvention>(f.GetCallConvention());
  79. NktRegisters registers = {0};
  80. NktCallContext context;
  81. PINT_PTR params = (pThis && cc == thiscall_)? (PINT_PTR)alloca(szBytes+sizeof(pThis)) : (PINT_PTR)pms;
  82. context.regs = &registers;
  83. context.tag = 0;
  84. //Arrange parameters for call
  85. switch (cc)
  86. {
  87. case thiscall_:
  88. {
  89. if (pThis)
  90. {
  91. params[0] = (INT_PTR)pThis;
  92. memcpy((params+1), pms, szBytes);
  93. }
  94. break;
  95. }
  96. //This pointer on ECX
  97. case thiscall_ms_:
  98. {
  99. if (pThis)
  100. {
  101. registers.ECX = (INT_PTR)pThis;
  102. }
  103. else
  104. {
  105. _ASSERT(szBytes >= REGISTER_SIZE);
  106. registers.ECX = params[0];
  107. ++params;
  108. szBytes -= REGISTER_SIZE;
  109. }
  110. break;
  111. }
  112. //First and second param on ECX & EDX
  113. case fastcall_:
  114. {
  115. int dec = 0;
  116. PINT_PTR regs[] = { &registers.ECX, &registers.EDX };
  117. while (dec < 2 && szBytes >= REGISTER_SIZE)
  118. {
  119. *regs[dec] = params[0];
  120. ++params;
  121. szBytes -= REGISTER_SIZE;
  122. }
  123. break;
  124. }
  125. }
  126. context.pms = params;
  127. return RawCallFunction(
  128. f.GetAddress(),
  129. static_cast<NktCallingConvention>(f.GetCallConvention()),
  130. context, 
  131. f.GetStackParamSize(), 
  132. f.GetCallerCleanSize());
  133. }
  134. ///
  135. /// Utils::CallFunction
  136. ///
  137. void NktUtils::CallFunction(const NktFunctionWrapper& fw, NktRegisters* registers, const void* params, SIZE_T szBytes)
  138. {
  139. NktCallingConvention cc = static_cast<NktCallingConvention>(fw.GetCallConvention());
  140. NktCallContext context;
  141. context.pms = params;
  142. context.regs = registers;
  143. context.tag = 0;
  144. RawCallFunction(fw.GetAddress(), cc, context, fw.GetStackParamSize(), fw.GetCallerCleanSize());
  145. }
  146. ///
  147. /// Utils::UpdateIndirections
  148. ///
  149. void NktUtils::UpdateIndirections(NktVirtualCode& code, const unsigned int prev_base, const unsigned int new_base)
  150. {
  151. SIZE_T count = 0;
  152. _OffsetType offtype = 0;
  153. _DecodeType dt = Decode32Bits;
  154. SIZE_T codeSize = code.Pivot();
  155. SIZE_T maxInst = (codeSize < INST_MAXIMUM_SIZE)? INST_MAXIMUM_SIZE : codeSize;
  156. _DecodedInst* decodedInstructions =  (_DecodedInst*)alloca(maxInst * sizeof(_DecodedInst));
  157. unsigned int dc = 0;
  158. //Decode instructions:
  159. _DecodeResult res = distorm_decode(offtype, (unsigned char*)code.GetAddress(), (int)codeSize, dt, decodedInstructions, (unsigned int)maxInst, &dc);
  160. _ASSERT(dc);
  161. //Translate:
  162. DV_PTR diff = 0;
  163. for (unsigned int i = 0; i < dc; i++)
  164. {
  165. _DecodedInst& di = decodedInstructions[i];
  166. if (IsInstructionRelative(WS_TO_STRING(di.instructionHex)))
  167. {
  168. unsigned int opAt = (unsigned int)(di.offset + (di.size - sizeof(void*)));
  169. code.RawRead(opAt, (unsigned int*)&diff);
  170. DV_PTR addrDest = prev_base + diff;
  171. code.RawWrite(opAt, addrDest - new_base);
  172. }
  173. }
  174. }
  175. ///
  176. /// Utils::WriteMem
  177. ///
  178. BOOL NktUtils::WriteMem(const NktVirtualCode& code, void* dest, DWORD pid)
  179. {
  180. DWORD oldProt = 0;
  181. BOOL ret;
  182. ret = VirtualProtect(dest, code.GetSize(), PAGE_EXECUTE_READWRITE, &oldProt);
  183. if (ret)
  184. {
  185. HANDLE hproc = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
  186. ret = WriteProcessMemory(hproc, dest, (LPVOID)code.GetAddress(), code.Pivot(), NULL);
  187. VirtualProtect(dest, code.GetSize(), oldProt, NULL);
  188. ::CloseHandle(hproc);
  189. }
  190. return ret;
  191. }
  192. ///
  193. /// Utils::SuspendThreads
  194. ///
  195. void NktUtils::SuspendThreads(NktSegmentRange range, OUT NktIdList& suspended_threads, DWORD process_id)
  196. {
  197. DWORD curr_thread_id = GetCurrentThreadId();
  198. HANDLE hSnapshot = NULL;
  199. bool suspending = true;
  200. THREADENTRY32 thread_entry;
  201. CONTEXT thread_context;
  202. ZeroMemory(&thread_entry, sizeof(THREADENTRY32));
  203. ZeroMemory(&thread_context, sizeof(CONTEXT));
  204. thread_entry.dwSize = sizeof(THREADENTRY32);
  205. thread_context.ContextFlags = CONTEXT_ALL;
  206. while (suspending)
  207. {
  208. suspending = false;
  209. //Get snapshot:
  210. hSnapshot =  CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, process_id);
  211. if(hSnapshot == INVALID_HANDLE_VALUE)
  212. throw E_HANDLE;
  213. //Loop threads:
  214. if (!Thread32First(hSnapshot, &thread_entry))
  215. throw E_FAIL;
  216. do
  217. {
  218. //Skip for this thread and other process' threads
  219. if ((thread_entry.th32OwnerProcessID != process_id) ||
  220. (thread_entry.th32ThreadID == curr_thread_id) )
  221. continue;
  222. //Skip if we already suspended this thread:
  223. if (suspended_threads.contains(thread_entry.th32ThreadID))
  224. continue;
  225. //Open thread for context and suspend:
  226. HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, FALSE, thread_entry.th32ThreadID);
  227. //Suspend the thread:
  228. NktNtApi::SuspendThread(hThread);
  229. //Give thread processor until it leaves our segment:
  230. GetThreadContext(hThread, &thread_context);
  231. while (range.Contains((void*)thread_context.Eip) == 0)
  232. {
  233. ResumeThread(hThread);
  234. Sleep(0);
  235. NktNtApi::SuspendThread(hThread);
  236. GetThreadContext(hThread, &thread_context);
  237. }
  238. suspended_threads.push_back(thread_entry.th32ThreadID);
  239. CloseHandle(hThread);
  240. suspending = true;
  241. }
  242. while(Thread32Next(hSnapshot, &thread_entry));
  243. //Release snapshot:
  244. CloseHandle(hSnapshot);
  245. }
  246. }
  247. ///
  248. /// Utils::ResumeThreads
  249. ///
  250. void NktUtils::ResumeThreads(IN NktIdList& suspended_threads)
  251. {
  252. for (NktIdList::iterator it = suspended_threads.begin(); it != suspended_threads.end(); ++it)
  253. {
  254. DWORD thread_id = *it;
  255. HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, thread_id);
  256. ResumeThread(hThread);
  257. ::CloseHandle(hThread);//CloseHandle(hThread);
  258. }
  259. }
  260. ///
  261. /// Utils::CreateCall
  262. ///
  263. void NktUtils::CreateCall(const NktFunctionWrapper& fnc, OUT NktVirtualCode& code, LPCVOID pThis, bool useJmp)
  264. {
  265. int cc = fnc.GetCallConvention();
  266. USHORT cleanSize = fnc.GetCallerCleanSize();
  267. //Params:
  268. switch (cc)
  269. {
  270. //First param on Stack:
  271. case stdcall_:
  272. case cdecl_:
  273. {
  274. if (!pThis) break;
  275. }
  276. case thiscall_:
  277. {
  278. code.Append(PUSH_INM, (UINT)pThis);
  279. break;
  280. }
  281. //First param on ECX:
  282. case fastcall_:
  283. {
  284. if (!pThis) break;
  285. }
  286. case thiscall_ms_:
  287. {
  288. code.Append(MOV_ECX, (UINT)pThis);
  289. break;
  290. }
  291. default:
  292. NODEFAULT;
  293. }
  294. //Call:
  295. DV_PTR fncAddr = (DV_PTR)fnc.GetAddress();
  296. (!useJmp)? code.AppendCall(fncAddr) : code.AppendJump(fncAddr);
  297. //Clean:
  298. NktUtils::CreateClean(fnc, cleanSize, code);
  299. }
  300. NktVirtualCode NktUtils::CreateCall(const NktFunctionWrapper& fnc, LPCVOID pThis, bool useJmp)
  301. {
  302. NktVirtualCode code;
  303. CreateCall(fnc, code, pThis, useJmp);
  304. return code;
  305. }
  306. ///
  307. /// Utils::CreateClean
  308. ///
  309. void NktUtils::CreateClean(const NktFunctionWrapper& fnc, SIZE_T size, OUT NktVirtualCode& code)
  310. {
  311. int cc = fnc.GetCallConvention();
  312. switch(cc)
  313. {
  314. //Caller cleans
  315. case syscall_:
  316. case thiscall_:
  317. case cdecl_:
  318. {
  319. code.Append(ADD_ESP, (UCHAR)size);
  320. break;
  321. }
  322. //Callee cleans
  323. case thiscall_ms_:
  324. case stdcall_:
  325. case pascal_:
  326. case safecall_:
  327. case fastcall_:
  328. break;
  329. }
  330. }
  331. ///
  332. /// Utils::AppendJmp
  333. ///
  334. void NktUtils::AppendUpdatedJmp(NktVirtualCode& code, const void* fnc)
  335. {
  336. //Append overridden memory:
  337. SIZE_T size = code.Pivot();
  338. NktUtils::UpdateIndirections(code, (unsigned int)fnc, code.GetAddress());
  339. //Append jump to original function:
  340. code.AppendJump((DV_PTR)fnc + size);
  341. }
  342. ///
  343. /// Tools::GetThreadString
  344. ///
  345. #define EV_BASE_NAME (L"\BaseNamedObjects\DV_")
  346. int __fastcall NktUtils::GetThreadString(WCHAR* buffer)
  347. {
  348. _ASSERT(buffer);
  349. DWORD tid = NktNtApi::GetCurrentThreadId();
  350. wcscpy(buffer, EV_BASE_NAME);
  351. int length = wcslen(EV_BASE_NAME);
  352. length += base64_encode((unsigned char*)&tid, &buffer[length], sizeof(tid));
  353. buffer[length++] = '';
  354. return length;
  355. }
  356. /************************************************************************/
  357. /* Helpers                                                              */
  358. /************************************************************************/
  359. ///
  360. /// IsInstructionRelative
  361. ///
  362. BOOL IsInstructionRelative(const std::string& inst)
  363. {
  364. BOOL ret = FALSE;
  365. for (int i = 0; i < sizeof(RelativeInstructions)/sizeof(RelativeInstructions[0]) && !ret; ++i)
  366. {
  367. ret = inst.find(RelativeInstructions[i]) != std::string::npos;
  368. }
  369. return ret;
  370. }
  371. ///
  372. /// RawCallFunction
  373. ///
  374. INT_PTR RawCallFunction(LPCVOID f, NktCallingConvention cc, const NktCallContext& ctx, SIZE_T stackBytes, SIZE_T cleanSize)
  375. {
  376. _ASSERT(f && ctx.regs);
  377. PINT_PTR rECX, rEDX, rEAX, rEFL, rError, params;
  378. NktRegisters* registers = ctx.regs;
  379. params = (PINT_PTR)ctx.pms;
  380. rEAX = &registers->EAX;
  381. rECX = &registers->ECX;
  382. rEDX = &registers->EDX;
  383. rEFL  = &registers->EFLAGS;
  384. rError = &registers->lastError;
  385. _asm
  386. {
  387. //prelude:
  388. push ecx
  389. push esi
  390. push edi
  391. //Load params:
  392. mov esi, [params]
  393. test esi, esi
  394. jz _asm_stack_loaded
  395. mov ecx, [stackBytes]
  396. sub esp, ecx
  397. shr ecx, 2
  398. mov esi, [params]
  399. mov edi, esp
  400. rep movsd
  401. _asm_stack_loaded:
  402. //Load ECX:
  403. mov esi, [rECX]
  404. mov ecx, [esi]
  405. //Load EDX:
  406. mov esi, [rEDX]
  407. mov edx, [esi]
  408. //Restore Thread Last Error:
  409. mov esi, rError
  410. mov esi, [esi]
  411. mov edi, fs:18h
  412. mov [edi+34h], esi
  413. //Call
  414. mov eax, [f]
  415. call eax
  416. //Store EAX(return):
  417. mov edi, rEAX
  418. mov [edi], eax
  419. //Store FLAGS(return):
  420. pushfd
  421. pop eax
  422. mov edi, rEFL
  423. mov [edi], eax
  424. //Store Thread Last Error:
  425. mov eax, fs:18h
  426. mov eax, [eax+34h]
  427. mov edi, rError
  428. mov [edi], eax
  429. _asm_epilogue:
  430. //Clear stacked params
  431. // *add 0 to esp seems to be a better choice than testing if it's necessary on every case...
  432. add esp, cleanSize
  433. //Restore registers and return
  434. pop edi
  435. pop esi
  436. pop ecx
  437. //ret
  438. }
  439. }