PageRenderTime 30ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/sandbox/win/src/service_resolver_32.cc

https://gitlab.com/jonnialva90/iridium-browser
C++ | 424 lines | 293 code | 70 blank | 61 comment | 90 complexity | a2c6a5b1e66e11bd1abb5b20d68b1949 MD5 | raw file
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "sandbox/win/src/service_resolver.h"
  5. #include "base/memory/scoped_ptr.h"
  6. #include "sandbox/win/src/win_utils.h"
  7. namespace {
  8. #pragma pack(push, 1)
  9. const BYTE kMovEax = 0xB8;
  10. const BYTE kMovEdx = 0xBA;
  11. const USHORT kMovEdxEsp = 0xD48B;
  12. const USHORT kCallPtrEdx = 0x12FF;
  13. const USHORT kCallEdx = 0xD2FF;
  14. const BYTE kCallEip = 0xE8;
  15. const BYTE kRet = 0xC2;
  16. const BYTE kRet2 = 0xC3;
  17. const USHORT kJmpEdx = 0xE2FF;
  18. const USHORT kXorEcx = 0xC933;
  19. const ULONG kLeaEdx = 0x0424548D;
  20. const ULONG kCallFs1 = 0xC015FF64;
  21. const USHORT kCallFs2 = 0;
  22. const BYTE kCallFs3 = 0;
  23. const BYTE kAddEsp1 = 0x83;
  24. const USHORT kAddEsp2 = 0x4C4;
  25. const BYTE kJmp32 = 0xE9;
  26. const USHORT kSysenter = 0x340F;
  27. // Service code for 32 bit systems.
  28. // NOTE: on win2003 "call dword ptr [edx]" is "call edx".
  29. struct ServiceEntry {
  30. // This struct contains roughly the following code:
  31. // 00 mov eax,25h
  32. // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
  33. // 0a call dword ptr [edx]
  34. // 0c ret 2Ch
  35. // 0f nop
  36. BYTE mov_eax; // = B8
  37. ULONG service_id;
  38. BYTE mov_edx; // = BA
  39. ULONG stub;
  40. USHORT call_ptr_edx; // = FF 12
  41. BYTE ret; // = C2
  42. USHORT num_params;
  43. BYTE nop;
  44. };
  45. // Service code for 32 bit Windows 8.
  46. struct ServiceEntryW8 {
  47. // This struct contains the following code:
  48. // 00 b825000000 mov eax,25h
  49. // 05 e803000000 call eip+3
  50. // 0a c22c00 ret 2Ch
  51. // 0d 8bd4 mov edx,esp
  52. // 0f 0f34 sysenter
  53. // 11 c3 ret
  54. // 12 8bff mov edi,edi
  55. BYTE mov_eax; // = B8
  56. ULONG service_id;
  57. BYTE call_eip; // = E8
  58. ULONG call_offset;
  59. BYTE ret_p; // = C2
  60. USHORT num_params;
  61. USHORT mov_edx_esp; // = BD D4
  62. USHORT sysenter; // = 0F 34
  63. BYTE ret; // = C3
  64. USHORT nop;
  65. };
  66. // Service code for a 32 bit process running on a 64 bit os.
  67. struct Wow64Entry {
  68. // This struct may contain one of two versions of code:
  69. // 1. For XP, Vista and 2K3:
  70. // 00 b825000000 mov eax, 25h
  71. // 05 33c9 xor ecx, ecx
  72. // 07 8d542404 lea edx, [esp + 4]
  73. // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
  74. // 12 c22c00 ret 2Ch
  75. //
  76. // 2. For Windows 7:
  77. // 00 b825000000 mov eax, 25h
  78. // 05 33c9 xor ecx, ecx
  79. // 07 8d542404 lea edx, [esp + 4]
  80. // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
  81. // 12 83c404 add esp, 4
  82. // 15 c22c00 ret 2Ch
  83. //
  84. // So we base the structure on the bigger one:
  85. BYTE mov_eax; // = B8
  86. ULONG service_id;
  87. USHORT xor_ecx; // = 33 C9
  88. ULONG lea_edx; // = 8D 54 24 04
  89. ULONG call_fs1; // = 64 FF 15 C0
  90. USHORT call_fs2; // = 00 00
  91. BYTE call_fs3; // = 00
  92. BYTE add_esp1; // = 83 or ret
  93. USHORT add_esp2; // = C4 04 or num_params
  94. BYTE ret; // = C2
  95. USHORT num_params;
  96. };
  97. // Service code for a 32 bit process running on 64 bit Windows 8.
  98. struct Wow64EntryW8 {
  99. // 00 b825000000 mov eax, 25h
  100. // 05 64ff15c0000000 call dword ptr fs:[0C0h]
  101. // 0b c22c00 ret 2Ch
  102. // 0f 90 nop
  103. BYTE mov_eax; // = B8
  104. ULONG service_id;
  105. ULONG call_fs1; // = 64 FF 15 C0
  106. USHORT call_fs2; // = 00 00
  107. BYTE call_fs3; // = 00
  108. BYTE ret; // = C2
  109. USHORT num_params;
  110. BYTE nop;
  111. };
  112. // Make sure that relaxed patching works as expected.
  113. const size_t kMinServiceSize = offsetof(ServiceEntry, ret);
  114. static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize,
  115. "wrong service length");
  116. static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length");
  117. static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length");
  118. struct ServiceFullThunk {
  119. union {
  120. ServiceEntry original;
  121. ServiceEntryW8 original_w8;
  122. Wow64Entry wow_64;
  123. Wow64EntryW8 wow_64_w8;
  124. };
  125. int internal_thunk; // Dummy member to the beginning of the internal thunk.
  126. };
  127. #pragma pack(pop)
  128. }; // namespace
  129. namespace sandbox {
  130. NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
  131. const void* interceptor_module,
  132. const char* target_name,
  133. const char* interceptor_name,
  134. const void* interceptor_entry_point,
  135. void* thunk_storage,
  136. size_t storage_bytes,
  137. size_t* storage_used) {
  138. NTSTATUS ret = Init(target_module, interceptor_module, target_name,
  139. interceptor_name, interceptor_entry_point,
  140. thunk_storage, storage_bytes);
  141. if (!NT_SUCCESS(ret))
  142. return ret;
  143. relative_jump_ = 0;
  144. size_t thunk_bytes = GetThunkSize();
  145. scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
  146. ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
  147. thunk_buffer.get());
  148. if (!IsFunctionAService(&thunk->original) &&
  149. (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage)))
  150. return STATUS_UNSUCCESSFUL;
  151. ret = PerformPatch(thunk, thunk_storage);
  152. if (NULL != storage_used)
  153. *storage_used = thunk_bytes;
  154. return ret;
  155. }
  156. size_t ServiceResolverThunk::GetThunkSize() const {
  157. return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
  158. }
  159. NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
  160. const char* target_name,
  161. BYTE* thunk_storage,
  162. size_t storage_bytes,
  163. size_t* storage_used) {
  164. NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
  165. if (!NT_SUCCESS(ret))
  166. return ret;
  167. size_t thunk_bytes = GetThunkSize();
  168. if (storage_bytes < thunk_bytes)
  169. return STATUS_UNSUCCESSFUL;
  170. ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
  171. if (!IsFunctionAService(&thunk->original) &&
  172. (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
  173. return STATUS_UNSUCCESSFUL;
  174. }
  175. if (NULL != storage_used)
  176. *storage_used = thunk_bytes;
  177. return ret;
  178. }
  179. bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
  180. ServiceEntry function_code;
  181. SIZE_T read;
  182. if (!::ReadProcessMemory(process_, target_, &function_code,
  183. sizeof(function_code), &read))
  184. return false;
  185. if (sizeof(function_code) != read)
  186. return false;
  187. if (kMovEax != function_code.mov_eax ||
  188. kMovEdx != function_code.mov_edx ||
  189. (kCallPtrEdx != function_code.call_ptr_edx &&
  190. kCallEdx != function_code.call_ptr_edx) ||
  191. kRet != function_code.ret)
  192. return false;
  193. // Find the system call pointer if we don't already have it.
  194. if (kCallEdx != function_code.call_ptr_edx) {
  195. DWORD ki_system_call;
  196. if (!::ReadProcessMemory(process_,
  197. bit_cast<const void*>(function_code.stub),
  198. &ki_system_call, sizeof(ki_system_call), &read))
  199. return false;
  200. if (sizeof(ki_system_call) != read)
  201. return false;
  202. HMODULE module_1, module_2;
  203. // last check, call_stub should point to a KiXXSystemCall function on ntdll
  204. if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
  205. GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
  206. bit_cast<const wchar_t*>(ki_system_call), &module_1))
  207. return false;
  208. if (NULL != ntdll_base_) {
  209. // This path is only taken when running the unit tests. We want to be
  210. // able to patch a buffer in memory, so target_ is not inside ntdll.
  211. module_2 = ntdll_base_;
  212. } else {
  213. if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
  214. GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
  215. reinterpret_cast<const wchar_t*>(target_),
  216. &module_2))
  217. return false;
  218. }
  219. if (module_1 != module_2)
  220. return false;
  221. }
  222. // Save the verified code
  223. memcpy(local_thunk, &function_code, sizeof(function_code));
  224. return true;
  225. }
  226. NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
  227. void* remote_thunk) {
  228. ServiceEntry intercepted_code;
  229. size_t bytes_to_write = sizeof(intercepted_code);
  230. ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
  231. local_thunk);
  232. ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
  233. remote_thunk);
  234. // patch the original code
  235. memcpy(&intercepted_code, &full_local_thunk->original,
  236. sizeof(intercepted_code));
  237. intercepted_code.mov_eax = kMovEax;
  238. intercepted_code.service_id = full_local_thunk->original.service_id;
  239. intercepted_code.mov_edx = kMovEdx;
  240. intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
  241. intercepted_code.call_ptr_edx = kJmpEdx;
  242. bytes_to_write = kMinServiceSize;
  243. if (relative_jump_) {
  244. intercepted_code.mov_eax = kJmp32;
  245. intercepted_code.service_id = relative_jump_;
  246. bytes_to_write = offsetof(ServiceEntry, mov_edx);
  247. }
  248. // setup the thunk
  249. SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
  250. remote_thunk, interceptor_);
  251. size_t thunk_size = GetThunkSize();
  252. // copy the local thunk buffer to the child
  253. SIZE_T written;
  254. if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
  255. thunk_size, &written))
  256. return STATUS_UNSUCCESSFUL;
  257. if (thunk_size != written)
  258. return STATUS_UNSUCCESSFUL;
  259. // and now change the function to intercept, on the child
  260. if (NULL != ntdll_base_) {
  261. // running a unit test
  262. if (!::WriteProcessMemory(process_, target_, &intercepted_code,
  263. bytes_to_write, &written))
  264. return STATUS_UNSUCCESSFUL;
  265. } else {
  266. if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
  267. bytes_to_write))
  268. return STATUS_UNSUCCESSFUL;
  269. }
  270. return STATUS_SUCCESS;
  271. }
  272. bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
  273. void* remote_thunk) {
  274. ServiceEntry function_code;
  275. SIZE_T read;
  276. if (!::ReadProcessMemory(process_, target_, &function_code,
  277. sizeof(function_code), &read))
  278. return false;
  279. if (sizeof(function_code) != read)
  280. return false;
  281. if (kJmp32 == function_code.mov_eax) {
  282. // Plain old entry point patch. The relative jump address follows it.
  283. ULONG relative = function_code.service_id;
  284. // First, fix our copy of their patch.
  285. relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
  286. function_code.service_id = relative;
  287. // And now, remember how to re-patch it.
  288. ServiceFullThunk *full_thunk =
  289. reinterpret_cast<ServiceFullThunk*>(remote_thunk);
  290. const ULONG kJmp32Size = 5;
  291. relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
  292. bit_cast<ULONG>(target_) - kJmp32Size;
  293. }
  294. // Save the verified code
  295. memcpy(local_thunk, &function_code, sizeof(function_code));
  296. return true;
  297. }
  298. bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
  299. Wow64Entry function_code;
  300. SIZE_T read;
  301. if (!::ReadProcessMemory(process_, target_, &function_code,
  302. sizeof(function_code), &read))
  303. return false;
  304. if (sizeof(function_code) != read)
  305. return false;
  306. if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx ||
  307. kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
  308. kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3)
  309. return false;
  310. if ((kAddEsp1 == function_code.add_esp1 &&
  311. kAddEsp2 == function_code.add_esp2 &&
  312. kRet == function_code.ret) || kRet == function_code.add_esp1) {
  313. // Save the verified code
  314. memcpy(local_thunk, &function_code, sizeof(function_code));
  315. return true;
  316. }
  317. return false;
  318. }
  319. bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
  320. Wow64EntryW8 function_code;
  321. SIZE_T read;
  322. if (!::ReadProcessMemory(process_, target_, &function_code,
  323. sizeof(function_code), &read))
  324. return false;
  325. if (sizeof(function_code) != read)
  326. return false;
  327. if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 ||
  328. kCallFs2 != function_code.call_fs2 ||
  329. kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
  330. return false;
  331. }
  332. // Save the verified code
  333. memcpy(local_thunk, &function_code, sizeof(function_code));
  334. return true;
  335. }
  336. bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
  337. ServiceEntryW8 function_code;
  338. SIZE_T read;
  339. if (!::ReadProcessMemory(process_, target_, &function_code,
  340. sizeof(function_code), &read))
  341. return false;
  342. if (sizeof(function_code) != read)
  343. return false;
  344. if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip ||
  345. function_code.call_offset != 3 || kRet != function_code.ret_p ||
  346. kMovEdxEsp != function_code.mov_edx_esp ||
  347. kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
  348. return false;
  349. }
  350. // Save the verified code
  351. memcpy(local_thunk, &function_code, sizeof(function_code));
  352. return true;
  353. }
  354. } // namespace sandbox