Leaked source code of windows server 2003
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.

485 lines
11 KiB

  1. /*
  2. Copyright (c) 2001 Microsoft Corporation
  3. File name:
  4. hotpatch.c
  5. Author:
  6. Adrian Marinescu (adrmarin) Nov 14 2001
  7. */
  8. #include <ntos.h>
  9. #include <nt.h>
  10. #include <ntrtl.h>
  11. #include <nturtl.h>
  12. #include "ldrp.h"
  13. #include "hotpatch.h"
  14. ULONG LdrpHotpatchCount = 0;
  15. LIST_ENTRY LdrpHotPatchList;
  16. NTSTATUS
  17. LdrpSetupHotpatch (
  18. IN PRTL_PATCH_HEADER RtlPatchData
  19. )
  20. /*++
  21. Routine Description:
  22. This utility routine is used to:
  23. - find the targed module (the patch apply to)
  24. - search for an existing identical patch, and create a new one if not
  25. existent
  26. - Prepare the fixup code
  27. N.B. It assumes that the loader lock is held.
  28. Arguments:
  29. DllPatchHandle - The handle of the patch image
  30. Patch - The pointer to the patch header
  31. PatchFlags - The flags for the patch being applied.
  32. Return Value:
  33. NTSTATUS
  34. --*/
  35. {
  36. PLIST_ENTRY Next;
  37. NTSTATUS Status;
  38. //
  39. // walk the table entry list to find the dll
  40. //
  41. Next = PebLdr.InLoadOrderModuleList.Flink;
  42. for ( ; Next != &PebLdr.InLoadOrderModuleList; Next = Next->Flink) {
  43. PPATCH_LDR_DATA_TABLE_ENTRY Entry;
  44. Entry = CONTAINING_RECORD (Next, PATCH_LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
  45. //
  46. // when we unload, the memory order links flink field is nulled.
  47. // this is used to skip the entry pending list removal.
  48. //
  49. if ( !Entry->InMemoryOrderLinks.Flink ) {
  50. continue;
  51. }
  52. if (RtlpIsSameImage(RtlPatchData, Entry)) {
  53. break;
  54. }
  55. }
  56. if (RtlPatchData->TargetDllBase == NULL) {
  57. return STATUS_DLL_NOT_FOUND;
  58. }
  59. //
  60. // Create the new structure rtl patch structure here
  61. // This requires some relocation info to be processed,
  62. // so we need to allow write access to the patch dll
  63. //
  64. Status = LdrpSetProtection (RtlPatchData->PatchLdrDataTableEntry->DllBase, FALSE);
  65. if (!NT_SUCCESS(Status)) {
  66. return Status;
  67. }
  68. Status = RtlInitializeHotPatch (RtlPatchData, 0);
  69. //
  70. // Restore the protection to RO
  71. //
  72. LdrpSetProtection (RtlPatchData->PatchLdrDataTableEntry->DllBase, TRUE);
  73. return Status;
  74. }
  75. NTSTATUS
  76. LdrpApplyHotPatch(
  77. IN PRTL_PATCH_HEADER RtlPatchData,
  78. IN ULONG PatchFlags
  79. )
  80. /*++
  81. Routine Description:
  82. The function applies the changes to the target code.
  83. Arguments:
  84. RtlPatchData - Supplies the patch information
  85. PatchFlags - Supplies the patch flags
  86. Return Value:
  87. NTSTATUS
  88. --*/
  89. {
  90. NTSTATUS Status;
  91. //
  92. // Check whether we change the status or not.
  93. //
  94. if (((PatchFlags ^ RtlPatchData->CodeInfo->Flags) & FLG_HOTPATCH_ACTIVE) == 0) {
  95. return STATUS_NOT_SUPPORTED;
  96. }
  97. //
  98. // Unprotect the target binary pages
  99. //
  100. Status = LdrpSetProtection (RtlPatchData->TargetDllBase, FALSE);
  101. if (!NT_SUCCESS(Status)) {
  102. return Status;
  103. }
  104. //
  105. // Make the system call to modify the code from a DPC routine
  106. //
  107. Status = NtSetSystemInformation ( SystemHotpatchInformation,
  108. RtlPatchData->CodeInfo,
  109. RtlPatchData->CodeInfo->InfoSize);
  110. if (NT_SUCCESS(Status)) {
  111. //
  112. // Update the flags to contain the new state
  113. //
  114. RtlPatchData->CodeInfo->Flags ^= FLG_HOTPATCH_ACTIVE;
  115. }
  116. LdrpSetProtection (RtlPatchData->TargetDllBase, TRUE);
  117. return Status;
  118. }
  119. LONG
  120. LdrHotPatchRoutine (
  121. PVOID PatchInfo
  122. )
  123. /*++
  124. Routine Description:
  125. This is the worker routine that an external program can use for
  126. thread injection.
  127. Arguments:
  128. Patch - The pointer to the patch header. The application which calls
  129. this routine should never free or unmap this structure, as the current
  130. process can start using the code located inside this blob.
  131. Return Value:
  132. NTSTATUS
  133. --*/
  134. {
  135. NTSTATUS Status;
  136. PHOTPATCH_HEADER Patch;
  137. ULONG PatchFlags;
  138. PVOID DllHandle = NULL;
  139. LOGICAL FirstLoad;
  140. UNICODE_STRING PatchImageName, TargetImageName;
  141. PSYSTEM_HOTPATCH_CODE_INFORMATION RemoteInfo;
  142. PLIST_ENTRY Next;
  143. PLDR_DATA_TABLE_ENTRY PatchLdrTableEntry;
  144. PRTL_PATCH_HEADER RtlPatchData;
  145. BOOLEAN LoaderLockAcquired = FALSE;
  146. FirstLoad = FALSE;
  147. Status = STATUS_SUCCESS;
  148. __try {
  149. RemoteInfo = (PSYSTEM_HOTPATCH_CODE_INFORMATION)PatchInfo;
  150. if (!(RemoteInfo->Flags & FLG_HOTPATCH_NAME_INFO)) {
  151. Status = STATUS_INVALID_PARAMETER;
  152. leave;
  153. }
  154. PatchImageName.Buffer = (PWCHAR)((PUCHAR)RemoteInfo + RemoteInfo->UserModeInfo.NameOffset);
  155. PatchImageName.Length = (USHORT)RemoteInfo->UserModeInfo.NameLength;
  156. PatchImageName.MaximumLength = PatchImageName.Length;
  157. TargetImageName.Buffer = (PWCHAR)((PUCHAR)RemoteInfo + RemoteInfo->UserModeInfo.TargetNameOffset);
  158. TargetImageName.Length = (USHORT)RemoteInfo->UserModeInfo.TargetNameLength;
  159. TargetImageName.MaximumLength = TargetImageName.Length;
  160. PatchFlags = RemoteInfo->Flags;
  161. RtlEnterCriticalSection (&LdrpLoaderLock);
  162. LoaderLockAcquired = TRUE;
  163. if (TargetImageName.Length) {
  164. Status = STATUS_DLL_NOT_FOUND;
  165. Next = PebLdr.InLoadOrderModuleList.Flink;
  166. for ( ; Next != &PebLdr.InLoadOrderModuleList; Next = Next->Flink) {
  167. PLDR_DATA_TABLE_ENTRY Entry;
  168. Entry = CONTAINING_RECORD (Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
  169. //
  170. // when we unload, the memory order links flink field is nulled.
  171. // this is used to skip the entry pending list removal.
  172. //
  173. if ( !Entry->InMemoryOrderLinks.Flink ) {
  174. continue;
  175. }
  176. if (RtlEqualUnicodeString (&TargetImageName, &Entry->BaseDllName, TRUE)) {
  177. Status = STATUS_SUCCESS;
  178. break;
  179. }
  180. }
  181. if (!NT_SUCCESS(Status)) {
  182. leave;
  183. }
  184. }
  185. //
  186. // Load the module in memory. If this is not the first time
  187. // we apply the patch, the load will reference the LoadCount for
  188. // the existing module.
  189. //
  190. if (LdrpHotpatchCount == 0) {
  191. InitializeListHead (&LdrpHotPatchList);
  192. }
  193. Status = LdrLoadDll (NULL, NULL, &PatchImageName, &DllHandle );
  194. if (!NT_SUCCESS(Status)) {
  195. leave;
  196. }
  197. //
  198. // Search the loader table entry for the patch data
  199. //
  200. PatchLdrTableEntry = NULL;
  201. Next = PebLdr.InLoadOrderModuleList.Flink;
  202. for ( ; Next != &PebLdr.InLoadOrderModuleList; Next = Next->Flink) {
  203. PLDR_DATA_TABLE_ENTRY Entry;
  204. Entry = CONTAINING_RECORD (Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
  205. //
  206. // when we unload, the memory order links flink field is nulled.
  207. // this is used to skip the entry pending list removal.
  208. //
  209. if ( !Entry->InMemoryOrderLinks.Flink ) {
  210. continue;
  211. }
  212. if (DllHandle == Entry->DllBase) {
  213. PatchLdrTableEntry = Entry;
  214. break;
  215. }
  216. }
  217. if (PatchLdrTableEntry == NULL) {
  218. Status = STATUS_UNSUCCESSFUL;
  219. leave;
  220. }
  221. Patch = RtlGetHotpatchHeader(DllHandle);
  222. if (Patch == NULL) {
  223. Status = STATUS_INVALID_IMAGE_FORMAT;
  224. leave;
  225. }
  226. RtlPatchData = RtlFindRtlPatchHeader(&LdrpHotPatchList, PatchLdrTableEntry);
  227. if (RtlPatchData == NULL) {
  228. Status = RtlCreateHotPatch(&RtlPatchData, Patch, PatchLdrTableEntry, PatchFlags);
  229. if (!NT_SUCCESS(Status)) {
  230. leave;
  231. }
  232. Status = LdrpSetupHotpatch(RtlPatchData);
  233. if (!NT_SUCCESS(Status)) {
  234. RtlFreeHotPatchData(RtlPatchData);
  235. leave;
  236. }
  237. FirstLoad = TRUE;
  238. } else {
  239. //
  240. // Existing hotpatch case.
  241. // Rebuild the hook information, if the hotpatch was not active
  242. //
  243. if ((RtlPatchData->CodeInfo->Flags & FLG_HOTPATCH_ACTIVE) == 0) {
  244. Status = RtlReadHookInformation( RtlPatchData );
  245. if (!NT_SUCCESS(Status)) {
  246. leave;
  247. }
  248. }
  249. }
  250. Status = LdrpApplyHotPatch (RtlPatchData, PatchFlags);
  251. if (FirstLoad) {
  252. if (NT_SUCCESS(Status)) {
  253. //
  254. // We succesfully applied the patch. Add it to the Patch list
  255. //
  256. RtlPatchData->NextPatch = (PRTL_PATCH_HEADER)RtlPatchData->TargetLdrDataTableEntry->PatchInformation;
  257. RtlPatchData->TargetLdrDataTableEntry->PatchInformation = RtlPatchData;
  258. InsertTailList (&LdrpHotPatchList, &RtlPatchData->PatchList);
  259. LdrpHotpatchCount += 1;
  260. } else {
  261. RtlFreeHotPatchData(RtlPatchData);
  262. FirstLoad = FALSE; // force unload the module
  263. leave;
  264. }
  265. }
  266. } __finally {
  267. if (LoaderLockAcquired) {
  268. RtlLeaveCriticalSection (&LdrpLoaderLock);
  269. }
  270. //
  271. // Unload the patch dll. LdrpPerformHotPatch added a reference to the LoadCount
  272. // if succesfully installed.
  273. //
  274. if ((!FirstLoad) && (DllHandle != NULL)) {
  275. LdrUnloadDll (DllHandle);
  276. }
  277. }
  278. RtlExitUserThread(Status);
  279. // return Status;
  280. }
  281. NTSTATUS
  282. LdrpRundownHotpatchList (
  283. PRTL_PATCH_HEADER PatchHead
  284. )
  285. /*++
  286. Routine Description:
  287. This function cleans up the hotpatch data when the target dll is unloaded.
  288. The function assumes the loader lock is not held.
  289. Arguments:
  290. PatchHead - The head of the patch list
  291. Return Value:
  292. Returns the appropriate status
  293. --*/
  294. {
  295. while (PatchHead) {
  296. //
  297. // Remove the patch data from the list
  298. //
  299. PRTL_PATCH_HEADER CrtPatch = PatchHead;
  300. PatchHead = PatchHead->NextPatch;
  301. RtlEnterCriticalSection (&LdrpLoaderLock);
  302. RemoveEntryList (&CrtPatch->PatchList);
  303. LdrpHotpatchCount -= 1;
  304. RtlLeaveCriticalSection (&LdrpLoaderLock);
  305. //
  306. // Unload all instances for that dll.
  307. //
  308. if (CrtPatch->PatchImageBase) {
  309. LdrUnloadDll (CrtPatch->PatchImageBase);
  310. }
  311. RtlFreeHotPatchData (CrtPatch);
  312. }
  313. return STATUS_SUCCESS;
  314. }