Windows NT 4.0 source code leak
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.

481 lines
17 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. debug.c
  5. Abstract:
  6. Main loop for INSTALER program
  7. Author:
  8. Steve Wood (stevewo) 09-Aug-1994
  9. Revision History:
  10. --*/
  11. #include "instaler.h"
  12. DWORD
  13. DebugEventHandler(
  14. LPDEBUG_EVENT DebugEvent
  15. );
  16. VOID
  17. InstallBreakpointsForDLL(
  18. PPROCESS_INFO Process,
  19. LPVOID BaseOfDll
  20. );
  21. VOID
  22. RemoveBreakpointsForDLL(
  23. PPROCESS_INFO Process,
  24. LPVOID BaseOfDll
  25. );
  26. char *DebugEventNames[] = {
  27. "Unknown debug event",
  28. "EXCEPTION_DEBUG_EVENT",
  29. "CREATE_THREAD_DEBUG_EVENT",
  30. "CREATE_PROCESS_DEBUG_EVENT",
  31. "EXIT_THREAD_DEBUG_EVENT",
  32. "EXIT_PROCESS_DEBUG_EVENT",
  33. "LOAD_DLL_DEBUG_EVENT",
  34. "UNLOAD_DLL_DEBUG_EVENT",
  35. "OUTPUT_DEBUG_STRING_EVENT",
  36. "RIP_EVENT",
  37. "Unknown debug event",
  38. "Unknown debug event",
  39. "Unknown debug event",
  40. "Unknown debug event",
  41. "Unknown debug event",
  42. "Unknown debug event",
  43. "Unknown debug event",
  44. "Unknown debug event",
  45. "Unknown debug event",
  46. "Unknown debug event",
  47. NULL
  48. };
  49. VOID
  50. DebugEventLoop( VOID )
  51. {
  52. DEBUG_EVENT DebugEvent;
  53. DWORD ContinueStatus;
  54. DWORD OldPriority;
  55. //
  56. // We want to process debug events quickly
  57. //
  58. OldPriority = GetPriorityClass( GetCurrentProcess() );
  59. SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
  60. do {
  61. if (!WaitForDebugEvent( &DebugEvent, INFINITE )) {
  62. DeclareError( INSTALER_WAITDEBUGEVENT_FAILED, GetLastError() );
  63. ExitProcess( 1 );
  64. }
  65. if (DebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
  66. if (DebugEvent.u.Exception.ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT &&
  67. DebugEvent.u.Exception.ExceptionRecord.ExceptionCode != STATUS_SINGLE_STEP
  68. ) {
  69. DbgEvent( DBGEVENT, ( "Debug exception event - Code: %x Address: %x Info: [%u] %x %x %x %x\n",
  70. DebugEvent.u.Exception.ExceptionRecord.ExceptionCode,
  71. DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress,
  72. DebugEvent.u.Exception.ExceptionRecord.NumberParameters,
  73. DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 0 ],
  74. DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 1 ],
  75. DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 2 ],
  76. DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 3 ]
  77. )
  78. );
  79. }
  80. }
  81. else {
  82. DbgEvent( DBGEVENT, ( "Debug %s (%x) event\n",
  83. DebugEventNames[ DebugEvent.dwDebugEventCode ],
  84. DebugEvent.dwDebugEventCode
  85. )
  86. );
  87. }
  88. ContinueStatus = DebugEventHandler( &DebugEvent );
  89. if (!ContinueDebugEvent( DebugEvent.dwProcessId,
  90. DebugEvent.dwThreadId,
  91. ContinueStatus
  92. )
  93. ) {
  94. DeclareError( INSTALER_CONTDEBUGEVENT_FAILED, GetLastError() );
  95. ExitProcess( 1 );
  96. }
  97. }
  98. while (!IsListEmpty( &ProcessListHead ));
  99. //
  100. // Drop back to old priority to interact with user.
  101. //
  102. SetPriorityClass( GetCurrentProcess(), OldPriority );
  103. }
  104. DWORD
  105. DebugEventHandler(
  106. LPDEBUG_EVENT DebugEvent
  107. )
  108. {
  109. DWORD ContinueStatus;
  110. PPROCESS_INFO Process;
  111. PTHREAD_INFO Thread;
  112. PBREAKPOINT_INFO Breakpoint;
  113. ContinueStatus = (DWORD)DBG_CONTINUE;
  114. if (FindProcessAndThreadForEvent( DebugEvent, &Process, &Thread )) {
  115. switch (DebugEvent->dwDebugEventCode) {
  116. case CREATE_PROCESS_DEBUG_EVENT:
  117. //
  118. // Create process event includes first thread of process
  119. // as well. Remember process and thread in our process tree
  120. //
  121. if (AddProcess( DebugEvent, &Process )) {
  122. InheritHandles( Process );
  123. AddThread( DebugEvent, Process, &Thread );
  124. }
  125. break;
  126. case EXIT_PROCESS_DEBUG_EVENT:
  127. //
  128. // Exit process event includes last thread of process
  129. // as well. Remove process and thread from our process tree
  130. //
  131. if (DeleteThread( Process, Thread )) {
  132. DeleteProcess( Process );
  133. }
  134. break;
  135. case CREATE_THREAD_DEBUG_EVENT:
  136. //
  137. // Create thread. Remember thread in our process tree.
  138. //
  139. AddThread( DebugEvent, Process, &Thread );
  140. break;
  141. case EXIT_THREAD_DEBUG_EVENT:
  142. //
  143. // Exit thread. Remove thread from our process tree.
  144. //
  145. DeleteThread( Process, Thread );
  146. break;
  147. case LOAD_DLL_DEBUG_EVENT:
  148. //
  149. // DLL just mapped into process address space. See if it is one
  150. // of the ones we care about and if so, install the necessary
  151. // breakpoints.
  152. //
  153. InstallBreakpointsForDLL( Process, DebugEvent->u.LoadDll.lpBaseOfDll );
  154. break;
  155. case UNLOAD_DLL_DEBUG_EVENT:
  156. //
  157. // DLL just unmapped from process address space. See if it is one
  158. // of the ones we care about and if so, remove the breakpoints we
  159. // installed when it was mapped.
  160. //
  161. RemoveBreakpointsForDLL( Process, DebugEvent->u.UnloadDll.lpBaseOfDll );
  162. break;
  163. case OUTPUT_DEBUG_STRING_EVENT:
  164. case RIP_EVENT:
  165. //
  166. // Ignore these
  167. //
  168. break;
  169. case EXCEPTION_DEBUG_EVENT:
  170. //
  171. // Assume we wont handle this exception
  172. //
  173. ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED;
  174. switch (DebugEvent->u.Exception.ExceptionRecord.ExceptionCode) {
  175. //
  176. // Breakpoint exception.
  177. //
  178. case STATUS_BREAKPOINT:
  179. EnterCriticalSection( &BreakTable );
  180. if (Thread->BreakpointToStepOver != NULL) {
  181. //
  182. // If this breakpoint was in place to step over an API entry
  183. // point breakpoint, then deal with it by ending single
  184. // step mode, resuming all threads in the process and
  185. // reinstalling the API breakpoint we just stepped over.
  186. // Finally return handled for this exception so the
  187. // thread can proceed.
  188. //
  189. EndSingleStepBreakpoint( Process, Thread );
  190. HandleThreadsForSingleStep( Process, Thread, FALSE );
  191. InstallBreakpoint( Process, Thread->BreakpointToStepOver );
  192. Thread->BreakpointToStepOver = NULL;
  193. ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
  194. }
  195. else {
  196. //
  197. // Otherwise, see if this breakpoint is either an API entry
  198. // point breakpoint for the process or a return address
  199. // breakpoint for a thread in the process.
  200. //
  201. Breakpoint = FindBreakpoint( DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress,
  202. Process,
  203. Thread
  204. );
  205. if (Breakpoint != NULL) {
  206. //
  207. // This is one of our breakpoints, call the breakpoint
  208. // handler.
  209. //
  210. if (HandleBreakpoint( Process, Thread, Breakpoint )) {
  211. //
  212. // Now single step over this breakpoint, by removing it and
  213. // setting a breakpoint at the next instruction (or using
  214. // single stepmode if the processor supports it). We also
  215. // suspend all the threads in the process except this one so
  216. // we know the next breakpoint/single step exception we see
  217. // for this process will be for this one.
  218. //
  219. Thread->BreakpointToStepOver = Breakpoint;
  220. RemoveBreakpoint( Process, Breakpoint );
  221. HandleThreadsForSingleStep( Process, Thread, TRUE );
  222. BeginSingleStepBreakpoint( Process, Thread );
  223. }
  224. else {
  225. Thread->BreakpointToStepOver = NULL;
  226. }
  227. ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
  228. }
  229. else {
  230. DbgEvent( DBGEVENT, ( "Skipping over hardcoded breakpoint at %x\n",
  231. DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
  232. )
  233. );
  234. //
  235. // If not one of our breakpoints, assume it is a hardcoded
  236. // breakpoint and skip over it. This will get by the one
  237. // breakpoint in LdrInit triggered by the process being
  238. // invoked with DEBUG_PROCESS.
  239. //
  240. if (SkipOverHardcodedBreakpoint( Process,
  241. Thread,
  242. DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
  243. )
  244. ) {
  245. //
  246. // If we successfully skipped over this hard-coded breakpoint
  247. // then return handled for this exception.
  248. //
  249. ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
  250. }
  251. }
  252. }
  253. LeaveCriticalSection( &BreakTable );
  254. break;
  255. case STATUS_SINGLE_STEP:
  256. //
  257. // We should only see this exception on processors that
  258. // support a single step mode.
  259. //
  260. EnterCriticalSection( &BreakTable );
  261. if (Thread->BreakpointToStepOver != NULL) {
  262. EndSingleStepBreakpoint( Process, Thread );
  263. HandleThreadsForSingleStep( Process, Thread, FALSE );
  264. InstallBreakpoint( Process, Thread->BreakpointToStepOver );
  265. Thread->BreakpointToStepOver = NULL;
  266. ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
  267. }
  268. LeaveCriticalSection( &BreakTable );
  269. break;
  270. case STATUS_VDM_EVENT:
  271. //
  272. // Returning NOT_HANDLED will cause the default behavior to
  273. // occur.
  274. //
  275. break;
  276. default:
  277. DbgEvent( DBGEVENT, ( "Unknown exception: %08x at %08x\n",
  278. DebugEvent->u.Exception.ExceptionRecord.ExceptionCode,
  279. DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
  280. )
  281. );
  282. break;
  283. }
  284. break;
  285. default:
  286. DbgEvent( DBGEVENT, ( "Unknown debug event\n" ) );
  287. break;
  288. }
  289. }
  290. return( ContinueStatus );
  291. }
  292. VOID
  293. InstallBreakpointsForDLL(
  294. PPROCESS_INFO Process,
  295. LPVOID BaseOfDll
  296. )
  297. {
  298. UCHAR ModuleIndex, ApiIndex;
  299. PBREAKPOINT_INFO Breakpoint;
  300. //
  301. // Loop over module table to see if the base address of this DLL matches
  302. // one of the module handles we want to set breakpoints in. If not, ignore
  303. // event.
  304. //
  305. for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) {
  306. if (ModuleInfo[ ModuleIndex ].ModuleHandle == BaseOfDll) {
  307. //
  308. // Loop over the list of API entry points for this module and set
  309. // a process specific breakpoint at the first instruction of each
  310. // entrypoint.
  311. //
  312. for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) {
  313. if (ModuleIndex == ApiInfo[ ApiIndex ].ModuleIndex) {
  314. if (CreateBreakpoint( ApiInfo[ ApiIndex ].EntryPointAddress,
  315. Process,
  316. NULL, // process specific
  317. ApiIndex,
  318. NULL,
  319. &Breakpoint
  320. )
  321. ) {
  322. Breakpoint->ModuleName = ModuleInfo[ ApiInfo[ ApiIndex ].ModuleIndex ].ModuleName;
  323. Breakpoint->ProcedureName = ApiInfo[ ApiIndex ].EntryPointName;
  324. DbgEvent( DBGEVENT, ( "Installed breakpoint for %ws!%s at %08x\n",
  325. Breakpoint->ModuleName,
  326. Breakpoint->ProcedureName,
  327. ApiInfo[ ApiIndex ].EntryPointAddress
  328. )
  329. );
  330. }
  331. }
  332. }
  333. break;
  334. }
  335. }
  336. }
  337. VOID
  338. RemoveBreakpointsForDLL(
  339. PPROCESS_INFO Process,
  340. LPVOID BaseOfDll
  341. )
  342. {
  343. UCHAR ModuleIndex, ApiIndex;
  344. //
  345. // Loop over module index to see if the base address of this DLL matches
  346. // one of the module handles we set breakpoints in above. If not, ignore
  347. // event.
  348. //
  349. for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) {
  350. if (ModuleInfo[ ModuleIndex ].ModuleHandle == BaseOfDll) {
  351. //
  352. // Loop over the list of API entry points for this module and remove
  353. // each process specific breakpoint set above for each entrypoint.
  354. //
  355. for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) {
  356. if (ModuleIndex == ApiInfo[ ApiIndex ].ModuleIndex) {
  357. DestroyBreakpoint( ApiInfo[ ApiIndex ].EntryPointAddress,
  358. Process,
  359. NULL // process specific
  360. );
  361. }
  362. }
  363. break;
  364. }
  365. }
  366. }
  367. BOOLEAN
  368. InstallBreakpoint(
  369. PPROCESS_INFO Process,
  370. PBREAKPOINT_INFO Breakpoint
  371. )
  372. {
  373. if (!Breakpoint->SavedInstructionValid &&
  374. !ReadMemory( Process,
  375. Breakpoint->Address,
  376. &Breakpoint->SavedInstruction,
  377. SizeofBreakpointInstruction,
  378. "save instruction"
  379. )
  380. ) {
  381. return FALSE;
  382. }
  383. else
  384. if (!WriteMemory( Process,
  385. Breakpoint->Address,
  386. BreakpointInstruction,
  387. SizeofBreakpointInstruction,
  388. "breakpoint instruction"
  389. )
  390. ) {
  391. return FALSE;
  392. }
  393. else {
  394. Breakpoint->SavedInstructionValid = TRUE;
  395. return TRUE;
  396. }
  397. }
  398. BOOLEAN
  399. RemoveBreakpoint(
  400. PPROCESS_INFO Process,
  401. PBREAKPOINT_INFO Breakpoint
  402. )
  403. {
  404. if (!Breakpoint->SavedInstructionValid ||
  405. !WriteMemory( Process,
  406. Breakpoint->Address,
  407. &Breakpoint->SavedInstruction,
  408. SizeofBreakpointInstruction,
  409. "restore saved instruction"
  410. )
  411. ) {
  412. return FALSE;
  413. }
  414. else {
  415. return TRUE;
  416. }
  417. }