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.

775 lines
20 KiB

  1. #include "precomp.h"
  2. #include <memory.h>
  3. #include <stdlib.h>
  4. #include "wdbgexts.h"
  5. #include "Debugger.h"
  6. #include "UserDump.h"
  7. #include "DbgHelp.h"
  8. #ifdef BP_SIZE
  9. ULONG g_BpSize = BP_SIZE;
  10. #endif
  11. #ifdef BP_INSTR
  12. ULONG g_BpInstr = BP_INSTR;
  13. #endif
  14. typedef void (*PFNDBGEXT)(HANDLE hCurrentProcess,
  15. HANDLE hCurrentThread,
  16. DWORD dwUnused,
  17. PWINDBG_EXTENSION_APIS lpExtensionApis,
  18. LPSTR lpArgumentString);
  19. TCHAR g_szCrashDumpFile[MAX_PATH];
  20. PFNDBGEXT g_pfnPhextsInit;
  21. BOOL g_bInitialBreakpoint = TRUE;
  22. PROCESS_INFORMATION g_ProcessInformation;
  23. WINDBG_EXTENSION_APIS ExtensionAPIs;
  24. typedef struct tagLOADEDDLL {
  25. struct tagLOADEDDLL* pNext;
  26. LPTSTR pszName;
  27. LPVOID lpBaseOfDll;
  28. } LOADEDDLL, *PLOADEDDLL;
  29. PLOADEDDLL g_pFirstDll;
  30. PPROCESS_INFO g_pProcessHead = NULL;
  31. PPROCESS_INFO g_pFirstProcess = NULL;
  32. //
  33. // BUGBUG: what's this for ?
  34. //
  35. TCHAR g_szDbgString[1024];
  36. //
  37. // Local function prototypes
  38. //
  39. void DebuggerLoop(void);
  40. DWORD WINAPI
  41. DebugApp(
  42. LPTSTR pszAppName
  43. )
  44. /*++
  45. Return: TRUE if successful, FALSE otherwise.
  46. Desc: Launch the debuggee.
  47. --*/
  48. {
  49. BOOL bRet;
  50. STARTUPINFO StartupInfo;
  51. wcscpy(g_szCrashDumpFile, L"d:\\temp\\crash.dmp");
  52. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  53. StartupInfo.cb = sizeof(StartupInfo);
  54. bRet = CreateProcess(NULL,
  55. pszAppName,
  56. NULL,
  57. NULL,
  58. FALSE,
  59. CREATE_SEPARATE_WOW_VDM | DEBUG_ONLY_THIS_PROCESS,
  60. NULL,
  61. NULL,
  62. &StartupInfo,
  63. &g_ProcessInformation);
  64. if (!bRet) {
  65. return 0;
  66. }
  67. if (g_ProcessInformation.hProcess != NULL) {
  68. InitVerifierLogSupport(pszAppName, g_ProcessInformation.dwProcessId);
  69. //
  70. // Start the debugger loop.
  71. //
  72. DebuggerLoop();
  73. //
  74. // Free the memory.
  75. //
  76. PPROCESS_INFO pProcess = g_pProcessHead;
  77. PPROCESS_INFO pProcessNext;
  78. while (pProcess != NULL) {
  79. pProcessNext = pProcess->pNext;
  80. PTHREAD_INFO pThread = pProcess->pFirstThreadInfo;
  81. PTHREAD_INFO pThreadNext;
  82. while (pThread != NULL) {
  83. pThreadNext = pThread->pNext;
  84. HeapFree(GetProcessHeap(), 0, pThread);
  85. pThread = pThreadNext;
  86. }
  87. HeapFree(GetProcessHeap(), 0, pProcess);
  88. pProcess = pProcessNext;
  89. }
  90. g_pProcessHead = NULL;
  91. PLOADEDDLL pDll = g_pFirstDll;
  92. PLOADEDDLL pDllNext;
  93. while (pDll != NULL) {
  94. pDllNext = pDll->pNext;
  95. HeapFree(GetProcessHeap(), 0, pDll->pszName);
  96. HeapFree(GetProcessHeap(), 0, pDll);
  97. pDll = pDllNext;
  98. }
  99. g_pFirstDll = NULL;
  100. CloseHandle(g_ProcessInformation.hProcess);
  101. CloseHandle(g_ProcessInformation.hThread);
  102. ZeroMemory(&g_ProcessInformation, sizeof(PROCESS_INFORMATION));
  103. }
  104. return 1;
  105. }
  106. PPROCESS_INFO
  107. GetProcessInfo(
  108. HANDLE hProcess
  109. )
  110. {
  111. PPROCESS_INFO pProcess = g_pProcessHead;
  112. while (pProcess != NULL) {
  113. if (pProcess->hProcess == hProcess) {
  114. return pProcess;
  115. }
  116. }
  117. return NULL;
  118. }
  119. SIZE_T
  120. ReadMemoryDbg(
  121. HANDLE hProcess,
  122. LPVOID Address,
  123. LPVOID Buffer,
  124. SIZE_T Length
  125. )
  126. {
  127. SIZE_T cbRead;
  128. LPVOID AddressBp;
  129. PPROCESS_INFO pProcess;
  130. if (!ReadProcessMemory(hProcess,
  131. Address,
  132. Buffer,
  133. Length,
  134. &cbRead)) {
  135. return 0;
  136. }
  137. pProcess = GetProcessInfo(hProcess);
  138. if (pProcess == NULL) {
  139. return 0;
  140. }
  141. #if defined(BP_INSTR) && defined(BP_SIZE)
  142. for (int i = 0; i < MAX_BREAKPOINTS; i++) {
  143. AddressBp = pProcess->bp[i].Address;
  144. if ((ULONG_PTR)AddressBp >= (ULONG_PTR)Address &&
  145. (ULONG_PTR)AddressBp < (ULONG_PTR)Address + Length) {
  146. CopyMemory((LPVOID)((ULONG_PTR)Buffer + (ULONG_PTR)AddressBp - (ULONG_PTR)Address),
  147. &pProcess->bp[i].OriginalInstr,
  148. g_BpSize);
  149. }
  150. }
  151. #endif
  152. return cbRead;
  153. }
  154. BOOL
  155. WriteMemoryDbg(
  156. HANDLE hProcess,
  157. PVOID Address,
  158. PVOID Buffer,
  159. SIZE_T Length
  160. )
  161. {
  162. SIZE_T cb = 0;
  163. BOOL bSuccess;
  164. bSuccess = WriteProcessMemory(hProcess, Address, Buffer, Length, &cb);
  165. if (!bSuccess || cb != Length) {
  166. return FALSE;
  167. }
  168. return TRUE;
  169. }
  170. BOOL
  171. GetImageName(
  172. HANDLE hProcess,
  173. ULONG_PTR ImageBase,
  174. PVOID ImageNamePtr,
  175. LPTSTR ImageName,
  176. DWORD ImageNameLength
  177. )
  178. /*++
  179. Return: TRUE if successful, FALSE otherwise.
  180. Desc: BUGBUG: give details here
  181. --*/
  182. {
  183. DWORD_PTR i;
  184. BYTE UnicodeBuf[256 * 2];
  185. IMAGE_DOS_HEADER dh;
  186. IMAGE_NT_HEADERS nh;
  187. IMAGE_EXPORT_DIRECTORY expdir;
  188. if (!ReadMemoryDbg(hProcess,
  189. (ULONG*)ImageNamePtr,
  190. &i,
  191. sizeof(i))) {
  192. goto GetFromExports;
  193. }
  194. if (!ReadMemoryDbg(hProcess,
  195. (ULONG*)i,
  196. (ULONG*)UnicodeBuf,
  197. sizeof(UnicodeBuf))) {
  198. goto GetFromExports;
  199. }
  200. ZeroMemory(ImageName, ImageNameLength);
  201. #ifdef UNICODE
  202. lstrcpyW(ImageName, (LPCWSTR)UnicodeBuf);
  203. #else
  204. WideCharToMultiByte(CP_ACP,
  205. 0,
  206. (LPWSTR)UnicodeBuf,
  207. wcslen((LPWSTR)UnicodeBuf),
  208. ImageName,
  209. ImageNameLength,
  210. NULL,
  211. NULL);
  212. #endif // UNICODE
  213. if (lstrlen(ImageName) == 0) {
  214. goto GetFromExports;
  215. }
  216. return TRUE;
  217. GetFromExports:
  218. if (!ReadMemoryDbg(hProcess,
  219. (ULONG*)ImageBase,
  220. (ULONG*)&dh,
  221. sizeof(IMAGE_DOS_HEADER))) {
  222. return FALSE;
  223. }
  224. if (dh.e_magic != IMAGE_DOS_SIGNATURE) {
  225. return FALSE;
  226. }
  227. if (!ReadMemoryDbg(hProcess,
  228. (ULONG*)(ImageBase + dh.e_lfanew),
  229. (ULONG*)&nh,
  230. sizeof(IMAGE_NT_HEADERS))) {
  231. return FALSE;
  232. }
  233. if (nh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) {
  234. return FALSE;
  235. }
  236. if (!ReadMemoryDbg(hProcess,
  237. (ULONG*)(ImageBase +
  238. nh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress),
  239. (ULONG*)&expdir,
  240. sizeof(IMAGE_EXPORT_DIRECTORY))) {
  241. return FALSE;
  242. }
  243. if (!ReadMemoryDbg(hProcess,
  244. (ULONG*)(ImageBase + expdir.Name),
  245. #ifdef UNICODE
  246. (ULONG*)UnicodeBuf,
  247. #else
  248. (ULONG*)ImageName,
  249. #endif // UNICODE
  250. ImageNameLength)) {
  251. return FALSE;
  252. }
  253. #ifdef UNICODE
  254. MultiByteToWideChar(CP_ACP,
  255. 0,
  256. (LPCSTR)UnicodeBuf,
  257. -1,
  258. ImageName,
  259. ImageNameLength);
  260. #endif // UNICODE
  261. return TRUE;
  262. }
  263. void
  264. AddDll(
  265. LPVOID lpBaseOfDll,
  266. LPCTSTR pszDllName
  267. )
  268. /*++
  269. Return: void.
  270. Desc: BUGBUG: give details here
  271. --*/
  272. {
  273. PLOADEDDLL pDll;
  274. pDll = (PLOADEDDLL)HeapAlloc(GetProcessHeap(), 0, sizeof(LOADEDDLL));
  275. if (pDll == NULL) {
  276. return;
  277. }
  278. pDll->pszName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, (lstrlen(pszDllName) + 1) * sizeof(TCHAR));
  279. if (pDll->pszName == NULL) {
  280. HeapFree(GetProcessHeap(), 0, pDll);
  281. return;
  282. }
  283. lstrcpy(pDll->pszName, pszDllName);
  284. pDll->lpBaseOfDll = lpBaseOfDll;
  285. pDll->pNext = g_pFirstDll;
  286. g_pFirstDll = pDll;
  287. }
  288. LPTSTR
  289. GetDll(
  290. LPVOID lpBaseOfDll
  291. )
  292. /*++
  293. Return: The name of the DLL with the specified base address.
  294. Desc: BUGBUG
  295. --*/
  296. {
  297. PLOADEDDLL pDll = g_pFirstDll;
  298. while (pDll != NULL) {
  299. if (pDll->lpBaseOfDll == lpBaseOfDll) {
  300. return pDll->pszName;
  301. }
  302. pDll = pDll->pNext;
  303. }
  304. return NULL;
  305. }
  306. void
  307. RemoveDll(
  308. LPVOID lpBaseOfDll
  309. )
  310. /*++
  311. Return: void.
  312. Desc: BUGBUG: give details here
  313. --*/
  314. {
  315. PLOADEDDLL* ppDll = &g_pFirstDll;
  316. PLOADEDDLL pDllFree;
  317. while (*ppDll != NULL) {
  318. if ((*ppDll)->lpBaseOfDll == lpBaseOfDll) {
  319. HeapFree(GetProcessHeap(), 0, (*ppDll)->pszName);
  320. pDllFree = *ppDll;
  321. *ppDll = (*ppDll)->pNext;
  322. HeapFree(GetProcessHeap(), 0, pDllFree);
  323. break;
  324. }
  325. ppDll = &((*ppDll)->pNext);
  326. }
  327. }
  328. BOOL
  329. ProcessModuleLoad(
  330. PPROCESS_INFO pProcess,
  331. DEBUG_EVENT* pde
  332. )
  333. /*++
  334. Return: TRUE on success, FALSE otherwise
  335. Desc: Process all module load debug events, create process & dll load.
  336. The purpose is to allocate a MODULEINFO structure, fill in the
  337. necessary values, and load the symbol table.
  338. --*/
  339. {
  340. HANDLE hFile=NULL;
  341. DWORD_PTR dwBaseOfImage;
  342. if (pde->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
  343. hFile = pde->u.CreateProcessInfo.hFile;
  344. dwBaseOfImage = (DWORD_PTR)pde->u.CreateProcessInfo.lpBaseOfImage;
  345. SymInitialize(pProcess->hProcess, NULL, FALSE);
  346. } else if (pde->dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) {
  347. hFile = pde->u.LoadDll.hFile;
  348. dwBaseOfImage = (DWORD_PTR)pde->u.LoadDll.lpBaseOfDll;
  349. }
  350. if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
  351. return FALSE;
  352. }
  353. if (dwBaseOfImage==(DWORD_PTR)NULL) {
  354. return FALSE;
  355. }
  356. if (!SymLoadModule(pProcess->hProcess, hFile, NULL, NULL, dwBaseOfImage, 0)) {
  357. return FALSE;
  358. }
  359. return TRUE;
  360. }
  361. void
  362. DebuggerLoop(
  363. void
  364. )
  365. /*++
  366. Return: void.
  367. Desc: BUGBUG: give details here
  368. --*/
  369. {
  370. DEBUG_EVENT DebugEv; // debugging event information
  371. DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
  372. PPROCESS_INFO pProcess = NULL;
  373. PTHREAD_INFO pThread = NULL;
  374. while (TRUE) {
  375. //
  376. // Wait for a debugging event to occur. The second parameter indicates
  377. // that the function does not return until a debugging event occurs.
  378. //
  379. WaitForDebugEvent(&DebugEv, INFINITE);
  380. //
  381. // Update the processes and threads lists.
  382. //
  383. pProcess = g_pProcessHead;
  384. while (pProcess != NULL) {
  385. if (pProcess->dwProcessId == DebugEv.dwProcessId) {
  386. break;
  387. }
  388. pProcess = pProcess->pNext;
  389. }
  390. if (pProcess == NULL) {
  391. //
  392. // New process.
  393. //
  394. pProcess = (PPROCESS_INFO)HeapAlloc(GetProcessHeap(),
  395. HEAP_ZERO_MEMORY,
  396. sizeof(PROCESS_INFO));
  397. if (pProcess == NULL) {
  398. break;
  399. }
  400. pProcess->dwProcessId = DebugEv.dwProcessId;
  401. pProcess->pNext = g_pProcessHead;
  402. g_pProcessHead = pProcess;
  403. }
  404. pThread = pProcess->pFirstThreadInfo;
  405. while (pThread != NULL) {
  406. if (pThread->dwThreadId == DebugEv.dwThreadId) {
  407. break;
  408. }
  409. pThread = pThread->pNext;
  410. }
  411. if (pThread == NULL) {
  412. //
  413. // New thread.
  414. //
  415. pThread = (PTHREAD_INFO)HeapAlloc(GetProcessHeap(),
  416. HEAP_ZERO_MEMORY,
  417. sizeof(THREAD_INFO));
  418. if (pThread == NULL) {
  419. break;
  420. }
  421. pThread->dwThreadId = DebugEv.dwThreadId;
  422. pThread->pNext = pProcess->pFirstThreadInfo;
  423. pProcess->pFirstThreadInfo = pThread;
  424. }
  425. dwContinueStatus = DBG_CONTINUE;
  426. if (DebugEv.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) {
  427. goto EndProcess;
  428. }
  429. switch (DebugEv.dwDebugEventCode) {
  430. case EXCEPTION_DEBUG_EVENT:
  431. switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode) {
  432. case EXCEPTION_BREAKPOINT:
  433. //
  434. // Hit a breakpoint.
  435. //
  436. if (!pProcess->bSeenLdrBp) {
  437. pProcess->bSeenLdrBp = TRUE;
  438. //
  439. // When the initial breakpoint is hit all the
  440. // staticly linked DLLs are already loaded so
  441. // we can go ahead and set breakpoints.
  442. //
  443. DPF("[DebuggerLoop] Hit initial breakpoint.");
  444. //
  445. // Now it would be a good time to initialize the debugger extensions.
  446. //
  447. break;
  448. }
  449. DPF("[DebuggerLoop] Hit breakpoint.");
  450. VLog("Hard coded Breakpoint");
  451. if (MessageBox(NULL,
  452. _T("An Access Violation occured. Do you want to create")
  453. _T(" a crash dump file so you can later debug this problem ?"),
  454. _T("Error"),
  455. MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) == IDYES) {
  456. pProcess->DebugEvent = DebugEv;
  457. GenerateUserModeDump(g_szCrashDumpFile,
  458. pProcess,
  459. &DebugEv.u.Exception);
  460. }
  461. if (MessageBox(NULL,
  462. _T("Do you want to continue running the program ?"),
  463. _T("Error"),
  464. MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) != IDYES) {
  465. goto EndProcess;
  466. }
  467. dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
  468. break;
  469. case STATUS_SINGLE_STEP:
  470. DPF("[DebuggerLoop] Hit single step breakpoint.");
  471. break;
  472. case EXCEPTION_ACCESS_VIOLATION:
  473. DPF("[DebuggerLoop] AV. Addr: 0x%08X, firstChance %d",
  474. DebugEv.u.Exception.ExceptionRecord.ExceptionAddress,
  475. DebugEv.u.Exception.dwFirstChance);
  476. if (DebugEv.u.Exception.dwFirstChance == 0) {
  477. VLog("Access Violation");
  478. if (MessageBox(NULL,
  479. _T("An Access Violation occured. Do you want to create")
  480. _T(" a crash dump file so you can later debug this problem ?"),
  481. _T("Error"),
  482. MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) == IDYES) {
  483. pProcess->DebugEvent = DebugEv;
  484. GenerateUserModeDump(g_szCrashDumpFile,
  485. pProcess,
  486. &DebugEv.u.Exception);
  487. }
  488. if (MessageBox(NULL,
  489. _T("Do you want to continue running the program ?"),
  490. _T("Error"),
  491. MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) != IDYES) {
  492. goto EndProcess;
  493. }
  494. }
  495. dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
  496. break;
  497. default:
  498. DPF("[DebuggerLoop] Unknown debugger exception.");
  499. dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
  500. break;
  501. }
  502. break;
  503. case CREATE_THREAD_DEBUG_EVENT:
  504. pThread->hProcess = pProcess->hProcess;
  505. pThread->hThread = DebugEv.u.CreateThread.hThread;
  506. DPF("[DebuggerLoop] new thread. StartAddress: 0x%x",
  507. DebugEv.u.CreateThread.lpStartAddress);
  508. break;
  509. case EXIT_THREAD_DEBUG_EVENT:
  510. DPF("[DebuggerLoop] exiting thread with code: 0x%x",
  511. DebugEv.u.ExitThread.dwExitCode);
  512. break;
  513. case CREATE_PROCESS_DEBUG_EVENT:
  514. pProcess->hProcess = DebugEv.u.CreateProcessInfo.hProcess;
  515. pThread->hProcess = pProcess->hProcess;
  516. pThread->hThread = DebugEv.u.CreateProcessInfo.hThread;
  517. if (g_pFirstProcess == NULL) {
  518. g_pFirstProcess = pProcess;
  519. }
  520. pProcess->EntryPoint = DebugEv.u.CreateProcessInfo.lpStartAddress;
  521. ProcessModuleLoad(pProcess, &DebugEv);
  522. DPF("[DebuggerLoop] new process. BaseImage: 0x%x StartAddress: 0x%x",
  523. DebugEv.u.CreateProcessInfo.lpBaseOfImage,
  524. DebugEv.u.CreateProcessInfo.lpStartAddress);
  525. break;
  526. case LOAD_DLL_DEBUG_EVENT:
  527. {
  528. TCHAR szAsciiBuf[256];
  529. TCHAR szDllName[128];
  530. TCHAR szExt[16];
  531. BOOL bRet;
  532. bRet = GetImageName(pProcess->hProcess,
  533. (ULONG_PTR)DebugEv.u.LoadDll.lpBaseOfDll,
  534. DebugEv.u.LoadDll.lpImageName,
  535. szAsciiBuf,
  536. sizeof(szAsciiBuf));
  537. if (!bRet) {
  538. DPF("[DebuggerLoop] DLL LOADED. BaseDll: 0x%X cannot get the name.",
  539. DebugEv.u.LoadDll.lpBaseOfDll);
  540. AddDll(DebugEv.u.LoadDll.lpBaseOfDll, NULL);
  541. } else {
  542. _tsplitpath(szAsciiBuf, NULL, NULL, szDllName, szExt);
  543. lstrcat(szDllName, szExt);
  544. AddDll(DebugEv.u.LoadDll.lpBaseOfDll, szDllName);
  545. DPF("[DebuggerLoop] DLL LOADED. BaseDll: 0x%X \"%S\".",
  546. DebugEv.u.LoadDll.lpBaseOfDll,
  547. szDllName);
  548. }
  549. ProcessModuleLoad(pProcess, &DebugEv);
  550. CloseHandle(DebugEv.u.LoadDll.hFile);
  551. break;
  552. }
  553. case UNLOAD_DLL_DEBUG_EVENT:
  554. {
  555. LPTSTR pszName = GetDll(DebugEv.u.UnloadDll.lpBaseOfDll);
  556. if (pszName == NULL) {
  557. DPF("[DebuggerLoop] DLL UNLOADED. BaseDll: 0x%X unknown.",
  558. DebugEv.u.UnloadDll.lpBaseOfDll);
  559. } else {
  560. DPF("[DebuggerLoop] DLL UNLOADED. BaseDll: 0x%X \"%S\".",
  561. DebugEv.u.UnloadDll.lpBaseOfDll, pszName);
  562. }
  563. RemoveDll(DebugEv.u.UnloadDll.lpBaseOfDll);
  564. break;
  565. }
  566. case OUTPUT_DEBUG_STRING_EVENT:
  567. ReadMemoryDbg(pThread->hProcess,
  568. DebugEv.u.DebugString.lpDebugStringData,
  569. g_szDbgString,
  570. DebugEv.u.DebugString.nDebugStringLength);
  571. #ifdef UNICODE
  572. DPF("DPF - %s", g_szDbgString);
  573. #else
  574. DPF("DPF - %S", g_szDbgString);
  575. #endif // UNICODE
  576. break;
  577. default:
  578. DPF("[DebuggerLoop] Unknown event 0x%X",
  579. DebugEv.dwDebugEventCode);
  580. break;
  581. }
  582. //
  583. // Resume executing the thread that reported the debugging event.
  584. //
  585. ContinueDebugEvent(DebugEv.dwProcessId,
  586. DebugEv.dwThreadId,
  587. dwContinueStatus);
  588. }
  589. EndProcess:
  590. DPF("[DebuggerLoop] The debugged process has exited.");
  591. }