Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

891 lines
18 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. win.c
  5. Abstract:
  6. This module exports windows specific apis
  7. Algorithm:
  8. Unfortunately, implementing OpenThread cannot be done in a simple
  9. manner. What follows is a very system dependent hack. If the structure
  10. of the TDB or the implementation of OpenProcess change very much, this
  11. function will break.
  12. To have any idea of what we are doing here, you should be familiar with
  13. Win9x internals. If you are not familiar with the Win9x source, consult
  14. the book "Windows 95 System Programming SECRETS" by Matt Pietrek. Things
  15. are not exactly the same for Win98 -- but pretty close.
  16. OpenThread is a very simple function. If we were compiled withing the
  17. Win9x source code base, the code would be simple:
  18. OpenThread:
  19. pObj = TidToTDB (dwThreadId);
  20. return AllocHandle (GetCurrentPdb(), pObj, Flags);
  21. Since we are not, the challenge is implementing the functions TidToTDB()
  22. and AllocHandle().
  23. Our approach is as follows:
  24. 1) We reverse-engineer TidToTDB since it is simple. TidToTDB is just
  25. the thread-id xor'd with the Win9x Obfuscator.
  26. 2) We search through the code of OpenProcess until we find the address
  27. of AllocHandle. We use this to allocate new handles in the
  28. process's handle database.
  29. 3) OpenThread is then implemented in terms of the above primitives.
  30. Author:
  31. Matthew D Hendel (math) 01-Sept-1999
  32. Revision History:
  33. --*/
  34. #include "pch.h"
  35. #include "impl.h"
  36. //
  37. // Windows 9x support is x86 only.
  38. //
  39. #ifdef _X86_
  40. typedef struct _MATCH_BUFFER {
  41. ULONG Offset;
  42. BYTE Byte;
  43. } MATCH_BUFFER, *PMATCH_BUFFER;
  44. typedef struct _OS_INFORMATION {
  45. PMATCH_BUFFER MatchBuffer;
  46. ULONG AllocHandleOffset;
  47. } OS_INFORMATION, POS_INFORMATION;
  48. /*++
  49. Operating System:
  50. Win95
  51. Description:
  52. This is the disasm of the OpenProcess routine on Win95. We attempt to
  53. match this routine and pull out the value for AllocHandle from the code
  54. for this routine. In this case, AllocHande is called by the third call in
  55. this function.
  56. The instructions marked by '*' are those we use for matching.
  57. OpenProcess:
  58. * BFF9404C: FF 74 24 0C push dword ptr [esp+0Ch]
  59. * BFF94050: E8 2D 87 FE FF call BFF7C782
  60. BFF94055: 85 C0 test eax,eax
  61. BFF94057: 75 04 jne BFF9405D
  62. BFF94059: 33 C0 xor eax,eax
  63. BFF9405B: EB 56 jmp BFF940B3
  64. BFF9405D: 83 38 05 cmp dword ptr [eax],5
  65. BFF94060: 74 0E je BFF94070
  66. BFF94062: 6A 57 push 57h
  67. * BFF94064: E8 BC 68 FE FF call BFF7A925
  68. BFF94069: B9 FF FF FF FF mov ecx,0FFFFFFFFh
  69. BFF9406E: EB 33 jmp BFF940A3
  70. BFF94070: B9 00 00 00 00 mov ecx,0
  71. BFF94075: 8B 54 24 04 mov edx,dword ptr [esp+4]
  72. BFF94079: 83 7C 24 08 01 cmp dword ptr [esp+8],1
  73. BFF9407E: 83 D1 FF adc ecx,0FFFFFFFFh
  74. BFF94081: 81 E2 BF FF 1F 00 and edx,1FFFBFh
  75. BFF94087: 81 E1 00 00 00 80 and ecx,80000000h
  76. BFF9408D: 0B CA or ecx,edx
  77. BFF9408F: 8B 15 7C C2 FB BF mov edx,dword ptr ds:[BFFBC27Ch]
  78. BFF94095: 80 C9 40 or cl,40h
  79. BFF94098: 51 push ecx
  80. BFF94099: 50 push eax
  81. BFF9409A: FF 32 push dword ptr [edx]
  82. * BFF9409C: E8 6E 76 FE FF call BFF7B70F
  83. BFF940A1: 8B C8 mov ecx,eax
  84. BFF940A3: 8D 41 01 lea eax,[ecx+1]
  85. BFF940A6: 83 F8 01 cmp eax,1
  86. BFF940A9: B8 00 00 00 00 mov eax,0
  87. BFF940AE: 83 D0 FF adc eax,0FFFFFFFFh
  88. BFF940B1: 23 C1 and eax,ecx
  89. BFF940B3: C2 0C 00 ret 0Ch
  90. --*/
  91. MATCH_BUFFER Win95AllocHandleMatch [] = {
  92. //
  93. // ret 0x0C at offset 103
  94. //
  95. { 103, 0xC2 },
  96. { 104, 0x0C },
  97. { 105, 0x00 },
  98. //
  99. // push dword ptr [exp 0x0C] at offset 0
  100. //
  101. { 0, 0xFF },
  102. { 1, 0x74 },
  103. { 2, 0x24 },
  104. { 3, 0x0C },
  105. //
  106. // call at offset 4
  107. //
  108. { 4, 0xE8 },
  109. //
  110. // call at offset 24
  111. //
  112. { 24, 0xE8 },
  113. //
  114. // call at offset 80
  115. //
  116. { 80, 0xE8 },
  117. //
  118. // End of match list.
  119. //
  120. { -1, -1 }
  121. };
  122. /*++
  123. Operating system:
  124. Win98
  125. Description:
  126. See comments above regarding OpenProcess.
  127. OpenProcess:
  128. * BFF95C4D: FF 74 24 0C push dword ptr [esp+0Ch]
  129. * BFF95C51: E8 C9 8E FE FF call BFF7EB1F
  130. BFF95C56: 85 C0 test eax,eax
  131. BFF95C58: 75 04 jne BFF95C5E
  132. BFF95C5A: 33 C0 xor eax,eax
  133. BFF95C5C: EB 53 jmp BFF95CB1
  134. BFF95C5E: 80 38 06 cmp byte ptr [eax],6
  135. BFF95C61: 74 0E je BFF95C71
  136. BFF95C63: 6A 57 push 57h
  137. * BFF95C65: E8 27 6D FE FF call BFF7C991
  138. BFF95C6A: B9 FF FF FF FF mov ecx,0FFFFFFFFh
  139. BFF95C6F: EB 30 jmp BFF95CA1
  140. BFF95C71: B9 00 00 00 00 mov ecx,0
  141. BFF95C76: 8B 54 24 04 mov edx,dword ptr [esp+4]
  142. BFF95C7A: 83 7C 24 08 01 cmp dword ptr [esp+8],1
  143. BFF95C7F: 83 D1 FF adc ecx,0FFFFFFFFh
  144. BFF95C82: 81 E2 FF 0F 1F 00 and edx,1F0FFFh
  145. BFF95C88: 81 E1 00 00 00 80 and ecx,80000000h
  146. BFF95C8E: 0B CA or ecx,edx
  147. BFF95C90: 8B 15 DC 9C FC BF mov edx,dword ptr ds:[BFFC9CDCh]
  148. BFF95C96: 51 push ecx
  149. BFF95C97: 50 push eax
  150. BFF95C98: FF 32 push dword ptr [edx]
  151. * BFF95C9A: E8 5A 7E FE FF call BFF7DAF9
  152. BFF95C9F: 8B C8 mov ecx,eax
  153. BFF95CA1: 8D 41 01 lea eax,[ecx+1]
  154. BFF95CA4: 83 F8 01 cmp eax,1
  155. BFF95CA7: B8 00 00 00 00 mov eax,0
  156. BFF95CAC: 83 D0 FF adc eax,0FFFFFFFFh
  157. BFF95CAF: 23 C1 and eax,ecx
  158. * BFF95CB1: C2 0C 00 ret 0Ch
  159. --*/
  160. MATCH_BUFFER Win98AllocHandleMatch [] = {
  161. //
  162. // ret 0x0C at offset 100
  163. //
  164. { 100, 0xC2 },
  165. { 101, 0x0C },
  166. { 102, 0x00 },
  167. //
  168. // push dword ptr [exp 0x0C] at offset 0
  169. //
  170. { 0, 0xFF },
  171. { 1, 0x74 },
  172. { 2, 0x24 },
  173. { 3, 0x0C },
  174. //
  175. // call at offset 4
  176. //
  177. { 4, 0xE8 },
  178. //
  179. // call at offset 24
  180. //
  181. { 24, 0xE8 },
  182. //
  183. // call at offset 77
  184. //
  185. { 77, 0xE8 },
  186. //
  187. // End of match list.
  188. //
  189. { -1, -1 }
  190. };
  191. OS_INFORMATION SupportedSystems [] =
  192. {
  193. { Win95AllocHandleMatch, 81 },
  194. { Win98AllocHandleMatch, 78 }
  195. };
  196. typedef
  197. HANDLE
  198. (__stdcall * ALLOC_HANDLE_ROUTINE) (
  199. PVOID Pdb,
  200. PVOID Obj,
  201. DWORD Flags
  202. );
  203. //
  204. // Global variables
  205. //
  206. ALLOC_HANDLE_ROUTINE WinpAllocHandle = NULL;
  207. DWORD WinpObfuscator = 0;
  208. #pragma warning (disable:4035)
  209. //
  210. // OffsetTib is NOT dependent on the OS. The compiler uses this value.
  211. //
  212. #define OffsetTib 0x18
  213. _inline
  214. PVOID
  215. WinpGetCurrentTib(
  216. )
  217. {
  218. #if defined(_X86_)
  219. __asm mov eax, fs:[OffsetTib]
  220. #else
  221. return NULL;
  222. #endif
  223. }
  224. #pragma warning (default:4035)
  225. BOOL
  226. WinpGetAllocHandleFromStream(
  227. IN PBYTE Buffer,
  228. IN PVOID BaseOfBuffer,
  229. IN PMATCH_BUFFER MatchBuffer,
  230. IN ULONG Offset,
  231. IN ULONG * Val
  232. )
  233. /*++
  234. Routine Description:
  235. Find the address of the AllocHandle routine. This is done by searching
  236. through the code of the OpenProcess routine, looking for the third
  237. call instruction in that function. The third call calls AllocHandle().
  238. Arguments:
  239. Buffer - Buffer of instructions to search through.
  240. BaseOfBuffer - The base address of the buffer.
  241. MatchBuffer - The match buffer to compare against.
  242. Offset - The offset of call destination.
  243. Val - A buffer to return the value of AllocHandle.
  244. Return Values:
  245. TRUE - Success.
  246. FALSE - Failure.
  247. --*/
  248. {
  249. UINT i;
  250. for (i = 0; MatchBuffer [i].Offset != -1; i++) {
  251. if (Buffer [MatchBuffer[i].Offset] != MatchBuffer[i].Byte) {
  252. return FALSE;
  253. }
  254. }
  255. //
  256. // This assumes that the call instruction is a near, relative call (E8).
  257. // If this is not the case, the calculation below is incorrect.
  258. //
  259. // The calculation gives us the destination relative to the next
  260. // instruction after the call.
  261. //
  262. *Val = (ULONG) BaseOfBuffer + Offset + *(PLONG) &Buffer [Offset] + 4;
  263. return TRUE;
  264. }
  265. ULONG
  266. WinGetModuleSize(
  267. PVOID Base
  268. )
  269. /*++
  270. Routine Description:
  271. Get the SizeOfImage field given the base address of a module.
  272. Return Values:
  273. SizeOfImage field of the specified module on success.
  274. NULL on failure.
  275. --*/
  276. {
  277. ULONG Size;
  278. PIMAGE_NT_HEADERS NtHeaders;
  279. NtHeaders = ImageNtHeader ( Base );
  280. if ( NtHeaders ) {
  281. Size = NtHeaders->OptionalHeader.SizeOfImage;
  282. } else {
  283. Size = 0;
  284. }
  285. return Size;
  286. }
  287. BOOL
  288. WinpInitAllocHandle (
  289. )
  290. /*++
  291. Routine Description:
  292. Initialize the global variable WxAllocHandle to the value of the Win9x
  293. internal routine, AllocHandle.
  294. Arguments:
  295. None
  296. Return Values:
  297. TRUE - If we were able to successfully obtain a pointer to AllocHandle.
  298. FALSE - Otherwise.
  299. Comments:
  300. The client of this routine should verify that this handle is correct by
  301. calling WxCheckOpenThread() before blindly assuming the pointer is
  302. correct.
  303. --*/
  304. {
  305. ULONG i;
  306. BOOL Succ;
  307. PVOID OpenProcessPtr;
  308. ULONG Kernel32Base;
  309. ULONG Kernel32Size;
  310. ULONG AllocHandle;
  311. BYTE Buffer [ 200 ];
  312. if ( WinpAllocHandle ) {
  313. return TRUE;
  314. }
  315. Kernel32Base = (ULONG) GetModuleHandle ( "kernel32.dll" );
  316. ASSERT ( Kernel32Base );
  317. if (!Kernel32Base)
  318. {
  319. return FALSE;
  320. }
  321. Kernel32Size = WinGetModuleSize ( (PVOID) Kernel32Base );
  322. ASSERT ( Kernel32Size != 0 );
  323. OpenProcessPtr = GetProcAddress (
  324. (HINSTANCE) Kernel32Base,
  325. "OpenProcess"
  326. );
  327. if (!OpenProcessPtr)
  328. {
  329. return FALSE;
  330. }
  331. //
  332. // Win9x thunks out functions when a debugger is present. To work around
  333. // this we undo the thunk when it looks like its been thunked.
  334. //
  335. if ( (ULONG) OpenProcessPtr < Kernel32Base ||
  336. (ULONG) OpenProcessPtr > Kernel32Base + Kernel32Size ) {
  337. OpenProcessPtr = (PVOID) *(PULONG)( (PBYTE)OpenProcessPtr + 1 );
  338. }
  339. if ( (ULONG) OpenProcessPtr < Kernel32Base ||
  340. (ULONG) OpenProcessPtr > Kernel32Base + Kernel32Size ) {
  341. return FALSE;
  342. }
  343. CopyMemory (Buffer, OpenProcessPtr, sizeof (Buffer));
  344. //
  345. // Check the buffer
  346. //
  347. for ( i = 0; i < ARRAY_COUNT (SupportedSystems); i++) {
  348. Succ = WinpGetAllocHandleFromStream (
  349. Buffer,
  350. OpenProcessPtr,
  351. SupportedSystems[i].MatchBuffer,
  352. SupportedSystems[i].AllocHandleOffset,
  353. &AllocHandle
  354. );
  355. if ( Succ ) {
  356. //
  357. // Verify WinpAllocHandle within range of Kernel32.
  358. //
  359. if (AllocHandle > Kernel32Base &&
  360. AllocHandle < Kernel32Base + Kernel32Size) {
  361. WinpAllocHandle = (ALLOC_HANDLE_ROUTINE) AllocHandle;
  362. break;
  363. }
  364. }
  365. }
  366. if ( !Succ ) {
  367. WinpAllocHandle = NULL;
  368. }
  369. return Succ;
  370. }
  371. //
  372. // This value is basically FIELD_OFFSET (TDB, Tib). It is dependent on the
  373. // specific version of the OS (95, 98).
  374. //
  375. #define WIN95_TDB_OFFSET (0x10)
  376. #define WIN98_TDB_OFFSET (0x08)
  377. DWORD
  378. WinpGetObfuscator(
  379. )
  380. /*++
  381. Routine Description:
  382. Get the Obfuscator DWORD.
  383. Arguments:
  384. None.
  385. Return Values:
  386. The Obfuscator or 0 on failure.
  387. Comments:
  388. This routine depends on internal structures from the Win9x sources. If
  389. another major revision of windows changes many of these structures, this
  390. function may break.
  391. --*/
  392. {
  393. ULONG Tib;
  394. ULONG Type;
  395. ULONG Major;
  396. if (WinpObfuscator != 0) {
  397. return WinpObfuscator;
  398. }
  399. GenGetSystemType (&Type, &Major, NULL, NULL, NULL);
  400. ASSERT ( Type == Win9x );
  401. Tib = (DWORD)WinpGetCurrentTib ();
  402. if ( Major == 95 ) {
  403. WinpObfuscator = (GetCurrentThreadId () ^ (Tib - WIN95_TDB_OFFSET));
  404. } else {
  405. //
  406. // If a windows-based system that is not 95 or 98 comes along,
  407. // we should make sure the WINxx_TDB_OFFSET is correct.
  408. //
  409. ASSERT ( Major == 98 );
  410. WinpObfuscator = (GetCurrentThreadId () ^ (Tib - WIN98_TDB_OFFSET));
  411. }
  412. return WinpObfuscator;
  413. }
  414. LPVOID
  415. WinpTidToTDB(
  416. IN DWORD ThreadId
  417. )
  418. {
  419. return (PVOID) (ThreadId ^ WinpGetObfuscator ());
  420. }
  421. LPVOID
  422. WinpGetCurrentPdb(
  423. )
  424. {
  425. return (LPVOID) (GetCurrentProcessId () ^ WinpGetObfuscator ());
  426. }
  427. HANDLE
  428. WinpOpenThreadInternal(
  429. DWORD dwAccess,
  430. BOOL bInheritHandle,
  431. DWORD ThreadId
  432. )
  433. {
  434. HANDLE hThread;
  435. PVOID ThreadObj;
  436. ASSERT (WinpAllocHandle);
  437. //
  438. // Convert the ThreadId to a Thread Object
  439. //
  440. ThreadObj = WinpTidToTDB (ThreadId);
  441. if (ThreadObj == NULL) {
  442. return NULL;
  443. }
  444. //
  445. // NB: we do not check that the handle really is a thread handle.
  446. // The type varies from version to version of the OS, so it is not
  447. // correct to check it.
  448. //
  449. try {
  450. hThread = WinpAllocHandle (
  451. WinpGetCurrentPdb (),
  452. ThreadObj,
  453. dwAccess
  454. );
  455. }
  456. except (EXCEPTION_EXECUTE_HANDLER) {
  457. hThread = NULL;
  458. }
  459. if (hThread == (HANDLE) (-1)) {
  460. hThread = NULL;
  461. }
  462. return hThread;
  463. }
  464. #if _MSC_FULL_VER >= 13008827
  465. #pragma warning(push)
  466. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  467. #endif
  468. DWORD
  469. WINAPI
  470. WinpCheckThread(
  471. PVOID unused
  472. )
  473. {
  474. for (;;) {
  475. }
  476. return 0;
  477. }
  478. #if _MSC_FULL_VER >= 13008827
  479. #pragma warning(pop)
  480. #endif
  481. BOOL
  482. WinpCheckOpenThread(
  483. )
  484. /*++
  485. Routine Description:
  486. Check that WxOpenThread actually works.
  487. Arguments:
  488. None.
  489. Return Values:
  490. TRUE - If WxOpenThread works properly.
  491. FALSE - Otherwise.
  492. --*/
  493. {
  494. BOOL Succ;
  495. HANDLE hThread1;
  496. HANDLE hThread2;
  497. DWORD ThreadId;
  498. CONTEXT Context1;
  499. CONTEXT Context2;
  500. LONG SuspendCount;
  501. SuspendCount = 0;
  502. hThread1 = NULL;
  503. hThread2 = NULL;
  504. hThread1 = CreateThread (NULL,
  505. 0,
  506. WinpCheckThread,
  507. 0,
  508. 0,
  509. &ThreadId
  510. );
  511. if ( hThread1 == NULL ) {
  512. return FALSE;
  513. }
  514. hThread2 = WinpOpenThreadInternal (
  515. THREAD_ALL_ACCESS,
  516. FALSE,
  517. ThreadId
  518. );
  519. if ( hThread2 == NULL ) {
  520. Succ = FALSE;
  521. goto Exit;
  522. }
  523. Succ = TRUE;
  524. try {
  525. //
  526. // First we check that we can suspend the thread. If that is
  527. // successful, then get the context using the read thread
  528. // handle and the newly opened thread handle and check that
  529. // they are the same.
  530. //
  531. SuspendCount = SuspendThread ( hThread2 );
  532. if ( SuspendCount == -1 ) {
  533. Succ = FALSE;
  534. leave;
  535. }
  536. Context1.ContextFlags = CONTEXT_FULL;
  537. Succ = GetThreadContext ( hThread2, &Context1 );
  538. if ( !Succ ) {
  539. leave;
  540. }
  541. Context2.ContextFlags = CONTEXT_FULL;
  542. Succ = GetThreadContext ( hThread1, &Context2 );
  543. if ( !Succ ) {
  544. leave;
  545. }
  546. if ( Context1.Eip != Context2.Eip ) {
  547. Succ = FALSE;
  548. leave;
  549. }
  550. }
  551. except ( EXCEPTION_EXECUTE_HANDLER ) {
  552. Succ = FALSE;
  553. }
  554. Exit:
  555. if ( SuspendCount > 0 ) {
  556. ResumeThread ( hThread2 );
  557. }
  558. TerminateThread ( hThread1, 0xDEAD );
  559. if ( hThread1 ) {
  560. CloseHandle ( hThread1 );
  561. }
  562. if ( hThread2 ) {
  563. CloseHandle ( hThread2 );
  564. }
  565. return Succ;
  566. }
  567. BOOL
  568. WinInitialize(
  569. )
  570. {
  571. if ( WinpAllocHandle == NULL ) {
  572. if (!WinpInitAllocHandle ()) {
  573. SetLastError (ERROR_NOT_SUPPORTED);
  574. return FALSE;
  575. }
  576. if (!WinpCheckOpenThread ()) {
  577. SetLastError (ERROR_NOT_SUPPORTED);
  578. return FALSE;
  579. }
  580. }
  581. return TRUE;
  582. }
  583. VOID
  584. WinFree(
  585. )
  586. {
  587. WinpAllocHandle = NULL;
  588. WinpObfuscator = 0;
  589. }
  590. HANDLE
  591. WINAPI
  592. WinOpenThread(
  593. DWORD dwAccess,
  594. BOOL bInheritHandle,
  595. DWORD ThreadId
  596. )
  597. /*++
  598. Routine Description:
  599. Obtain a thread handle from a thread id on Win9x platform.x
  600. Arguments:
  601. dwAccess - Thread access requested.
  602. bInheritHandle - ALWAYS IGNORED.
  603. ThreadId - The identifier of the thread for which a handle is to
  604. be returned.
  605. Return Values:
  606. A handle to the open thread on success or NULL on failure.
  607. --*/
  608. {
  609. HANDLE Handle;
  610. //
  611. // It is necessary to call WinInitialize() before calling this function.
  612. // If this was not called, return failure.
  613. //
  614. if ( WinpAllocHandle == NULL ) {
  615. SetLastError ( ERROR_DLL_INIT_FAILED );
  616. return FALSE;
  617. }
  618. Handle = WinpOpenThreadInternal (
  619. dwAccess,
  620. bInheritHandle,
  621. ThreadId
  622. );
  623. return Handle;
  624. }
  625. #endif // _X86_