service_resolver_32.cc 15 KB

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