├── .gitattributes ├── .gitignore ├── LICENSE.TXT ├── README.md ├── RELEASE NOTES.md ├── Tests.cmd ├── Tests ├── BugIdTests │ ├── BugIdTests.sln │ ├── BugIdTests.vcxproj │ ├── BugIdTests.vcxproj.user │ ├── bin │ │ ├── x64 │ │ │ ├── BugIdTests.exe │ │ │ └── BugIdTests.pdb │ │ ├── x64d │ │ │ ├── BugIdTests.exe │ │ │ └── BugIdTests.pdb │ │ ├── x86 │ │ │ ├── .vs │ │ │ │ └── BugIdTests │ │ │ │ │ └── v17 │ │ │ │ │ └── .suo │ │ │ ├── BugIdTests.exe │ │ │ └── BugIdTests.pdb │ │ └── x86d │ │ │ ├── BugIdTests.exe │ │ │ └── BugIdTests.pdb │ └── src │ │ ├── BugIdTests.cpp │ │ ├── fAccessHeapOrStackMemoryBlock.cpp │ │ ├── fAccessHeapOrStackMemoryBlock.h │ │ ├── fAllocateHeapOrStackMemoryForBlockSizeAndMemoryTypeArguments.h │ │ ├── fAllocateVirtualMemoryForAllocationTypeOrReturnMemoryAddressArgument.cpp │ │ ├── fAllocateVirtualMemoryForAllocationTypeOrReturnMemoryAddressArgument.h │ │ ├── fCall.asm │ │ ├── fCallWithStackPointer.asm │ │ ├── fFreeAllocatedHeapMemory.cpp │ │ ├── fFreeAllocatedHeapMemory.h │ │ ├── fIllegalInstruction.asm │ │ ├── fIntegerOverflow.asm │ │ ├── fJump.asm │ │ ├── fPopWithStackPointer.asm │ │ ├── fPrivilegedInstruction.asm │ │ ├── fPushWithStackPointer.asm │ │ ├── fRet.asm │ │ ├── fRetWithStackPointer.asm │ │ ├── fTestHeapWrongHandle.cpp │ │ ├── fTestHeapWrongHandle.h │ │ ├── fTestPureCall.cpp │ │ ├── fTestPureCall.h │ │ ├── fTestSafeInt.cpp │ │ ├── fTestSafeInt.h │ │ ├── fTestStackRecursion.cpp │ │ ├── fTestStackRecursion.h │ │ ├── fThrowFailFastWithErrorContextForWRTError.cpp │ │ ├── fThrowFailFastWithErrorContextForWRTError.h │ │ ├── fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments.cpp │ │ ├── fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments.h │ │ ├── mISA.h │ │ ├── mISAArgumentParsers.cpp │ │ ├── mISAArgumentParsers.h │ │ ├── mMASM.h │ │ ├── mMemory.cpp │ │ └── mMemory.h ├── EnablePageheapForTests.cmd ├── Tests.py ├── Tests_python.cmd ├── fAddAccessViolationTests.py ├── fAddBufferOverrunTests.py ├── fAddCorruptStackPointerTests.py ├── fAddDoubleFreeTests.py ├── fAddMisalignedFreeTests.py ├── fAddOutOfBoundsTests.py ├── fAddSafeIntTests.py ├── fAddUseAfterFreeTests.py ├── fRunASingleTest.py ├── fShowHelp.py ├── fTestDependencies.py ├── internal-python-module-dependencies.txt ├── mGlobals.py ├── mStandardExitCodes.py └── mTestLevels.py ├── __init__.py ├── cASanErrorDetector.py ├── cBugId.py ├── cCdbWrapper ├── __init__.py ├── cCdbWrapper.py ├── cCdbWrapper_cCdbStoppedException.py ├── cCdbWrapper_cEndOfCommandOutputMarkerMissingException.py ├── cCdbWrapper_cHelperThread.py ├── cCdbWrapper_fAllocateReserveMemoryIfNeeded.py ├── cCdbWrapper_fApplicationStdOutOrErrHelperThread.py ├── cCdbWrapper_fAttachCdbToProcessForId.py ├── cCdbWrapper_fCdbInterruptOnTimeoutHelperThread.py ├── cCdbWrapper_fCdbStdErrHelperThread.py ├── cCdbWrapper_fCdbStdInOutHelperThread.py ├── cCdbWrapper_fCleanupHelperThread.py ├── cCdbWrapper_fClearTimeout.py ├── cCdbWrapper_fHandleAttachedToApplicationProcess.py ├── cCdbWrapper_fHandleAttachedToUtilityProcess.py ├── cCdbWrapper_fHandleBreakpoint.py ├── cCdbWrapper_fHandleCurrentApplicationProcessTermination.py ├── cCdbWrapper_fHandleDebugOutputFromApplication.py ├── cCdbWrapper_fHandleExceptionInUtilityProcess.py ├── cCdbWrapper_fInterruptApplicationExecution.py ├── cCdbWrapper_fQueueAttachForProcessExecutableNames.py ├── cCdbWrapper_fQueueAttachForProcessId.py ├── cCdbWrapper_fRemoveBreakpoint.py ├── cCdbWrapper_fRunTimeoutCallbacks.py ├── cCdbWrapper_fSaveDumpToFile.py ├── cCdbWrapper_fSelectProcessIdAndThreadId.py ├── cCdbWrapper_fStartUWPApplication.py ├── cCdbWrapper_fTerminateUWPApplication.py ├── cCdbWrapper_fUpdateCdbISA.py ├── cCdbWrapper_fasbExecuteCdbCommand.py ├── cCdbWrapper_fasbReadOutput.py ├── cCdbWrapper_foSetTimeout.py ├── cCdbWrapper_foStartApplicationProcess.py ├── cCdbWrapper_ftbHandleLastCdbEvent.py ├── cCdbWrapper_fuAddBreakpointForProcessIdAndAddress.py └── cCdbWrapper_fuGetValueForRegister.py ├── cCollateralBugHandler ├── __init__.py ├── cCollateralBugHandler.py ├── cCollateralBugHandler_fbIgnoreAccessViolationException.py ├── cCollateralBugHandler_fbPoisonFlags.py └── cCollateralBugHandler_fbPoisonRegister.py ├── cErrorDetails.py ├── cException.py ├── cExcessiveCPUUsageDetector.py ├── cFunction.py ├── cStowedException.py ├── cTimeout.py ├── cVerifierStopDetector ├── __init__.py └── cVerifierStopDetector.py ├── dsDebuggingToolsPath_sISA.py ├── dxConfig.py ├── dxProductDetails.json ├── fInitializeProduct.py ├── fnGetDebuggerTimeInSeconds.py ├── fsGetNumberDescription.py ├── fsNumberOfBytes.py ├── fsbExceptionHandlingCdbCommands.py ├── fsbGetCPPObjectClassNameFromVFTable.py ├── ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress.py ├── ftsReportLicenseHeaderAndFooterHTML.py ├── ftsReportProductHeaderAndFooterHTML.py ├── ftuLimitedAndAlignedMemoryDumpStartAddressAndSize.py ├── fu0ValueFromCdbHexOutput.py ├── lgtm.yml ├── mBugReport ├── __init__.py ├── cBugReport.py ├── cBugReport_foAnalyzeException_Cpp.py ├── cBugReport_foAnalyzeException_STATUS_ACCESS_VIOLATION.py ├── cBugReport_foAnalyzeException_STATUS_BREAKPOINT.py ├── cBugReport_foAnalyzeException_STATUS_FAILFAST_OOM_EXCEPTION.py ├── cBugReport_foAnalyzeException_STATUS_FAIL_FAST_EXCEPTION.py ├── cBugReport_foAnalyzeException_STATUS_STACK_BUFFER_OVERRUN.py ├── cBugReport_foAnalyzeException_STATUS_STACK_OVERFLOW.py ├── cBugReport_foAnalyzeException_STATUS_STOWED_EXCEPTION.py ├── cBugReport_foAnalyzeException_WRT_ORIGINATE_ERROR_EXCEPTION.py ├── cBugReport_fs0GetDisassemblyHTML.py ├── cBugReport_fs0GetMemoryDumpBlockHTML.py ├── cBugReport_fs0GetRegistersBlockHTML.py ├── cBugReport_fxProcessStack.py ├── fsGetHTMLForValue.py ├── mAccessViolation │ ├── __init__.py │ ├── fUpdateReportForProcessThreadAccessViolationTypeIdAddressAndOptionalSize.py │ ├── fbUpdateReportForAllocatedPointer.py │ ├── fbUpdateReportForCollateralPoisonPointer.py │ ├── fbUpdateReportForGuardPagePointer.py │ ├── fbUpdateReportForHeapManagerPointer.py │ ├── fbUpdateReportForInvalidPointer.py │ ├── fbUpdateReportForNULLPointer.py │ ├── fbUpdateReportForReservedPointer.py │ ├── fbUpdateReportForSpecialPointer.py │ ├── fbUpdateReportForStackPointer.py │ └── fbUpdateReportForUnallocatedPointer.py ├── sBlockHTMLTemplate.py └── sReportHTMLTemplate.py ├── mBugTranslations ├── ASan.py ├── Abandonment.py ├── Chrome.py ├── Cpp.py ├── Firefox.py ├── RTC.py ├── SafeInt.py ├── SlashGS.py ├── V8.py ├── __init__.py ├── cBugTranslation.py ├── chakra_dll.py ├── clr_dll.py ├── combase_dll.py ├── conhost_exe.py ├── corpol_dll.py ├── edgecontent_dll.py ├── edgehtml_dll.py ├── fApplyBugTranslationsToBugReport.py ├── iso.py ├── jscript9_dll.py ├── kernel32_dll.py ├── kernelbase_dll.py ├── mBugTranslations.py ├── mshtml_dll.py ├── ntdll_dll.py ├── oledb32_dll.py ├── ucrtbase_dll.py ├── verifier_dll.py └── wil.py ├── mCP437.py ├── mDisassembler ├── __init__.py ├── cDisassembly.py ├── cInstruction.py ├── fo0GetDisassemblyForProcessAndCdbCommand.py ├── fo0GetDisassemblyForProcessStartAddressAndNumberOfBytes.py ├── fo0GetDisassemblyForProcessStartAddressAndNumberOfInstructions.py ├── fo0GetInstructionForProcessAndAddress.py └── fo0GetInstructionForProcessAndBeforeAddress.py ├── mExceptions.py ├── mExports.py ├── mHeapManager ├── __init__.py ├── cPageHeapManagerData │ ├── __init__.py │ ├── cPageHeapManagerData.py │ ├── cPageHeapManagerData_fo0GetForProcessAndAddressNearHeapBlock.py │ ├── fo0GetAllocationHeaderForVirtualAllocationAndPointerSize.py │ ├── fo0GetPageHeapBlockForProcessAndAddress.py │ ├── fo0GetVirtualAllocationForProcessAndAddressNearHeapBlock.py │ └── mPageHeapStructuresAndStaticValues.py ├── cWindowsHeapManagerData.py └── iHeapManagerData.py ├── mModule ├── __init__.py └── cModule.py ├── mProcess ├── __init__.py ├── cProcess.py ├── cProcess_fEnsurePageHeapIsEnabled.py ├── cProcess_fLoadSymbols.py ├── cProcess_fa0txGetRegistersForThreadId.py ├── cProcess_fasbGetStack.py ├── cProcess_fo0GetFunctionForAddress.py ├── cProcess_fo0GetModuleForCdbId.py ├── cProcess_fo0GetPageHeapManagerDataForAddressNearHeapBlock.py ├── cProcess_fo0GetWindowsHeapManagerDataForAddressNearHeapBlock.py ├── cProcess_fs0GetDetailsForAddress.py ├── cProcess_fsb0GetSymbolForAddress.py ├── cProcess_ftxSplitSymbolOrAddress.py ├── cProcess_fu0GetAddressForSymbol.py ├── cProcess_fu0GetTargetAddressForCallInstructionReturnAddress.py └── dttxRelevantRegisters_by_sISA.py ├── mSourceCodeLinks ├── Chrome.py ├── Firefox.py ├── __init__.py ├── cBugIdTests.py ├── cSourceCodeLink.py └── fsb0GetSourceCodeLinkURLForPath.py ├── mStack ├── __init__.py ├── cStack.py └── cStackFrame.py └── rbSymbolOrAddress.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | \#* 2 | *.filters 3 | *.pyc 4 | *.suo 5 | *.user 6 | *.VC.db 7 | *.VC.opendb 8 | *.vs 9 | Internal error reports/* 10 | releases 11 | Tests/BugIdTests/build/ 12 | Tests/Reports/ 13 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution 4.0 International 2 | License. To view a copy of this license, visit 3 | http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative 4 | Commons, PO Box 1866, Mountain View, CA 94042, USA. 5 | 6 | The following is a human-readable summary of (and not a substitute for) the 7 | license. 8 | 9 | ------------------------------------------------------------------------------- 10 | 11 | You are free to: 12 | Share — copy and redistribute the material in any medium or format 13 | Adapt — remix, transform, and build upon the material 14 | for any purpose, even commercially. 15 | 16 | This license is acceptable for Free Cultural Works. 17 | The licensor cannot revoke these freedoms as long as you follow the license 18 | terms. 19 | 20 | Under the following terms: 21 | Attribution — You must give appropriate credit, provide a link to the 22 | license, and indicate if changes were made. You may do so in any 23 | reasonable manner, but not in any way that suggests the licensor endorses 24 | you or your use. 25 | No additional restrictions — You may not apply legal terms or technological 26 | measures that legally restrict others from doing anything the license 27 | permits. 28 | 29 | Notices: 30 | You do not have to comply with the license for elements of the material in 31 | the public domain or where your use is permitted by an applicable exception 32 | or limitation. 33 | No warranties are given. The license may not give you all of the permissions 34 | necessary for your intended use. For example, other rights such as publicity, 35 | privacy, or moral rights may limit how you use the material. 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains the BugId python module that can be used to detect, 2 | analyze and uniquely identify application bugs on Windows. 3 | 4 | It is used by the [BugId](https://github.com/SkyLined/BugId/) Python script. 5 | That repository contains more information about BugId in general and serves as 6 | an example of how one can use this module. -------------------------------------------------------------------------------- /Tests/BugIdTests/BugIdTests.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.12.35707.178 d17.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BugIdTests", "BugIdTests.vcxproj", "{461199FA-51D2-4D17-B488-F8B802080B59}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {461199FA-51D2-4D17-B488-F8B802080B59}.Debug|x64.ActiveCfg = Debug|x64 17 | {461199FA-51D2-4D17-B488-F8B802080B59}.Debug|x64.Build.0 = Debug|x64 18 | {461199FA-51D2-4D17-B488-F8B802080B59}.Debug|x86.ActiveCfg = Debug|Win32 19 | {461199FA-51D2-4D17-B488-F8B802080B59}.Debug|x86.Build.0 = Debug|Win32 20 | {461199FA-51D2-4D17-B488-F8B802080B59}.Release|x64.ActiveCfg = Release|x64 21 | {461199FA-51D2-4D17-B488-F8B802080B59}.Release|x64.Build.0 = Release|x64 22 | {461199FA-51D2-4D17-B488-F8B802080B59}.Release|x86.ActiveCfg = Release|Win32 23 | {461199FA-51D2-4D17-B488-F8B802080B59}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Tests/BugIdTests/BugIdTests.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x64/BugIdTests.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x64/BugIdTests.exe -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x64/BugIdTests.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x64/BugIdTests.pdb -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x64d/BugIdTests.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x64d/BugIdTests.exe -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x64d/BugIdTests.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x64d/BugIdTests.pdb -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x86/.vs/BugIdTests/v17/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x86/.vs/BugIdTests/v17/.suo -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x86/BugIdTests.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x86/BugIdTests.exe -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x86/BugIdTests.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x86/BugIdTests.pdb -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x86d/BugIdTests.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x86d/BugIdTests.exe -------------------------------------------------------------------------------- /Tests/BugIdTests/bin/x86d/BugIdTests.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyLined/mBugId/b273c5466ca3bc8801d54942a264f676b52f72e6/Tests/BugIdTests/bin/x86d/BugIdTests.pdb -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fAccessHeapOrStackMemoryBlock.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | 5 | #include 6 | #include 7 | 8 | #include "fAllocateHeapOrStackMemoryForBlockSizeAndMemoryTypeArguments.h" 9 | #include "fFreeAllocatedHeapMemory.h" 10 | #include "mMemory.h" 11 | #include "mISAArgumentParsers.h" 12 | 13 | // Writing to the stack may overwrite local variables. To avoid this interfering 14 | // with our code, global variables are used. 15 | PBYTE gpAccessAddress; 16 | ISAUINT guCounter; 17 | BOOL gbRead; 18 | INT giStep; 19 | BYTE gbByte = 0x41; 20 | VOID fAccessHeapOrStackMemoryBlock( 21 | const WCHAR* sHeapOrStackArgument, 22 | const WCHAR* sAccessTypeArgument, 23 | const WCHAR* sBlockSizeArgument, 24 | const WCHAR* sAccessStartOffsetArgument, 25 | const WCHAR* sAccessSizeArgument, 26 | INT iStep 27 | ) { 28 | fAllocateHeapOrStackMemoryForBlockSizeAndMemoryTypeArguments( 29 | sBlockSizeArgument, 30 | sHeapOrStackArgument 31 | ); 32 | ISAINT iOffset = fiGetISAINTForArgument( 33 | L"", 34 | L"an INT offset from the start of the memory block", 35 | sAccessStartOffsetArgument 36 | ); 37 | ISAUINT uAccessSize = fuGetISAUINTForArgument( 38 | L"", 39 | L"a UINT number of bytes to access starting at the offset", 40 | sAccessSizeArgument 41 | ); 42 | if (_wcsicmp(sAccessTypeArgument, L"Read") == 0) { 43 | wprintf(L"• Reading %Id/0x%IX bytes starting at offset %Id/0x%IX from the start of the memory block...\r\n", 44 | uAccessSize, uAccessSize, iOffset, iOffset); 45 | gbRead = TRUE; 46 | } else if (_wcsicmp(sAccessTypeArgument, L"Write") == 0) { 47 | gbRead = FALSE; 48 | wprintf(L"• Writing %Id/0x%IX bytes starting at offset %Id/0x%IX from the start of the memory block...\r\n", 49 | uAccessSize, uAccessSize, iOffset, iOffset); 50 | } else { 51 | fwprintf(stderr, L"✘ must be \"Read\" or \"Write\", not %s.\r\n", sAccessTypeArgument); 52 | ExitProcess(1); 53 | }; 54 | gpAccessAddress = (PBYTE)gpMemoryBlock + iOffset; 55 | giStep = iStep; 56 | guCounter = uAccessSize; 57 | // Beyond this point, locals should no longer be accessed, as these are on the 58 | // stack and may be corrupted when we are writing to memory. 59 | while (guCounter--) { 60 | // we we are going down, we want to change the address before reading/writing 61 | if (giStep < 0) gpAccessAddress += giStep; 62 | if (gbRead) { 63 | gbByte = *gpAccessAddress; 64 | } else { 65 | *gpAccessAddress = gbByte; 66 | }; 67 | // we we are going up, we want to change the address after reading/writing 68 | if (giStep > 0) gpAccessAddress += giStep; 69 | }; 70 | fFreeAllocatedHeapMemory(); 71 | }; 72 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fAccessHeapOrStackMemoryBlock.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | __forceinline VOID fAccessHeapOrStackMemoryBlock( 7 | const WCHAR* sHeapOrStackArgument, 8 | const WCHAR* sAccessTypeArgument, 9 | const WCHAR* sBlockSizeArgument, 10 | const WCHAR* sAccessStartOffsetArgument, 11 | const WCHAR* sAccessSizeArgument, 12 | INT iStep 13 | ); -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fAllocateHeapOrStackMemoryForBlockSizeAndMemoryTypeArguments.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | #include "fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments.h" 7 | 8 | // we need the `alloca` call to be inline, so we use a #define that calls a 9 | // function to do most of the work and then return FALSE if we need to do the 10 | // alloca, which is done inline. 11 | #define fAllocateHeapOrStackMemoryForBlockSizeAndMemoryTypeArguments( \ 12 | sBlockSizeArgument, \ 13 | sHeapOrStackArgument \ 14 | ) \ 15 | if (!fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments( \ 16 | sBlockSizeArgument, \ 17 | sHeapOrStackArgument \ 18 | )) { \ 19 | gpMemoryBlock = alloca(guMemoryBlockSize); \ 20 | wprintf(L"• Created a %IX byte stack block at address 0x%p.\r\n", guMemoryBlockSize, gpMemoryBlock); \ 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fAllocateVirtualMemoryForAllocationTypeOrReturnMemoryAddressArgument.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | // disable warnings about Spectre mitigations 5 | #pragma warning( disable : 5045 ) 6 | 7 | #include 8 | #include 9 | 10 | #include "mISAArgumentParsers.h" 11 | #include "mMemory.h" 12 | 13 | VOID fAllocateVirtualMemoryForAllocationTypeOrReturnMemoryAddressArgument( 14 | const WCHAR* sAllocationTypeOrMemoryAddressArgument 15 | ) { 16 | if (_wcsicmp(sAllocationTypeOrMemoryAddressArgument, L"Unallocated") == 0) { 17 | gpMemoryBlock = VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_NOACCESS); 18 | if (gpMemoryBlock == NULL) { 19 | fwprintf(stderr, L"✘ VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_NOACCESS) failed.\r\n"); 20 | ExitProcess(1); 21 | }; 22 | if (!VirtualFree(gpMemoryBlock, 0, MEM_RELEASE)) { 23 | fwprintf(stderr, L"✘ VirtualFree(0x%p, 0, MEM_RELEASE) failed.\r\n", gpMemoryBlock); 24 | ExitProcess(1); 25 | }; 26 | wprintf(L"• Created freed memory at 0x%p.\r\n", gpMemoryBlock); 27 | } else if (_wcsicmp(sAllocationTypeOrMemoryAddressArgument, L"Reserved") == 0) { 28 | gpMemoryBlock = VirtualAlloc(NULL, 0x100, MEM_RESERVE, PAGE_NOACCESS); 29 | if (gpMemoryBlock == NULL) { 30 | fwprintf(stderr, L"✘ VirtualAlloc(NULL, 0x100, MEM_RESERVE, PAGE_NOACCESS) failed.\r\n"); 31 | ExitProcess(1); 32 | }; 33 | wprintf(L"• Created reserved memory at address 0x%p.\r\n", gpMemoryBlock); 34 | } else if (_wcsicmp(sAllocationTypeOrMemoryAddressArgument, L"NoAccess") == 0) { 35 | gpMemoryBlock = VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_NOACCESS); 36 | if (gpMemoryBlock == NULL) { 37 | fwprintf(stderr, L"✘ VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_NOACCESS) failed.\r\n"); 38 | ExitProcess(1); 39 | }; 40 | wprintf(L"• Created non-accessible memory at address 0x%p.\r\n", gpMemoryBlock); 41 | } else if (_wcsicmp(sAllocationTypeOrMemoryAddressArgument, L"GuardPage") == 0) { 42 | gpMemoryBlock = VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_EXECUTE_READWRITE | PAGE_GUARD); 43 | if (gpMemoryBlock == NULL) { 44 | fwprintf(stderr, L"✘ VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_EXECUTE_READWRITE | PAGE_GUARD) failed.\r\n"); 45 | ExitProcess(1); 46 | }; 47 | wprintf(L"• Created a guard page at address 0x%p.\r\n", gpMemoryBlock); 48 | } else { 49 | gpMemoryBlock = (PVOID)fuGetISAUINTForArgument( 50 | L" or
", 51 | L"\"Unallocated\", \"Reserved\", \"NoAccess\", \"GuardPage\", or a UINT address to use", 52 | sAllocationTypeOrMemoryAddressArgument 53 | ); 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fAllocateVirtualMemoryForAllocationTypeOrReturnMemoryAddressArgument.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | VOID fAllocateVirtualMemoryForAllocationTypeOrReturnMemoryAddressArgument( 7 | const WCHAR* sAllocationTypeOrMemoryAddressArgument 8 | ); -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fCall.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fCall PROC pAddress:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fCall PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | IFNDEF _WIN64 27 | CALL pAddress 28 | ELSE 29 | CALL RCX 30 | ENDIF 31 | 32 | ;## Function postfix ######################################## 33 | IFNDEF _WIN64 34 | ; Code to restore old stack frame is added automatically 35 | RET 4 36 | ELSE 37 | ; Restore old stack frame 38 | LEAVE 39 | RET 40 | ENDIF 41 | fCall ENDP 42 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fCallWithStackPointer.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fCallWithStackPointer PROC pAddress:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fCallWithStackPointer PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | IFNDEF _WIN64 27 | ; save the old stack pointer in a volatile register 28 | MOV EAX, ESP 29 | ; set the stack pointer to the address in the function argument 30 | MOV ESP, pAddress 31 | ; jump ahead using a CALL instruction 32 | CALL pCallTarget 33 | 34 | pCallTarget LABEL NEAR 35 | ; restore the old stack pointer 36 | MOV ESP, EAX 37 | ELSE 38 | ; save the old stack pointer in a volatile register 39 | MOV RAX, RSP 40 | ; set the stack pointer to the address in the function argument 41 | MOV RSP, RCX 42 | ; jump ahead using a CALL instruction 43 | CALL pCallTarget 44 | 45 | pCallTarget LABEL NEAR 46 | ; restore the old stack pointer 47 | MOV RSP, RAX 48 | ENDIF 49 | 50 | 51 | ;## Function postfix ######################################## 52 | IFNDEF _WIN64 53 | ; Code to restore old stack frame is added automatically 54 | RET 4 55 | ELSE 56 | ; Restore old stack frame 57 | LEAVE 58 | RET 59 | ENDIF 60 | fCallWithStackPointer ENDP 61 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fFreeAllocatedHeapMemory.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | 5 | #include "mMemory.h" 6 | 7 | VOID fFreeAllocatedHeapMemory() { 8 | if (gbAllocatedHeapMemoryBlock) { 9 | HANDLE hHeap = GetProcessHeap(); 10 | HeapFree(hHeap, 0, gpMemoryBlock); 11 | gbAllocatedHeapMemoryBlock = FALSE; 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fFreeAllocatedHeapMemory.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | 5 | VOID fFreeAllocatedHeapMemory(); -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fIllegalInstruction.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fIllegalInstruction PROC pUnused:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fIllegalInstruction PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | ; this is an opcode for an illegal instruction 27 | db 0FFH, 0FFH, 0CCH 28 | 29 | ;## Function postfix ######################################## 30 | IFNDEF _WIN64 31 | MOV EAX, pUnused ; To avoid 32 | ; Code to restore old stack frame is added automatically 33 | RET 4 34 | ELSE 35 | ; Restore old stack frame 36 | LEAVE 37 | RET 38 | ENDIF 39 | fIllegalInstruction ENDP 40 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fIntegerOverflow.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fIntegerOverflow PROC pUnused:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fIntegerOverflow PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | ; trigger integer overflow 27 | MOV EAX, 80000000H 28 | CDQ 29 | IDIV EDX 30 | 31 | ;## Function postfix ######################################## 32 | IFNDEF _WIN64 33 | MOV EAX, pUnused ; To avoid 34 | ; Code to restore old stack frame is added automatically 35 | RET 4 36 | ELSE 37 | ; Restore old stack frame 38 | LEAVE 39 | RET 40 | ENDIF 41 | fIntegerOverflow ENDP 42 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fJump.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fJump PROC pAddress:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fJump PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | IFNDEF _WIN64 27 | JMP pAddress 28 | ELSE 29 | JMP RCX 30 | ENDIF 31 | 32 | ;## Function postfix ######################################## 33 | IFNDEF _WIN64 34 | ; Code to restore old stack frame is added automatically 35 | RET 4 36 | ELSE 37 | ; Restore old stack frame 38 | LEAVE 39 | RET 40 | ENDIF 41 | fJump ENDP 42 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fPopWithStackPointer.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fPopWithStackPointer PROC pAddress:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fPopWithStackPointer PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | IFNDEF _WIN64 27 | ; save the old stack pointer in a volatile register 28 | MOV EAX, ESP 29 | ; set the stack pointer to the address in the function argument 30 | MOV ESP, pAddress 31 | ; pop a value from the stack into a volatile register 32 | POP EDX 33 | ; restore the old stack pointer 34 | MOV ESP, EAX 35 | ELSE 36 | ; save the old stack pointer in a volatile register 37 | MOV RAX, RSP 38 | ; set the stack pointer to the address in the function argument 39 | MOV RSP, RCX 40 | ; pop a value from the stack into a volatile register 41 | POP RDX 42 | ; restore the old stack pointer 43 | MOV RSP, RAX 44 | ENDIF 45 | 46 | ;## Function postfix ######################################## 47 | IFNDEF _WIN64 48 | ; Code to restore old stack frame is added automatically 49 | RET 4 50 | ELSE 51 | ; Restore old stack frame 52 | LEAVE 53 | RET 54 | ENDIF 55 | fPopWithStackPointer ENDP 56 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fPrivilegedInstruction.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fPrivilegedInstruction PROC pUnused:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fPrivilegedInstruction PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | ; this is a privileged instruction 27 | CLI 28 | 29 | ;## Function postfix ######################################## 30 | IFNDEF _WIN64 31 | MOV EAX, pUnused ; To avoid 32 | ; Code to restore old stack frame is added automatically 33 | RET 4 34 | ELSE 35 | ; Restore old stack frame 36 | LEAVE 37 | RET 38 | ENDIF 39 | fPrivilegedInstruction ENDP 40 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fPushWithStackPointer.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fPushWithStackPointer PROC pAddress:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fPushWithStackPointer PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | IFNDEF _WIN64 27 | ; save the old stack pointer in a volatile register 28 | MOV EAX, ESP 29 | ; set the stack pointer to the address in the function argument 30 | MOV ESP, pAddress 31 | ; push a value onto the stack 32 | PUSH EAX 33 | ; restore the old stack pointer 34 | MOV ESP, EAX 35 | ELSE 36 | ; save the old stack pointer in a volatile register 37 | MOV RAX, RSP 38 | ; set the stack pointer to the address in the function argument 39 | MOV RSP, RCX 40 | ; push a value onto the stack 41 | PUSH RAX 42 | ; restore the old stack pointer 43 | MOV RSP, RAX 44 | ENDIF 45 | 46 | ;## Function postfix ######################################## 47 | IFNDEF _WIN64 48 | ; Code to restore old stack frame is added automatically 49 | RET 4 50 | ELSE 51 | ; Restore old stack frame 52 | LEAVE 53 | RET 54 | ENDIF 55 | fPushWithStackPointer ENDP 56 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fRet.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fRet PROC pAddress:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fRet PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | IFNDEF _WIN64 27 | ; EBP now points one DWORD below the return address 28 | MOV EAX, pAddress 29 | MOV [EBP + 4], EAX 30 | ELSE 31 | ; EBP now points one QWORD below the return address 32 | ; The argument passed to this function is in RCX 33 | MOV [RBP + 8], RCX 34 | ENDIF 35 | 36 | ;## Function postfix ######################################## 37 | IFNDEF _WIN64 38 | ; Code to restore old stack frame is added automatically 39 | RET 4 40 | ELSE 41 | ; Restore old stack frame 42 | LEAVE 43 | RET 44 | ENDIF 45 | fRet ENDP 46 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fRetWithStackPointer.asm: -------------------------------------------------------------------------------- 1 | ;## Function prefix ##################################### 2 | IFNDEF _WIN64 3 | 4 | .586 5 | .MODEL flat, stdcall 6 | .CODE 7 | ALIGN 8 8 | fRetWithStackPointer PROC pAddress:PTR 9 | ; Code to create new stack frame is added automatically 10 | 11 | ELSE 12 | 13 | .CODE 14 | ALIGN 16 15 | fRetWithStackPointer PROC FRAME 16 | ; Create new stack frame 17 | PUSH RBP 18 | .PUSHREG RBP 19 | MOV RBP, RSP 20 | .SETFRAME RBP, 0 21 | .ENDPROLOG 22 | 23 | ENDIF 24 | 25 | ;## Function code ######################################### 26 | IFNDEF _WIN64 27 | ; save the old stack pointer in a volatile register 28 | MOV EAX, ESP 29 | ; set the stack pointer to the address in the function argument 30 | MOV ESP, pAddress 31 | ; return to a value on the stack 32 | ; this encodes "RET 4" to avoid MASM adding an epilog 33 | db 0C2H, 4, 0 34 | ; restore the old stack pointer 35 | MOV ESP, EAX 36 | ELSE 37 | ; save the old stack pointer in a volatile register 38 | MOV RAX, RSP 39 | ; set the stack pointer to the address in the function argument 40 | MOV RSP, RCX 41 | ; return to a value on the stack 42 | RET 43 | ; restore the old stack pointer 44 | MOV RSP, RAX 45 | ENDIF 46 | 47 | ;## Function postfix ######################################## 48 | IFNDEF _WIN64 49 | ; Code to restore old stack frame is added automatically 50 | RET 4 51 | ELSE 52 | ; Restore old stack frame 53 | LEAVE 54 | RET 55 | ENDIF 56 | fRetWithStackPointer ENDP 57 | END -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fTestHeapWrongHandle.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | 5 | #include 6 | #include 7 | 8 | #include "fAllocateHeapOrStackMemoryForBlockSizeAndMemoryTypeArguments.h" 9 | #include "mMemory.h" 10 | 11 | VOID fTestHeapWrongHandle( 12 | const WCHAR* sBlockSizeArgument 13 | ) { 14 | fAllocateHeapOrStackMemoryForBlockSizeAndMemoryTypeArguments( 15 | sBlockSizeArgument, // sBlockSizeArgument 16 | L"Heap" // sHeapOrStackArgument 17 | ); 18 | // gpMemoryBlock is now set 19 | HANDLE hPrimaryHeap = GetProcessHeap(); 20 | HANDLE hSecondaryHeap = HeapCreate( 21 | 0, // DWORD flOptions 22 | 0, // SIZE_T dwInitialSize, 23 | 0 // SIZE_T dwMaximumSize 24 | ); 25 | if (hSecondaryHeap == 0) { 26 | fwprintf(stderr, L"✘ HeapCreate(0, 0, 0) failed.\r\n"); 27 | ExitProcess(1); 28 | }; 29 | wprintf(L"• Freeing the heap block at 0x%p from heap 0x%p using heap 0x%p...", 30 | gpMemoryBlock, hPrimaryHeap, hSecondaryHeap); 31 | HeapFree(hSecondaryHeap, 0, gpMemoryBlock); 32 | }; 33 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fTestHeapWrongHandle.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | 5 | #include 6 | #include 7 | 8 | VOID fTestHeapWrongHandle( 9 | const WCHAR* sBlockSizeArgument 10 | ); 11 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fTestPureCall.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | 5 | #include 6 | #include 7 | 8 | // cPureCallBase is initialize before initializing cPureCall. cPureCallBase call fVirtual, 9 | // which has not been initialized, causing a pure virtual function call error. 10 | class cPureCallBase; 11 | VOID fCallVirtual (cPureCallBase* pBase); 12 | class cPureCallBase { 13 | public: 14 | virtual void fVirtual() = 0; 15 | cPureCallBase() { 16 | fCallVirtual(this); 17 | }; 18 | virtual ~cPureCallBase() {}; 19 | }; 20 | VOID fCallVirtual (cPureCallBase* pBase) { 21 | pBase->fVirtual(); 22 | }; 23 | class cPureCall : cPureCallBase { 24 | public: 25 | void fVirtual() {}; 26 | }; 27 | 28 | VOID fTestPureCall() { 29 | wprintf(L"• Making a pure virtual function call...\r\n"); 30 | cPureCall oPureCall; 31 | }; -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fTestPureCall.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | 5 | VOID fTestPureCall(); -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fTestSafeInt.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | VOID fTestSafeInt( 7 | const WCHAR* sOperationArgument, 8 | const WCHAR* sSignednessArgument, 9 | const WCHAR* sBitSizeArgument 10 | ); 11 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fTestStackRecursion.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | // disable warnings about Spectre mitigations 5 | #pragma warning( disable : 5045 ) 6 | // disable warnings about alloca potentially throwing an exception 7 | #pragma warning( disable : 6255 ) 8 | 9 | #include 10 | #include 11 | 12 | #include "mISA.h" 13 | #include "mISAArgumentParsers.h" 14 | 15 | VOID fStackRecursionLoop(ISAUINT); 16 | VOID fStackRecursionLoopFiller(ISAUINT, ISAUINT); 17 | ISAUINT guStackRecursionCounter = 0; 18 | 19 | /* 20 | When testing with one function call in the loop, the stack will look like this: 21 | fStackRecursionLoop (loop #1) 22 | fStackRecursionLoop (loop #2) 23 | fStackRecursionLoop (loop #3) 24 | fStackRecursionLoop (loop #4) 25 | ... 26 | When testing with two function calls in the loop, the stack will look like this: 27 | fStackRecursionLoop (loop #1) 28 | fStackRecursionLoopFiller 29 | fStackRecursionLoop (loop #2) 30 | fStackRecursionLoopFiller 31 | ... 32 | When testing with three function calls in the loop, the stack will look like this: 33 | fStackRecursionLoop (loop #1) 34 | fStackRecursionLoopFiller 35 | fStackRecursionLoopFiller 36 | fStackRecursionLoop (loop #2) 37 | fStackRecursionLoopFiller 38 | fStackRecursionLoopFiller 39 | ... 40 | etc... 41 | */ 42 | 43 | VOID fStackRecursionLoop(ISAUINT uMaxNumberOfCallsInALoop) { 44 | alloca(0x1000); 45 | if (uMaxNumberOfCallsInALoop == 1) { 46 | // Call ourselves if we only need one function calls in the loop 47 | fStackRecursionLoop(uMaxNumberOfCallsInALoop); 48 | } else { 49 | // Call a second function if we need more function calls in the loop 50 | fStackRecursionLoopFiller(uMaxNumberOfCallsInALoop, 1); 51 | }; 52 | }; 53 | VOID fStackRecursionLoopFiller(ISAUINT uMaxNumberOfCallsInALoop, ISAUINT uNumberOfCallsMadeInCurrentLoop) { 54 | alloca(0x1000); 55 | if (++uNumberOfCallsMadeInCurrentLoop < uMaxNumberOfCallsInALoop) { 56 | // Call ourselves until the total number of calls in this "loop" is the max 57 | fStackRecursionLoopFiller(uMaxNumberOfCallsInALoop, uNumberOfCallsMadeInCurrentLoop); 58 | } else { 59 | // Call 60 | fStackRecursionLoop(uMaxNumberOfCallsInALoop); 61 | }; 62 | }; 63 | 64 | VOID fTestStackRecursion( 65 | const WCHAR* sNumberOfCallsArgument 66 | ) { 67 | ISAUINT uMaxNumberOfCallsInALoop = fuGetISAUINTForArgument( 68 | L"", 69 | L"a UINT number of functions in each recursion loop", 70 | sNumberOfCallsArgument 71 | ); 72 | wprintf(L"• Calling %Id functions recursively...\r\n", uMaxNumberOfCallsInALoop); 73 | fStackRecursionLoop(uMaxNumberOfCallsInALoop); 74 | }; -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fTestStackRecursion.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | VOID fTestStackRecursion( 7 | const WCHAR* sNumberOfCallsArgument 8 | ); -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fThrowFailFastWithErrorContextForWRTError.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | VOID fThrowFailFastWithErrorContextForWRTError( 7 | const WCHAR* sHRESULTArgument, 8 | const WCHAR* sErrorMessageArgument, 9 | BOOL bLanguageError 10 | ); -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | // disable warnings about Spectre mitigations 5 | #pragma warning( disable : 5045 ) 6 | // disable warnings about alloca potentially throwing an exception 7 | #pragma warning( disable : 6255 ) 8 | 9 | #include 10 | #include 11 | 12 | #include "mISAArgumentParsers.h" 13 | #include "mMemory.h" 14 | 15 | BOOL fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments( 16 | const WCHAR* sBlockSizeArgument, 17 | const WCHAR* sHeapOrStackArgument 18 | ) { 19 | BOOL bAllocateHeapMemory = _wcsicmp(sHeapOrStackArgument, L"Heap") == 0; 20 | guMemoryBlockSize = fuGetISAUINTForArgument( 21 | L"", 22 | L"a UINT size for the memory block", 23 | sBlockSizeArgument 24 | ); 25 | if (bAllocateHeapMemory) { 26 | HANDLE hHeap = GetProcessHeap(); 27 | gpMemoryBlock = HeapAlloc(hHeap, 0, guMemoryBlockSize); 28 | if (!gpMemoryBlock) { 29 | fwprintf(stderr, L"✘ HeapAlloc(0x%p, 0, 0x%IX) failed.\r\n", hHeap, guMemoryBlockSize); 30 | ExitProcess(1); 31 | }; 32 | gbAllocatedHeapMemoryBlock = TRUE; 33 | wprintf(L"• Created a %IX byte heap block at address 0x%p.\r\n", guMemoryBlockSize, gpMemoryBlock); 34 | return TRUE; 35 | } else if (_wcsicmp(sHeapOrStackArgument, L"Stack") == 0) { 36 | return FALSE; 37 | } else { 38 | fwprintf(stderr, L"✘ must be\"Heap\" or \"Stack\", not %s.\r\n", sHeapOrStackArgument); 39 | ExitProcess(1); 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | BOOL fbAllocateHeapOrSetRequiredStackMemoryAndReturnFalseForBlockSizeAndMemoryTypeArguments( 7 | const WCHAR* sBlockSizeArgument, 8 | const WCHAR* sHeapOrStackArgument 9 | ); 10 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/mISA.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | // Use Instruction Set Architecture (ISA) specific (unsigned) integers: 7 | #ifdef _WIN64 8 | #define ISAINT signed __int64 9 | #define ISAUINT unsigned __int64 10 | #else 11 | #define ISAINT INT 12 | #define ISAUINT UINT 13 | #endif 14 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/mISAArgumentParsers.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | // disable warnings about inline functions not being inlined. 3 | #pragma warning( disable : 4710 ) 4 | 5 | #include 6 | #include 7 | 8 | #include "mISA.h" 9 | 10 | // Use Instruction Set Architecture (ISA) specific (unsigned) integers: 11 | #ifdef _WIN64 12 | #define fuGetISAINTForStringWithBase _wcstoi64 13 | #define fuGetISAUINTForStringWithBase _wcstoui64 14 | #else 15 | #define fuGetISAINTForStringWithBase wcstol 16 | #define fuGetISAUINTForStringWithBase wcstoul 17 | #endif 18 | 19 | #define fuGetUINT32ForStringWithBase wcstoul 20 | 21 | VOID fReportErrorAndExit( 22 | const WCHAR* sArgumentName, 23 | const WCHAR* sArgumentType, 24 | const WCHAR* sArgument, 25 | WCHAR* sEndOfString 26 | ) { 27 | fwprintf(stderr, L"✘ Argument %s must be %s, not %s\r\n", sArgumentName, sArgumentType, sArgument); 28 | fwprintf(stderr, L" %s\r\n ", sArgument); 29 | for (WCHAR* pChar = (WCHAR*)sArgument; pChar != sEndOfString; pChar++) { 30 | fwprintf(stderr, L" "); 31 | }; 32 | fwprintf(stderr, L"^\r\n"); 33 | ExitProcess(1); 34 | }; 35 | 36 | DWORD fdwGetDWORDForArgument( 37 | const WCHAR* sArgumentName, 38 | const WCHAR* sArgumentType, 39 | const WCHAR* sArgument 40 | ) { 41 | INT iBase = 10; 42 | if (sArgument[0] == L'0' && sArgument[1] == L'x') { 43 | sArgument += 2; iBase = 16; 44 | }; 45 | WCHAR* sEndOfString; 46 | DWORD dwResult = (DWORD)fuGetUINT32ForStringWithBase(sArgument, &sEndOfString, iBase); 47 | if (*sEndOfString != 0) fReportErrorAndExit(sArgumentName, sArgumentType, sArgument, sEndOfString); 48 | return dwResult; 49 | }; 50 | 51 | HRESULT fhGetHRESULTForArgument( 52 | const WCHAR* sArgumentName, 53 | const WCHAR* sArgumentType, 54 | const WCHAR* sArgument 55 | ) { 56 | INT iBase = 10; 57 | if (sArgument[0] == L'0' && sArgument[1] == L'x') { 58 | sArgument += 2; iBase = 16; 59 | }; 60 | WCHAR* sEndOfString; 61 | HRESULT hResult = (HRESULT)fuGetUINT32ForStringWithBase(sArgument, &sEndOfString, iBase); 62 | if (*sEndOfString != 0) fReportErrorAndExit(sArgumentName, sArgumentType, sArgument, sEndOfString); 63 | return hResult; 64 | }; 65 | 66 | ISAUINT fuGetISAUINTForArgument( 67 | const WCHAR* sArgumentName, 68 | const WCHAR* sArgumentType, 69 | const WCHAR* sArgument 70 | ) { 71 | INT iBase = 10; 72 | if (sArgument[0] == L'0' && sArgument[1] == L'x') { 73 | sArgument += 2; iBase = 16; 74 | }; 75 | WCHAR* sEndOfString; 76 | ISAUINT uNumber = fuGetISAUINTForStringWithBase(sArgument, &sEndOfString, iBase); 77 | if (*sEndOfString != 0) fReportErrorAndExit(sArgumentName, sArgumentType, sArgument, sEndOfString); 78 | return uNumber; 79 | }; 80 | 81 | ISAINT fiGetISAINTForArgument( 82 | const WCHAR* sArgumentName, 83 | const WCHAR* sArgumentType, 84 | const WCHAR* sArgument 85 | ) { 86 | INT iBase = 10; 87 | BOOL bNegative = FALSE; 88 | if (sArgument[0] == L'-') { 89 | sArgument += 1; bNegative = TRUE; 90 | } 91 | if (sArgument[0] == L'0' && sArgument[1] == L'x') { 92 | sArgument += 2; iBase = 16; 93 | }; 94 | WCHAR* sEndOfString; 95 | ISAINT iNumber = fuGetISAINTForStringWithBase(sArgument, &sEndOfString, iBase); 96 | if (*sEndOfString != 0) fReportErrorAndExit(sArgumentName, sArgumentType, sArgument, sEndOfString); 97 | if (bNegative) { 98 | iNumber = -iNumber; 99 | }; 100 | return iNumber; 101 | }; 102 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/mISAArgumentParsers.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | #include 5 | 6 | #include "mISA.h" 7 | 8 | DWORD fdwGetDWORDForArgument( 9 | const WCHAR* sArgumentName, 10 | const WCHAR* sArgumentType, 11 | const WCHAR* sArgument 12 | ); 13 | 14 | HRESULT fhGetHRESULTForArgument( 15 | const WCHAR* sArgumentName, 16 | const WCHAR* sArgumentType, 17 | const WCHAR* sArgument 18 | ); 19 | 20 | ISAUINT fuGetISAUINTForArgument( 21 | const WCHAR* sArgumentName, 22 | const WCHAR* sArgumentType, 23 | const WCHAR* sArgument 24 | ); 25 | 26 | ISAINT fiGetISAINTForArgument( 27 | const WCHAR* sArgumentName, 28 | const WCHAR* sArgumentType, 29 | const WCHAR* sArgument 30 | ); 31 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/mMASM.h: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | // even the ones that don't need arguments have one 3 | // because name mangling sucks and this makes the 4 | // prefix and postfix code in all of them similar. 5 | VOID __stdcall fCall(PVOID); 6 | VOID __stdcall fJump(PVOID); 7 | VOID __stdcall fRet(PVOID); 8 | VOID __stdcall fIllegalInstruction(PVOID); 9 | VOID __stdcall fIntegerOverflow(PVOID); 10 | VOID __stdcall fPrivilegedInstruction(PVOID); 11 | VOID __stdcall fCallWithStackPointer(PVOID); 12 | VOID __stdcall fRetWithStackPointer(PVOID); 13 | VOID __stdcall fPushWithStackPointer(PVOID); 14 | VOID __stdcall fPopWithStackPointer(PVOID); 15 | }; 16 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/mMemory.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | 5 | #include "mISA.h" 6 | 7 | PVOID gpMemoryBlock = NULL; 8 | ISAUINT guMemoryBlockSize; 9 | BOOL gbAllocatedHeapMemoryBlock = FALSE; 10 | -------------------------------------------------------------------------------- /Tests/BugIdTests/src/mMemory.h: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | 3 | #include 4 | 5 | #include "mISA.h" 6 | 7 | extern PVOID gpMemoryBlock; 8 | extern ISAUINT guMemoryBlockSize; 9 | extern BOOL gbAllocatedHeapMemoryBlock; 10 | -------------------------------------------------------------------------------- /Tests/EnablePageheapForTests.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | chcp 65001 >nul 3 | :: Administrator prompt text is white on blue for clarity 4 | FSUTIL dirty query %SystemDrive% >nul 5 | :: If we are already elevated, just change color, execute command, and exit 6 | IF ERRORLEVEL 1 ( 7 | ECHO • Restarting with elevated privileges.... 8 | POWERSHELL Start-Process -Verb "RunAs" -FilePath %ComSpec% -ArgumentList @^('/C', '%~f0'^) >nul 9 | IF ERRORLEVEL 1 GOTO :ERROR 10 | ) ELSE ( 11 | ECHO • Enabling page heap for the testing binary... 12 | "%WinDir%\System32\reg.exe" ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\BugIdTests.exe" /v "GlobalFlag" /t REG_SZ /d "0x02109870" /f >nul 13 | IF ERRORLEVEL 1 GOTO :ERROR 14 | ECHO ✓ Page heap is now enabled for the testing binary. 15 | ) 16 | EXIT /B 0 17 | 18 | :TURN_PAGEHEAP_ON_FOR 19 | IF ERRORLEVEL 1 ( 20 | ECHO ✘ Cannot enable page heap for %~1! 21 | EXIT /B 1 22 | ) 23 | 24 | ECHO ✓ Enabled page heap for %~1. 25 | EXIT /B 0 26 | 27 | :ERROR 28 | ECHO ✘ Error %ERRORLEVEL%! 29 | EXIT /B 1 -------------------------------------------------------------------------------- /Tests/fAddDoubleFreeTests.py: -------------------------------------------------------------------------------- 1 | from mTestLevels import NORMAL, FULL; 2 | 3 | nExpectedTestTimeOnX64 = 2; 4 | 5 | def fAddDoubleFreeTests(dxTests, uMaxMemoryDumpSize): 6 | srIdAndLocation = r"3b0.5b3 @ bugidtests.exe!fFreeAllocatedHeapMemory"; 7 | dxTests["HeapDoubleFree"] = [ 8 | (NORMAL, [ 1], [r"DoubleFree\[1\] " + srIdAndLocation], nExpectedTestTimeOnX64), 9 | (FULL, [ 2], [r"DoubleFree\[2\] " + srIdAndLocation], nExpectedTestTimeOnX64), 10 | (FULL, [ 3], [r"DoubleFree\[3\] " + srIdAndLocation], nExpectedTestTimeOnX64), 11 | (FULL, [ 4], [r"DoubleFree\[4n\] " + srIdAndLocation], nExpectedTestTimeOnX64), 12 | # Extra tests to check if the code deals correctly with memory areas too large to dump completely: 13 | # (Note that these blocks will be large, which cause the memory to actually be freed. If this 14 | # happens, information about the block's size if destroedy and BugId shows "[?]" in the bug id) 15 | (FULL, [uMaxMemoryDumpSize], [r"DoubleFree\[(4n|\?)\] " + srIdAndLocation], nExpectedTestTimeOnX64), 16 | (FULL, [uMaxMemoryDumpSize + 1], [r"DoubleFree\[(4n\+1|\?)\] " + srIdAndLocation], nExpectedTestTimeOnX64), 17 | (FULL, [uMaxMemoryDumpSize + 4], [r"DoubleFree\[(4n|\?)\] " + srIdAndLocation], nExpectedTestTimeOnX64), 18 | ]; 19 | -------------------------------------------------------------------------------- /Tests/fAddMisalignedFreeTests.py: -------------------------------------------------------------------------------- 1 | from mTestLevels import NORMAL, FULL; 2 | 3 | nExpectedTestTimeOnX64 = 2; 4 | 5 | def fAddMisalignedFreeTests(dxTests): 6 | srIdAndLocation = r"3b0\.5b3 @ !fFreeAllocatedHeapMemory"; 7 | dxTests["HeapMisalignedFree"] = [ 8 | (NORMAL, [ 1, 1], [r"MisalignedFree\[1\]\+0 " + srIdAndLocation], nExpectedTestTimeOnX64), 9 | (FULL, [ 1, 2], [r"MisalignedFree\[1\]\+1 " + srIdAndLocation], nExpectedTestTimeOnX64), 10 | (FULL, [ 2, 4], [r"MisalignedFree\[2\]\+2 " + srIdAndLocation], nExpectedTestTimeOnX64), 11 | (FULL, [ 3, 6], [r"MisalignedFree\[3\]\+3 " + srIdAndLocation], nExpectedTestTimeOnX64), 12 | (FULL, [ 4, 8], [r"MisalignedFree\[4n\]\+4n " + srIdAndLocation], nExpectedTestTimeOnX64), 13 | (FULL, [ 5, 10], [r"MisalignedFree\[4n\+1\]\+4n\+1 " + srIdAndLocation], nExpectedTestTimeOnX64), 14 | (FULL, [ 2, 1], [r"MisalignedFree\[2\]@1 " + srIdAndLocation], nExpectedTestTimeOnX64), 15 | (FULL, [ 3, 2], [r"MisalignedFree\[3\]@2 " + srIdAndLocation], nExpectedTestTimeOnX64), 16 | (FULL, [ 4, 3], [r"MisalignedFree\[4n\]@3 " + srIdAndLocation], nExpectedTestTimeOnX64), 17 | (FULL, [ 5, 4], [r"MisalignedFree\[4n\+1\]@4n " + srIdAndLocation], nExpectedTestTimeOnX64), 18 | (FULL, [ 1, -1], [r"MisalignedFree\[1\]\-1 " + srIdAndLocation], nExpectedTestTimeOnX64), 19 | (FULL, [ 1, -2], [r"MisalignedFree\[1\]\-2 " + srIdAndLocation], nExpectedTestTimeOnX64), 20 | (FULL, [ 1, -3], [r"MisalignedFree\[1\]\-3 " + srIdAndLocation], nExpectedTestTimeOnX64), 21 | (FULL, [ 1, -4], [r"MisalignedFree\[1\]\-4n " + srIdAndLocation], nExpectedTestTimeOnX64), 22 | (FULL, [ 1, -5], [r"MisalignedFree\[1\]\-4n\-1 " + srIdAndLocation], nExpectedTestTimeOnX64), 23 | ]; 24 | -------------------------------------------------------------------------------- /Tests/fAddOutOfBoundsTests.py: -------------------------------------------------------------------------------- 1 | from mTestLevels import NORMAL, FULL; 2 | 3 | nExpectedTestTimeOnX64 = 2; 4 | 5 | def fAddOutOfBoundsTests(dxTests, uLargeHeapBlockSize): 6 | # Small out-of-bounds writes that do not go outside of the memory page and therefore do not cause access violations. 7 | # These are detected by Page Heap / Application Verifier when the memory is freed. 8 | # This means it is not reported in the `fWriteByte` function that does the writing, 9 | # but in the `fFreeAllocatedHeapMemory` function that frees the memory. 10 | srFreeIdAndLocation = r"3b0\.f3d @ !fFreeAllocatedHeapMemory"; 11 | dxTests["OutOfBounds"] = { 12 | "Heap": { 13 | "Write": [ 14 | (NORMAL, [ 1, -1, 1], [r"OOBW\[1\]\{\-1~1\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), 15 | (FULL, [ 2, -2, 2], [r"OOBW\[2\]\{\-2~2\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), 16 | (FULL, [ 3, -3, 3], [r"OOBW\[3\]\{\-3~3\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), 17 | (FULL, [ 4, -4, 4], [r"OOBW\[4n\]\{\-4n~4n\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), 18 | (FULL, [ 5, -5, 5], [r"OOBW\[4n\+1\]\{-4n\-1~4n\+1\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), 19 | (FULL, [ 1, -4, 5], [r"OOBW\[1\]\{\-4n~4n\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), # Last byte written is within bounds! 20 | (FULL, [ 4, -4, 1], [r"OOBW\[4n\]\{\-4n~1\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), 21 | # Make sure very large allocations do not cause issues in cBugId 22 | (FULL, [uLargeHeapBlockSize, -4, 4], 23 | [r"OOBW\[4n\]\{\-4n~4n\} " + srFreeIdAndLocation], nExpectedTestTimeOnX64), 24 | ], 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /Tests/fAddSafeIntTests.py: -------------------------------------------------------------------------------- 1 | from mTestLevels import FULL, NOT_FULL, FULL_x64; 2 | 3 | nExpectedTestTimeOnX64 = 2; 4 | 5 | def fAddSafeIntTests(dxTests): 6 | srSafeIntIdAndLocation = r"31c\.5b3 @ !fTestSafeInt"; 7 | dxSafeIntTests = dxTests["SafeInt"] = [ 8 | # A simple list of basic tests is run when not running full tests. 9 | # When running full tests, a comprehensive quit of tests is run that 10 | # is generated further down the code... 11 | (NOT_FULL, ["++", "signed", 64], [r"IntegerOverflow " + srSafeIntIdAndLocation], nExpectedTestTimeOnX64), 12 | (NOT_FULL, ["--", "unsigned", 32], [r"IntegerUnderflow " + srSafeIntIdAndLocation], nExpectedTestTimeOnX64), 13 | (NOT_FULL, ["*", "signed", 16], [r"IntegerTruncation " + srSafeIntIdAndLocation], nExpectedTestTimeOnX64), 14 | (NOT_FULL, ["truncate", "signed", 8], [r"IntegerTruncation " + srSafeIntIdAndLocation], nExpectedTestTimeOnX64), 15 | (NOT_FULL, ["signedness", "signed", 16], [r"IntegerTruncation " + srSafeIntIdAndLocation], nExpectedTestTimeOnX64), 16 | ]; 17 | 18 | for (sOperation, sBugTypeId) in { 19 | "++": "IntegerOverflow", 20 | "--": "IntegerUnderflow", 21 | "*": "IntegerTruncation", 22 | "truncate": "IntegerTruncation", 23 | "signedness": "IntegerTruncation", 24 | }.items(): 25 | for sSignedness in ["signed", "unsigned"]: 26 | for uBits in [8, 16, 32, 64]: 27 | if sOperation == "truncate" and (sSignedness == "unsigned" or uBits == 64): 28 | # * The signedness argument is ignored for "truncate", so we only need to run 29 | # the test once. We do so for "signed" and skip "unsigned". 30 | # * C++ does not support values > 64 bits, so we cannot truncate a value to 31 | # 64 bits. 32 | continue; 33 | uTestLevel = FULL if uBits < 64 else FULL_x64; 34 | dxSafeIntTests.append( 35 | (uTestLevel, [sOperation, sSignedness, uBits], ["%s %s" % (sBugTypeId, srSafeIntIdAndLocation)], nExpectedTestTimeOnX64), 36 | ); 37 | -------------------------------------------------------------------------------- /Tests/fShowHelp.py: -------------------------------------------------------------------------------- 1 | INFO = 0xFF0F; 2 | NORMAL = 0xFF07; 3 | 4 | def fShowHelp(oConsole): 5 | oConsole.fOutput("Usage:"); 6 | oConsole.fOutput(" ", INFO, "Tests [Arguments] [ISA [test command line arguments]]"); 7 | oConsole.fOutput("Where:"); 8 | oConsole.fOutput(" ", INFO, "ISA", NORMAL, " (optional)"); 9 | oConsole.fOutput(" ", "Select an Instruction Set Architecture to run tests for."); 10 | oConsole.fOutput(" ", "Valid values: ", INFO, "x86", NORMAL, " and ", INFO, "x64", NORMAL, "."); 11 | oConsole.fOutput(" ", INFO, "test command line arguments", NORMAL, " (optional)"); 12 | oConsole.fOutput(" ", "Provide command line arguments to pass to Tests\\bin\\Tests_*.exe."); 13 | oConsole.fOutput(" ", "If provided, a single test is run with the given command line arguments,"); 14 | oConsole.fOutput(" ", "instead of the normal test suit."); 15 | oConsole.fOutput(" ", "(Run ", INFO, "Tests\\bin\\Tests_*.exe --help", NORMAL, " for more details)"); 16 | oConsole.fOutput(" ", "Valid values: ", INFO, "x86", NORMAL, " and ", INFO, "x64", NORMAL, "."); 17 | oConsole.fOutput("Arguments:"); 18 | oConsole.fOutput(" ", INFO, "--quick"); 19 | oConsole.fOutput(" ", "Run shortened suite of basic tests."); 20 | oConsole.fOutput(" ", INFO, "--full"); 21 | oConsole.fOutput(" ", "Run full suite of extended tests."); 22 | oConsole.fOutput(" ", INFO, "--reports"); 23 | oConsole.fOutput(" ", "Generate reports for all tests."); 24 | oConsole.fOutput(" ", INFO, "--show-cdb-io"); 25 | oConsole.fOutput(" ", "Show cdb input/output during each test."); 26 | oConsole.fOutput(" ", INFO, "--debug"); 27 | oConsole.fOutput(" ", "Output debug messages during each test."); 28 | oConsole.fOutput(" ", INFO, "--reports"); 29 | oConsole.fOutput(" ", "Generate reports for all tests."); 30 | -------------------------------------------------------------------------------- /Tests/internal-python-module-dependencies.txt: -------------------------------------------------------------------------------- 1 | ast 2 | base64 3 | binascii 4 | bisect 5 | bz2 6 | calendar 7 | contextlib 8 | ctypes 9 | datetime 10 | dis 11 | email 12 | errno 13 | fnmatch 14 | gc 15 | hashlib 16 | heapq 17 | http 18 | importlib 19 | inspect 20 | ipaddress 21 | linecache 22 | locale 23 | lzma 24 | math 25 | nturl2path 26 | opcode 27 | platform 28 | posixpath 29 | queue 30 | quopri 31 | random 32 | select 33 | selectors 34 | shutil 35 | socket 36 | ssl 37 | string 38 | struct 39 | tempfile 40 | textwrap 41 | threading 42 | token 43 | tokenize 44 | traceback 45 | urllib 46 | uuid 47 | weakref 48 | -------------------------------------------------------------------------------- /Tests/mGlobals.py: -------------------------------------------------------------------------------- 1 | import os; 2 | sTestsFolderPath = os.path.dirname(os.path.abspath(__file__)); 3 | sReportsFolderPath = os.path.join(sTestsFolderPath, "Reports"); 4 | 5 | from mWindowsAPI import oSystemInfo; 6 | 7 | # Variables used to store global settings for tests. 8 | # Some of these can be provided through command line arguments. 9 | bDebugStartFinish = False; # Show some output when a test starts and finishes. 10 | bShowCdbIO = False; # Show cdb I/O during tests (you'll want to run only 1 test at a time for this). 11 | bShowApplicationIO = False; 12 | bLicenseWarningsShown = False; 13 | nExcessiveCPUUsageCheckInitialTimeoutInSeconds = 0.5; # CPU usage should normalize after half a second. 14 | bGenerateReportHTML = False; 15 | bSaveReportHTML = False; 16 | uTotalMaxMemoryUse = 0x10000000; # The test application memory use limit: it should be large enough to allow the test 17 | # to function (including enough space for ASan's shadow memory!), but small enough 18 | # to detect excessive memory use quickly, before it causes the entire system to run 19 | # s low on memory. 0x10000000 ~= 256 Mb, which seems to work well. 20 | uOOMAllocationBlockSize = 0xF0000; # The out-of-memory test allocations size. it should be large enough to cause OOM 21 | # reasonably fast, but small enough so a not to hit the guTotalMaxMemoryUse 22 | # immediately, as this would not represent a normal OOM scenario. 23 | uLargeHeapBlockSize = 0x00800000; # Should be large to detect potential issues when handling large allocations, but 24 | # not so large as to cause the application to allocate more memory than it is allowed 25 | # through the guTotalMaxMemoryUse variable. 26 | 27 | dsTestsBinaries_by_sISA = { 28 | "x86": os.path.join(sTestsFolderPath, "BugIdTests", "bin", "x86", "BugIdTests.exe"), 29 | "x64": os.path.join(sTestsFolderPath, "BugIdTests", "bin", "x64", "BugIdTests.exe"), 30 | }; 31 | dsASanTestsBinaries_by_sISA = { 32 | "x86": os.path.join(sTestsFolderPath, "BugIdTests", "bin", "x86d", "BugIdTests.exe"), 33 | "x64": os.path.join(sTestsFolderPath, "BugIdTests", "bin", "x64d", "BugIdTests.exe"), 34 | }; 35 | 36 | dsComSpec_by_sISA = { 37 | oSystemInfo.sOSISA: os.path.join(os.environ.get("WinDir"), "System32", "cmd.exe"), 38 | }; 39 | if oSystemInfo.sOSISA == "x64": 40 | dsComSpec_by_sISA["x86"] = os.path.join(os.environ.get("WinDir"), "SysWOW64", "cmd.exe"); 41 | 42 | bDoNotLoadSymbols = False; -------------------------------------------------------------------------------- /Tests/mStandardExitCodes.py: -------------------------------------------------------------------------------- 1 | # These error codes are the same for any script that adheres to this standard: 2 | guExitCodeSuccess = 0; 3 | guExitCodeInternalError = 1; 4 | guExitCodeBadArgument = 2; 5 | guExitCodeBadDependencyError = 3; 6 | guExitCodeLicenseError = 4; 7 | guExitCodeCannotReadFromFileSystem = 5; 8 | guExitCodeCannotWriteToFileSystem = 6; 9 | guExitCodeTerminatedByUser = 7; -------------------------------------------------------------------------------- /Tests/mTestLevels.py: -------------------------------------------------------------------------------- 1 | # test selection flags 2 | uRunForQuickTests = 1; 3 | uRunForNormalTests = 2; 4 | uRunForFullTests = 4; 5 | uRunFor_x86 = 0x10; 6 | uRunFor_x64 = 0x20; 7 | uRunForAnyISA = uRunFor_x86 | uRunFor_x64; 8 | # test selection keywords 9 | QUICK = uRunForQuickTests | uRunForNormalTests | uRunForFullTests | uRunForAnyISA; 10 | NORMAL = uRunForNormalTests | uRunForFullTests | uRunForAnyISA; 11 | FULL = uRunForFullTests | uRunForAnyISA; 12 | NOT_FULL = uRunForNormalTests | uRunForAnyISA; 13 | x86 = uRunForNormalTests | uRunForFullTests | uRunFor_x86; 14 | x64 = uRunForNormalTests | uRunForFullTests | uRunFor_x64; 15 | FULL_x86 = uRunForFullTests | uRunFor_x86; 16 | FULL_x64 = uRunForFullTests | uRunFor_x64; 17 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .fInitializeProduct import fInitializeProduct; 2 | fInitializeProduct(); 3 | 4 | from .mExports import *; -------------------------------------------------------------------------------- /cCdbWrapper/__init__.py: -------------------------------------------------------------------------------- 1 | from .cCdbWrapper import cCdbWrapper; 2 | 3 | __all__ = [ 4 | "cCdbWrapper", 5 | ]; -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_cCdbStoppedException.py: -------------------------------------------------------------------------------- 1 | class cCdbWrapper_cCdbStoppedException(Exception): 2 | pass; -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_cEndOfCommandOutputMarkerMissingException.py: -------------------------------------------------------------------------------- 1 | class cCdbWrapper_cEndOfCommandOutputMarkerMissingException(Exception): 2 | def __init__(oSelf, asbCommandOutput): 3 | oSelf.asbCommandOutput = asbCommandOutput; 4 | def __str__(oSelf): 5 | return "The 'End-of-Command-Output Marker' is missing from the command output:\r\n%s" % "\r\n".join(oSelf.asbCommandOutput); 6 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_cHelperThread.py: -------------------------------------------------------------------------------- 1 | import sys; 2 | 3 | from mMultiThreading import cLock, cThread; 4 | 5 | class cCdbWrapper_cHelperThread(object): 6 | def __init__(oSelf, oCdbWrapper, sName, fActivity, *axActivityArguments, **dxFlags): 7 | for sFlag in dxFlags: 8 | assert sFlag in ["bVital"], \ 9 | "Unknown flag %s" % sFlag; 10 | oSelf.__oCdbWrapper = oCdbWrapper; 11 | oSelf.sName = sName; 12 | oSelf.__fActivity = fActivity 13 | oSelf.__axActivityArguments = axActivityArguments; 14 | oSelf.__bVital = dxFlags.get("bVital", False); # Vital in this respect means kill cdb.exe if the thread terminates. 15 | 16 | oSelf.__oThread = None; 17 | oSelf.__oStartedLock = cLock(n0DeadlockTimeoutInSeconds = 1); 18 | 19 | def __str__(oSelf): 20 | uThreadId = oSelf.uId; 21 | sThreadId = ("#%d" % uThreadId) if uThreadId else "not started"; 22 | return "Thread %s [%s] %s(%s)" % (sThreadId, oSelf.sName, repr(oSelf.__fActivity), ", ".join([repr(xArgument) for xArgument in oSelf.__axActivityArguments])); 23 | 24 | @property 25 | def bIsRunning(oSelf): 26 | # Consider it running between the moment it was started to the moment it terminated. This includes the brief moment 27 | # where the thread is started but not yet running. 28 | return (oSelf.__oThread.bStarted and not oSelf.__oThread.bTerminated) if oSelf.__oThread else False; 29 | 30 | @property 31 | def uId(oSelf): 32 | return oSelf.__oThread.uId if oSelf.__oThread else None; 33 | 34 | def fbIsCurrentThread(oSelf): 35 | return oSelf.__oThread == cThread.foGetCurrent(); 36 | 37 | def fStart(oSelf): 38 | if oSelf.bIsRunning: 39 | oSelf.__oThread.fStart(); # This should cause an exception. 40 | oSelf.__oThread = cThread(oSelf.__fRun); 41 | assert not oSelf.bIsRunning, \ 42 | "Cannot start a thread while it is running"; 43 | oSelf.__oCdbWrapper.aoActiveHelperThreads.append(oSelf); 44 | oSelf.__oStartedLock.fAcquire(); 45 | oSelf.__oThread.fStart(); 46 | oSelf.__oStartedLock.fWait(); 47 | 48 | def fWait(oSelf): 49 | oSelf.__oThread.fWait(); 50 | 51 | def __fRun(oSelf): 52 | oSelf.__oStartedLock.fRelease(); 53 | oSelf.__oCdbWrapper.fbFireCallbacks("Log message", "helper thread started", { 54 | "Thread": str(oSelf), 55 | }); 56 | try: 57 | try: 58 | oSelf.__fActivity(*oSelf.__axActivityArguments); 59 | except oSelf.__oCdbWrapper.cCdbStoppedException as oCdbStoppedException: 60 | # There is only one type of exception that is expected which is raised in the cdb stdin/out thread when cdb has 61 | # terminated. This exception is only used to terminate that thread and should be caught and handled here, to 62 | # prevent it from being reported as an (unexpected) internal exception. 63 | pass; 64 | except Exception as oException: 65 | cException, oException, oTraceBack = sys.exc_info(); 66 | if not oSelf.__oCdbWrapper.fbFireCallbacks("Internal exception", oSelf.__oThread, oException, oTraceBack): 67 | oSelf.__oCdbWrapper.fTerminate(); 68 | raise; 69 | finally: 70 | oSelf.__oCdbWrapper.aoActiveHelperThreads.remove(oSelf); 71 | oSelf.__oCdbWrapper.fbFireCallbacks("Log message", "helper thread terminated", { 72 | "Thread": str(oSelf), 73 | }); 74 | if oSelf.__bVital and oSelf.__oCdbWrapper.bCdbIsRunning: 75 | if oSelf.__oCdbWrapper.oCdbConsoleProcess and oSelf.__oCdbWrapper.oCdbConsoleProcess.bIsRunning: 76 | # A vital thread terminated and cdb is still running: terminate cdb 77 | oSelf.__oCdbWrapper.oCdbConsoleProcess.fbTerminate() 78 | assert not oSelf.__oCdbWrapper.oCdbConsoleProcess.bIsRunning, \ 79 | "Could not terminate cdb"; 80 | oSelf.__oCdbWrapper.bCdbIsRunning = False; 81 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fAllocateReserveMemoryIfNeeded.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import cVirtualAllocation; 2 | 3 | from ..dxConfig import dxConfig; 4 | 5 | def cCdbWrapper_fAllocateReserveMemoryIfNeeded(oCdbWrapper): 6 | ### Allocate reserve memory ###################################################################################### 7 | # Reserve some memory for exception analysis in case the target application causes a system-wide low-memory 8 | # situation. 9 | if dxConfig["uReservedMemory"]: 10 | if oCdbWrapper.o0ReservedMemoryVirtualAllocation is None: 11 | try: 12 | oCdbWrapper.o0ReservedMemoryVirtualAllocation = cVirtualAllocation.foCreateForProcessId( 13 | uProcessId = oCdbWrapper.oUtilityProcess.uId, 14 | uSize = dxConfig["uReservedMemory"], 15 | ); 16 | except MemoryError: 17 | oCdbWrapper.fbFireCallbacks("Log message", "Could not allocate 0x%X bytes reserved memory." % dxConfig["uReservedMemory"]); 18 | oCdbWrapper.o0ReservedMemoryVirtualAllocation = None; 19 | # If we cannot allocate memory, we'll just continue anyway. 20 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fApplicationStdOutOrErrHelperThread.py: -------------------------------------------------------------------------------- 1 | from ..mCP437 import fsCP437HTMLFromString; 2 | 3 | gbDebugIO = False; # Used for debugging cdb I/O issues 4 | 5 | def cCdbWrapper_fApplicationStdOutOrErrHelperThread(oCdbWrapper, oConsoleProcess, oPipe, sPipeName): 6 | # sPipeName should be stdout or stderr 7 | uProcessId = oConsoleProcess.uId; 8 | while 1: 9 | s0Line = oPipe.fs0ReadLine(); 10 | if s0Line is None: 11 | break; 12 | if gbDebugIO: print("\r%s
\n" % \ 17 | (sClassName, fsCP437HTMLFromString(s0Line, u0TabStop = 8)); 18 | if gbDebugIO: print("\r%s
\n" % fsCP437HTMLFromString(s0Line, u0TabStop = 8); 16 | oCdbWrapper.sCdbIOHTML += sLineHTML; 17 | oCdbWrapper.fbFireCallbacks("Log message", "StdErr output", { 18 | "Line": s0Line, 19 | }); 20 | oCdbWrapper.fbFireCallbacks("Cdb stderr output", s0Line); 21 | oCdbWrapper.bCdbIsRunning = False; 22 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fClearTimeout.py: -------------------------------------------------------------------------------- 1 | def cCdbWrapper_fClearTimeout(oCdbWrapper, oTimeout): 2 | try: 3 | oCdbWrapper.aoTimeouts.remove(oTimeout); 4 | except ValueError: 5 | pass; # It has already been cleared or fired. -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fHandleAttachedToApplicationProcess.py: -------------------------------------------------------------------------------- 1 | from ..dxConfig import dxConfig; 2 | 3 | def cCdbWrapper_fHandleAttachedToApplicationProcess(oCdbWrapper): 4 | if dxConfig["bEnsurePageHeap"]: 5 | oCdbWrapper.oCdbCurrentProcess.fEnsurePageHeapIsEnabled(); 6 | # This first application process we attach to for an UWP application is the main process. 7 | if oCdbWrapper.o0UWPApplication and not oCdbWrapper.bUWPApplicationStarted: 8 | oCdbWrapper.bUWPApplicationStarted = True; 9 | oCdbWrapper.auMainProcessIds.append(oCdbWrapper.oCdbCurrentProcess.uId); 10 | bIsMainProcess = True; 11 | else: 12 | bIsMainProcess = oCdbWrapper.oCdbCurrentProcess.uId in oCdbWrapper.auMainProcessIds; 13 | 14 | if oCdbWrapper.sCdbISA != oCdbWrapper.oCdbCurrentProcess.sISA: 15 | # We assume that the user can specify the ISA of the target application when they start cBugId, so if a main 16 | # process is mismatched, this could have been prevented. 17 | bPreventable = bIsMainProcess; 18 | if not oCdbWrapper.oCdbCurrentProcess.oCdbWrapper.fbFireCallbacks("Cdb ISA not ideal", oCdbWrapper.oCdbCurrentProcess, oCdbWrapper.sCdbISA, bPreventable): 19 | # This is fatal if it's preventable and there is no callback handler 20 | assert not bPreventable, \ 21 | "Cdb ISA %s is not ideal for debugging %s process 0x%X (running %s)." % \ 22 | (oCdbWrapper.sCdbISA, oCdbWrapper.oCdbCurrentProcess.sISA, oCdbWrapper.oCdbCurrentProcess.uId, oCdbWrapper.oCdbCurrentProcess.sBinaryName); 23 | 24 | # If we have a JobObject, add this process to it. 25 | if oCdbWrapper.oJobObject and not oCdbWrapper.oJobObject.fbAddProcessForId(oCdbWrapper.oCdbCurrentProcess.uId): 26 | if not oCdbWrapper.bFailedToApplyApplicationMemoryLimitsEventFired: 27 | # The limits no longer affect the application after the first time this happens, so it is only reported once. 28 | oCdbWrapper.bFailedToApplyApplicationMemoryLimitsEventFired = True; 29 | oCdbWrapper.fbFireCallbacks("Failed to apply application memory limits", oCdbWrapper.oCdbCurrentProcess); 30 | oCdbWrapper.fbFireCallbacks("Failed to apply process memory limits", oCdbWrapper.oCdbCurrentProcess); 31 | 32 | # Fire events 33 | oCdbWrapper.fbFireCallbacks("Log message", "Process attached", { 34 | "Is main process": bIsMainProcess and "yes" or "no", 35 | "Process Id": "0x%X" % (oCdbWrapper.oCdbCurrentProcess.uId,), 36 | "Binary name": oCdbWrapper.oCdbCurrentProcess.sBinaryName, 37 | "Command line":oCdbWrapper.oCdbCurrentProcess.sCommandLine, 38 | }); 39 | oCdbWrapper.fbFireCallbacks("Process attached", oCdbWrapper.oCdbCurrentProcess); 40 | 41 | # Make sure child processes of the new process are debugged as well. 42 | oCdbWrapper.fasbExecuteCdbCommand( 43 | sbCommand = b".childdbg 1;", 44 | sb0Comment = b"Debug child processes", 45 | bRetryOnTruncatedOutput = True, 46 | ); # TODO: check output 47 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fHandleAttachedToUtilityProcess.py: -------------------------------------------------------------------------------- 1 | from ..fsbExceptionHandlingCdbCommands import fsbExceptionHandlingCdbCommands; 2 | 3 | def cCdbWrapper_fHandleAttachedToUtilityProcess(oCdbWrapper): 4 | # This is the utility process; cdb has loaded and started it. Go set some things up. 5 | # Set up exception handling and record the utility process' id. 6 | oCdbWrapper.fasbExecuteCdbCommand( 7 | sbCommand = fsbExceptionHandlingCdbCommands(), 8 | sb0Comment = b"Setup exception handling", 9 | ); 10 | oCdbWrapper.fbFireCallbacks("Log message", "Attached to utility process", { 11 | "Process id": "%d/0x%X" % (oCdbWrapper.oUtilityProcess.uId, oCdbWrapper.oUtilityProcess.uId), 12 | }); 13 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fHandleBreakpoint.py: -------------------------------------------------------------------------------- 1 | def cCdbWrapper_fHandleBreakpoint(oCdbWrapper, uBreakpointId): 2 | # A breakpoint was hit; sanity checks, log message and fire the callback 3 | uExpectedProcessId = oCdbWrapper.duProcessId_by_uBreakpointId[uBreakpointId]; 4 | assert oCdbWrapper.oCdbCurrentProcess.uId == uExpectedProcessId, \ 5 | "Breakpoint #%d was set in process 0x%X but reported to have been hit in process 0x%X!?" % \ 6 | (uBreakpointId, uExpectedProcessId, oCdbWrapper.oCdbCurrentProcess.uId); 7 | uBreakpointAddress = oCdbWrapper.duAddress_by_uBreakpointId[uBreakpointId]; 8 | # We could sanity check the breakpoint address matched current eip/rip, but I don't have time to implement it and 9 | # it would slow things down a bit. 10 | oCdbWrapper.fbFireCallbacks("Log message", "Breakpoint hit", { 11 | "Process": "0x%X" % (uExpectedProcessId,), 12 | "Breakpoint id": "%d" % (uBreakpointId,), 13 | "Address": "0x%X" % (uBreakpointAddress,), 14 | }); 15 | fBreakpointCallback = oCdbWrapper.dfCallback_by_uBreakpointId[uBreakpointId]; 16 | fBreakpointCallback(uBreakpointId); 17 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fHandleCurrentApplicationProcessTermination.py: -------------------------------------------------------------------------------- 1 | def cCdbWrapper_fHandleCurrentApplicationProcessTermination(oCdbWrapper): 2 | # A process was terminated. This may be the first time we hear of the process, i.e. the code above may have only 3 | # just added the process. I do not know why cdb did not throw an event when it was created: maybe it terminated 4 | # while being created due to some error? Anyway, having added the process in the above code, we'll now mark it as 5 | # terminated: 6 | oCdbWrapper.oCdbCurrentProcess.bTerminated = True; 7 | oCdbWrapper.fbFireCallbacks("Process terminated", oCdbWrapper.oCdbCurrentProcess); 8 | oCdbWrapper.fbFireCallbacks("Log message", "Terminated application process", { 9 | "Process id": oCdbWrapper.oCdbCurrentProcess.uId, 10 | "Is main process": (oCdbWrapper.oCdbCurrentProcess.uId in oCdbWrapper.auMainProcessIds) and "yes" or "no", 11 | }); 12 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fHandleDebugOutputFromApplication.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | def cCdbWrapper_fHandleDebugOutputFromApplication(oCdbWrapper, asbOutputWhileRunningApplication): 4 | if oCdbWrapper.oCdbCurrentProcess.uId == oCdbWrapper.oUtilityProcess.uId: 5 | return; # We do not expect these but if we do see them, they should be ignored. 6 | # Unfortunately, cdb outputs text whenever an ignored first chance exception happens and I cannot find out how to 7 | # silence it. So, we'll have to remove these from the output, which is sub-optimal, but should work well enough 8 | # for now. Also, page heap outputs stuff that we don't care about as well, which we hide here. 9 | asbDebugOutput = [ 10 | sbLine for sbLine in asbOutputWhileRunningApplication 11 | if not re.match(rb"^(%s)$" % rb"|".join([ 12 | rb"\(\w+\.\w+\): Unknown exception \- code \w{8} \(first chance\)", 13 | rb"Page heap: pid 0x\w+: page heap enabled with flags 0x\w+\.", 14 | ]), sbLine) 15 | ]; 16 | if asbDebugOutput: 17 | # It could be that the output was from page heap, in which case no event is fired. 18 | oCdbWrapper.fbFireCallbacks("Application debug output", oCdbWrapper.oCdbCurrentProcess, asbDebugOutput); 19 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fHandleExceptionInUtilityProcess.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import fbTerminateForThreadId; 2 | from mWindowsSDK import STATUS_ACCESS_VIOLATION, STATUS_BREAKPOINT, STATUS_STACK_BUFFER_OVERRUN; 3 | 4 | def cCdbWrapper_fHandleExceptionInUtilityProcess(oCdbWrapper, uExceptionCode, sRelatedErrorDefineName): 5 | if uExceptionCode == STATUS_BREAKPOINT: 6 | if oCdbWrapper.oCdbCurrentWindowsAPIThread.uId != oCdbWrapper.u0UtilityInterruptThreadId: 7 | # cdb.exe will start a new thread in the utility process and trigger a 8 | # breakpoint to halt execution when the user presses CTRL+C 9 | # The user pressed CTRL+BREAK: stop debugging 10 | oCdbWrapper.fbFireCallbacks( 11 | "Log message", 12 | "User interrupted BugId using CTRL+BREAK.", 13 | ); 14 | oCdbWrapper.fStop(); 15 | # debug breakpoints in the interrupt thread in the utility process are unwanted 16 | # but cannot be avoided, so we ignore them. 17 | elif uExceptionCode in [STATUS_ACCESS_VIOLATION, STATUS_STACK_BUFFER_OVERRUN]: 18 | if oCdbWrapper.oCdbCurrentWindowsAPIThread.uId == oCdbWrapper.u0PreviousUtilityInterruptThreadId: 19 | oCdbWrapper.fbFireCallbacks("Log message", "Interrupt thread access violation echo ignored in utility process", { 20 | "Thread id": oCdbWrapper.u0UtilityInterruptThreadId, 21 | }); 22 | return; 23 | oCdbWrapper.fbFireCallbacks("Log message", "Interrupt thread access violation handled in utility process", { 24 | "Thread id": oCdbWrapper.u0UtilityInterruptThreadId, 25 | }); 26 | # cdb has two bugs: 27 | # * on x86, it will freak out if we terminate the thread now, so we will 28 | # need to suspend the thread to prevent the unhandled exception from 29 | # terminating the utility process, and terminate it later. We terminate 30 | # it once we want to interrupt the application again. 31 | # * on x64, it will report the access violation in the utility process over 32 | # and over if we suspend the process, so we terminate the thread now. 33 | if oCdbWrapper.sCdbISA == "x86": 34 | oCdbWrapper.fasbExecuteCdbCommand( 35 | sbCommand = b"~;~f;~", 36 | sb0Comment = b"Suspend thread to prevent AV from terminating the process", 37 | ); 38 | oCdbWrapper.fbFireCallbacks("Log message", "Interrupt thread suspended in utility process", { 39 | "Thread id": oCdbWrapper.u0UtilityInterruptThreadId, 40 | }); 41 | oCdbWrapper.u0PreviousUtilityInterruptThreadId = oCdbWrapper.u0UtilityInterruptThreadId; 42 | else: 43 | oCdbWrapper.fbFireCallbacks("Log message", "Interrupt thread terminated in utility process", { 44 | "Thread id": oCdbWrapper.u0UtilityInterruptThreadId, 45 | }); 46 | assert not fbTerminateForThreadId(oCdbWrapper.u0UtilityInterruptThreadId, bWait = False), \ 47 | "Could not terminate previous utility process interrupt thread"; 48 | oCdbWrapper.u0UtilityInterruptThreadId = None; 49 | # We started a new thread in the utility process and triggered an access violation 50 | # to halt execution. 51 | # Terminate the thread in which we triggered an AV, so the utility process can continue running. 52 | # Since it is suspended, it will not terminate immediately, so we don't wait for it. 53 | # Mark the interrupt as handled. 54 | oCdbWrapper.fbFireCallbacks( 55 | "Log message", 56 | "Application interrupted", 57 | ); 58 | else: 59 | raise AssertionError( 60 | "Unhandled exception in utility process utility thread %d: 0x%X %s." % ( 61 | oCdbWrapper.u0UtilityInterruptThreadId, 62 | uExceptionCode, 63 | sRelatedErrorDefineName, 64 | )); 65 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fInterruptApplicationExecution.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import fbTerminateForThreadId, fuCreateThreadForProcessIdAndAddress; 2 | from mMultiThreading import cLock; 3 | 4 | # We want to avoid multiple threads executing this code in parallel, as that 5 | # could cause TOCTOU issues. We will use a lock to ensure that is can only be 6 | # executed serially. The lock should only ever be held for a few milliseconds, 7 | # so report a deadlock if it takes longer than 10 seconds to acquire the lock. 8 | # There is no scenario in which this is expected to happen though. 9 | oLock = cLock(n0DeadlockTimeoutInSeconds = 10); 10 | 11 | def cCdbWrapper_fInterruptApplicationExecution(oCdbWrapper): 12 | oLock.fAcquire(); 13 | try: 14 | assert oCdbWrapper.u0UtilityInterruptThreadId is None, \ 15 | "Interrupt thread 0x%X already created!" % oCdbWrapper.u0UtilityInterruptThreadId; 16 | if oCdbWrapper.u0PreviousUtilityInterruptThreadId: 17 | oCdbWrapper.fbFireCallbacks("Log message", "Previous interrupt thread terminated in utility process", { 18 | "Thread id": oCdbWrapper.u0PreviousUtilityInterruptThreadId, 19 | }); 20 | assert not fbTerminateForThreadId(oCdbWrapper.u0PreviousUtilityInterruptThreadId, bWait = False), \ 21 | "Could not terminate previous utility process interrupt thread"; 22 | oCdbWrapper.u0PreviousUtilityInterruptThreadId = None; 23 | # Asking cdb to interrupt the application can cause it to inject a thread that triggers an int 3. Unfortunately, 24 | # we have no reliable way of determining if this is the case or if the application itself has triggered an int 3. 25 | # 26 | # To remove any confusion, we will create a utility process that we also debug. In this utility process, we will 27 | # create a new thread and have it attempt to execute code at address 0. This will cause an access violation, which 28 | # will interrupt the application and cause cdb to report it. We can easily distinguish this exception from any other 29 | # exception caused by the target application because it will be reported to have happened in the utility process. 30 | assert oCdbWrapper.bCdbIsRunning, \ 31 | "Cannot interrupt application if cdb is not running!"; 32 | assert oCdbWrapper.bApplicationIsRunning, \ 33 | "Cannot interrupt application if it is not running!"; 34 | oCdbWrapper.u0UtilityInterruptThreadId = fuCreateThreadForProcessIdAndAddress(oCdbWrapper.oUtilityProcess.uId, 0xB1D); # B1D -> BugId 35 | oCdbWrapper.fbFireCallbacks("Log message", "Interrupt thread created in utility process", { 36 | "Thread id": oCdbWrapper.u0UtilityInterruptThreadId, 37 | }); 38 | print("#" * 80); 39 | print("### INTERRUPT THREAD ID: 0x%X (access violation queued)" % oCdbWrapper.u0UtilityInterruptThreadId); 40 | print("#" * 80); 41 | finally: 42 | oLock.fRelease(); -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fQueueAttachForProcessExecutableNames.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import fds0GetProcessesExecutableName_by_uId; 2 | 3 | def cCdbWrapper_fQueueAttachForProcessExecutableNames(oCdbWrapper, *asExecutableNames): 4 | asExecutableNamesLowered = [s.lower() for s in asExecutableNames]; 5 | for (uProcessId, s0ExecutableName) in fds0GetProcessesExecutableName_by_uId().items(): 6 | # If it is running one of the executables, check if it is being debugged: 7 | if s0ExecutableName is not None and s0ExecutableName.lower() in asExecutableNamesLowered: 8 | # If it is not yet being debugged, queue it for attaching: 9 | if uProcessId not in oCdbWrapper.doProcess_by_uId: 10 | oCdbWrapper.fQueueAttachForProcessId(uProcessId); 11 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fQueueAttachForProcessId.py: -------------------------------------------------------------------------------- 1 | def cCdbWrapper_fQueueAttachForProcessId(oCdbWrapper, uProcessId): 2 | assert oCdbWrapper.oCdbConsoleProcess is not None, \ 3 | "You cannot attach to a process when cdb is not running." 4 | # Queue the process id for attaching when the application is paused and cdb is 5 | # accepting commands (it might accept commands now, but it's easier to always 6 | # queue and have the main cdb std I/O loop handle executing the attach 7 | # commands). 8 | oCdbWrapper.auProcessIdsPendingAttach.append(uProcessId); 9 | if oCdbWrapper.bApplicationIsRunning: 10 | oCdbWrapper.fInterrupt( 11 | "Attaching to process %d/0x%X" % (uProcessId, uProcessId), 12 | ); 13 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fRemoveBreakpoint.py: -------------------------------------------------------------------------------- 1 | def cCdbWrapper_fRemoveBreakpoint(oCdbWrapper, uBreakpointId): 2 | uProcessId = oCdbWrapper.duProcessId_by_uBreakpointId[uBreakpointId]; 3 | oCdbWrapper.fSelectProcessId(uProcessId); 4 | # There is a bug in cdb where using "bc" to clear a breakpoint can still lead to a STATUS_BREAKPOINT exception at 5 | # the original address later. To work around this, we add the address of the breakpoint to a list of old breakpoints 6 | # and ignore any STATUS_BREAKPOINT exception at this address. There is a chance that the original instruction at 7 | # the breakpoint address triggers a real STATUS_BREAKPOINT exception, which we would then ignore by mistake. 8 | # However, I expect that chance of that happening to be negligible, so I make no attempts to address it. 9 | oCdbWrapper.fasbExecuteCdbCommand( 10 | sbCommand = b'bc%d;' % uBreakpointId, 11 | sb0Comment = b'Remove breakpoint', 12 | ); 13 | uAddress = oCdbWrapper.duAddress_by_uBreakpointId[uBreakpointId]; 14 | oCdbWrapper.dauOldBreakpointAddresses_by_uProcessId.setdefault(uProcessId, []).append(uAddress); 15 | del oCdbWrapper.duProcessId_by_uBreakpointId[uBreakpointId]; 16 | del oCdbWrapper.duAddress_by_uBreakpointId[uBreakpointId]; 17 | del oCdbWrapper.dfCallback_by_uBreakpointId[uBreakpointId]; 18 | oCdbWrapper.fbFireCallbacks("Log message", "Removed breakpoint", { 19 | "Breakpoint id": "%d" % uBreakpointId, 20 | "Process id": "%d/0x%X" % (uProcessId, uProcessId), 21 | }); 22 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fRunTimeoutCallbacks.py: -------------------------------------------------------------------------------- 1 | def cCdbWrapper_fRunTimeoutCallbacks(oCdbWrapper): 2 | ### Run timeout callbacks ###################################################################################### 3 | # Execute any pending timeout callbacks (this can happen when the interrupt on timeout thread has interrupted 4 | # the application or whenever the application is paused for another exception - the interrupt on timeout thread 5 | # is just there to make sure the application gets interrupted to do so when needed: otherwise the timeout may not 6 | # fire until an exception happens by chance). 7 | while 1: 8 | # Timeouts can create new timeouts, which may need to fire immediately, so this is run in a loop until no more 9 | # timeouts need to be fired. 10 | aoTimeoutsToFire = []; 11 | for oTimeout in oCdbWrapper.aoTimeouts[:]: 12 | if oTimeout.fbShouldFire(oCdbWrapper.nApplicationRunTimeInSeconds): 13 | oCdbWrapper.aoTimeouts.remove(oTimeout); 14 | aoTimeoutsToFire.append(oTimeout); 15 | if not aoTimeoutsToFire: 16 | return; 17 | for oTimeoutToFire in aoTimeoutsToFire: 18 | oTimeoutToFire.fFire(oCdbWrapper); 19 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fSaveDumpToFile.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertType; 2 | 3 | def cCdbWrapper_fSaveDumpToFile(oCdbWrapper, sFilePath, bOverwrite, bFull): 4 | fAssertType("sFilePath", sFilePath, str); 5 | asbFlags = [s for s in [ 6 | b"/o" if bOverwrite else None, 7 | b"/mAfFhuty" if bFull else b"/miR", 8 | ] if s]; 9 | oCdbWrapper.fasbExecuteCdbCommand( \ 10 | sbCommand = b".dump %s \"%s\";" % (b" ".join(asbFlags), bytes(sFilePath, "ascii", "strict")), 11 | sb0Comment = b"Save dump to file", 12 | ); 13 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fSelectProcessIdAndThreadId.py: -------------------------------------------------------------------------------- 1 | from ..mCP437 import fsCP437FromBytesString; 2 | 3 | def cCdbWrapper_fSelectProcessIdAndThreadId(oCdbWrapper, uProcessId = None, uThreadId = None): 4 | # Both arguments are optional 5 | sbSelectCommand = b""; 6 | asbSelected = []; 7 | if uProcessId is not None and (oCdbWrapper.oCdbCurrentProcess is None or uProcessId != oCdbWrapper.oCdbCurrentProcess.uId): 8 | # Select process if it is not yet the current process. 9 | assert uProcessId in oCdbWrapper.doProcess_by_uId, \ 10 | "Unknown process id %d/0x%X" % (uProcessId, uProcessId); 11 | sbSelectCommand += b"|~[0x%X]s;" % uProcessId; 12 | asbSelected.append(b"process"); 13 | # Assuming there's no error, track the new current process. 14 | oCdbWrapper.oCdbCurrentProcess = oCdbWrapper.doProcess_by_uId[uProcessId]; 15 | if oCdbWrapper.oCdbCurrentProcess.sISA != oCdbWrapper.sCdbCurrentISA: 16 | # Select process ISA if it is not yet the current ISA. ".block{}" is required 17 | sbSelectCommand += b".block{.effmach %s;};" % {"x86": b"x86", "x64": b"amd64"}[oCdbWrapper.oCdbCurrentProcess.sISA]; 18 | # Assuming there's no error, track the new current isa. 19 | oCdbWrapper.sCdbCurrentISA = oCdbWrapper.oCdbCurrentProcess.sISA; 20 | asbSelected.append(b"isa"); 21 | if uThreadId is not None: 22 | # We're not tracking the current thread; always set this. 23 | # TODO: track the current thread to reduce the number of times we may need to execute this command: 24 | sbSelectCommand += b"~~[0x%X]s;" % uThreadId; 25 | oCdbWrapper.oCdbCurrentWindowsAPIThread = oCdbWrapper.oCdbCurrentProcess.foGetWindowsAPIThreadForId(uThreadId); 26 | asbSelected.append(b"thread"); 27 | if sbSelectCommand: 28 | # We need to select a different process, isa or thread in cdb. 29 | asbSelectCommandOutput = oCdbWrapper.fasbExecuteCdbCommand( 30 | sbCommand = sbSelectCommand, 31 | sb0Comment = b"Select %s" % b"/".join(asbSelected), 32 | ); 33 | # cdb may or may not output the last instruction :S. But it will always output the isa on the last line if selected. 34 | if b"isa" in asbSelected: 35 | bUnexpectedOutput = asbSelectCommandOutput[-1] not in [ 36 | b"Effective machine: x86 compatible (x86)", 37 | b"Effective machine: x64 (AMD64)" 38 | ]; 39 | else: 40 | bUnexpectedOutput = False; #len(asbSelectCommandOutput) != 0; 41 | assert not bUnexpectedOutput, \ 42 | "Unexpected select %s output:\r\n%s" % \ 43 | (b"/".join(asbSelected), "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbSelectCommandOutput)); 44 | if b"process" in asbSelected and b"thread" not in asbSelected: 45 | # We changed the process, but did not choose a thread; find out what the new thread is: 46 | uThreadId = oCdbWrapper.fuGetValueForRegister(b"$tid", b"Get current thread id"); 47 | oCdbWrapper.oCdbCurrentWindowsAPIThread = oCdbWrapper.oCdbCurrentProcess.foGetWindowsAPIThreadForId(uThreadId); 48 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fStartUWPApplication.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertType; 2 | 3 | def cCdbWrapper_fStartUWPApplication(oCdbWrapper, oUWPApplication, sb0Argument): 4 | fAssertType("sb0Argument", sb0Argument, bytes, None); 5 | sbPackageName = bytes(oUWPApplication.sPackageName, "ascii", "strict"); 6 | sbPackageFullName = bytes(oUWPApplication.sPackageFullName, "ascii", "strict"); 7 | sbApplicationId = bytes(oUWPApplication.sApplicationId, "ascii", "strict"); 8 | if sb0Argument is None: 9 | # Note that the space between the application id and the command-terminating semi-colon MUST be there to 10 | # make sure the semi-colon is not interpreted as part of the application id! 11 | sbStartUWPApplicationCommand = b".createpackageapp %s %s ;" % \ 12 | (sbPackageFullName, sbApplicationId); 13 | oCdbWrapper.fbFireCallbacks("Log message", "Starting UWP application", { 14 | "Application Id": oUWPApplication.sApplicationId, 15 | "Package name": oUWPApplication.sPackageName, 16 | "Package full name": oUWPApplication.sPackageFullName, 17 | }); 18 | else: 19 | # Note that the space between the argument and the command-terminating semi-colon MUST be there to make 20 | # sure the semi-colon is not passed to the UWP app as part of the argument! 21 | sbStartUWPApplicationCommand = b".createpackageapp %s %s %s ;" % \ 22 | (sbPackageFullName, sbApplicationId, sb0Argument); 23 | oCdbWrapper.fbFireCallbacks("Log message", "Starting UWP application", { 24 | "Application Id": oUWPApplication.sApplicationId, 25 | "Package name": oUWPApplication.sPackageName, 26 | "Package full name": oUWPApplication.sPackageFullName, 27 | "Argument": sb0Argument, 28 | }); 29 | asbStartUWPApplicationOutput = oCdbWrapper.fasbExecuteCdbCommand( 30 | sbCommand = sbStartUWPApplicationCommand, 31 | sb0Comment = b"Start UWP application %s" % sbPackageName, 32 | ); 33 | assert asbStartUWPApplicationOutput == [b"Attach will occur on next execution"], \ 34 | "Unexpected .createpackageapp output: %s" % repr(asbStartUWPApplicationOutput); 35 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fTerminateUWPApplication.py: -------------------------------------------------------------------------------- 1 | from ..mCP437 import fsCP437FromBytesString; 2 | 3 | def cCdbWrapper_fTerminateUWPApplication(oCdbWrapper, oUWPApplication): 4 | oCdbWrapper.fbFireCallbacks("Log message", "Terminating UWP application", { 5 | "Application Id": oUWPApplication.sApplicationId, 6 | "Package name": oUWPApplication.sPackageName, 7 | "Package full name": oUWPApplication.sPackageFullName, 8 | }); 9 | # Kill it so we are sure to run a fresh copy. 10 | sbPackageName = bytes(oUWPApplication.sPackageName, "ascii", "strict"); 11 | sbPackageFullName = bytes(oUWPApplication.sPackageFullName, "ascii", "strict"); 12 | asbTerminateUWPApplicationOutput = oCdbWrapper.fasbExecuteCdbCommand( 13 | sbCommand = b".terminatepackageapp %s;" % sbPackageFullName, 14 | sb0Comment = b"Terminate UWP application %s" % sbPackageName, 15 | ); 16 | if asbTerminateUWPApplicationOutput == [ 17 | b"Failed - error: HRESULT 0x80010108", 18 | b" \"The object invoked has disconnected from its clients.\"", 19 | ]: 20 | # This suggest that the command was not executed successfully because an RPC channel broke. I'm trying to find out 21 | # if this can be solved by executing the command again (assuming a new RPC channel will get set up and this second 22 | # call will succeed). In order to find out, I will throw an assertion error either way and have the error message 23 | # report whether retrying is useful or not. If it is, this code should probably be in a loop that retries N times 24 | # until success, or reports an error if it still fails after that many tries. 25 | asbTerminateUWPApplicationOutput2 = oCdbWrapper.fasbExecuteCdbCommand( 26 | sbCommand = b".terminatepackageapp %s;" % sbPackageFullName, 27 | sb0Comment = b"Terminate UWP application %s" % sbPackageName, 28 | ); 29 | assert asbTerminateUWPApplicationOutput == asbTerminateUWPApplicationOutput2, \ 30 | "Cannot terminate UWP App and repeating the .terminatepackageapp command does not appear to be useful: %s" % \ 31 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbTerminateUWPApplicationOutput2); 32 | assert False, \ 33 | "Cannot terminate UWP App and repeating the .terminatepackageapp command gives an unknown error: %s" % \ 34 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbTerminateUWPApplicationOutput2); 35 | if asbTerminateUWPApplicationOutput: 36 | assert asbTerminateUWPApplicationOutput == [b'The "terminatePackageApp" action will be completed on next execution.'], \ 37 | "Unexpected .terminatepackageapp output:\r\n%s" % \ 38 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbTerminateUWPApplicationOutput); 39 | 40 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fUpdateCdbISA.py: -------------------------------------------------------------------------------- 1 | def cCdbWrapper_fUpdateCdbISA(oCdbWrapper): 2 | if oCdbWrapper.oCdbCurrentProcess.sISA != oCdbWrapper.sCdbCurrentISA: 3 | # Select process ISA if it is not yet the current ISA. ".block{}" is required 4 | oCdbWrapper.fasbExecuteCdbCommand( 5 | sbCommand = b".effmach %s;" % {"x86": b"x86", "x64": b"amd64"}[oCdbWrapper.oCdbCurrentProcess.sISA], 6 | sb0Comment = b"Switch to current process ISA", 7 | bRetryOnTruncatedOutput = True, 8 | ); 9 | # Assuming there's no error, track the new current isa. 10 | oCdbWrapper.sCdbCurrentISA = oCdbWrapper.oCdbCurrentProcess.sISA; 11 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_foSetTimeout.py: -------------------------------------------------------------------------------- 1 | import time; 2 | from ..cTimeout import cTimeout; 3 | 4 | def cCdbWrapper_foSetTimeout(oCdbWrapper, sDescription, nTimeoutInSeconds, f0Callback = None, txCallbackArguments = []): 5 | assert nTimeoutInSeconds >= 0, "Negative timeout time does not make sense"; 6 | oCdbWrapper.oApplicationTimeLock.fAcquire(); 7 | try: 8 | nFireAtOrAfterApplicationRunTimeInSeconds = nTimeoutInSeconds and oCdbWrapper.nApplicationRunTimeInSeconds + nTimeoutInSeconds; 9 | if oCdbWrapper.bApplicationIsRunning: 10 | # The application is currently running, make an estimate for how long to determine when to stop the application: 11 | nFireAtOrAfterApplicationRunTimeInSeconds += time.time() - oCdbWrapper.n0ApplicationResumeTimeInSeconds; 12 | finally: 13 | oCdbWrapper.oApplicationTimeLock.fRelease(); 14 | oTimeout = cTimeout(sDescription, nFireAtOrAfterApplicationRunTimeInSeconds, f0Callback, txCallbackArguments); 15 | oCdbWrapper.aoTimeouts.append(oTimeout); 16 | return oTimeout; 17 | -------------------------------------------------------------------------------- /cCdbWrapper/cCdbWrapper_fuGetValueForRegister.py: -------------------------------------------------------------------------------- 1 | from ..fu0ValueFromCdbHexOutput import fu0ValueFromCdbHexOutput; 2 | from ..mCP437 import fsCP437FromBytesString; 3 | 4 | def cCdbWrapper_fuGetValueForRegister(oCdbWrapper, sbRegister, sb0Comment): 5 | # This is a register or pseudo-register: it's much faster to get these using the "r" command than printing them 6 | # as is done for other values: 7 | asbCommandOutput = oCdbWrapper.fasbExecuteCdbCommand( 8 | sbCommand = b"r @%s;" % sbRegister, 9 | sb0Comment = sb0Comment, 10 | ); 11 | assert len(asbCommandOutput) == 1, \ 12 | "Expected exactly one line in \"r\" command output:\r\n%s" % \ 13 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbCommandOutput); 14 | asbRegisterAndValueResult = asbCommandOutput[0].split(b"=", 1); 15 | assert len(asbRegisterAndValueResult) == 2, \ 16 | "Missing \"=\" in result:\r\n%s" % \ 17 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbCommandOutput); 18 | sbRegisterResult, sbValue = asbRegisterAndValueResult; 19 | assert sbRegisterResult.lower() == sbRegister.lower(), \ 20 | "Expected result to start with %s, not %s\r\n%s" % ( 21 | repr(sbRegister.lower() + "="), 22 | repr(sbRegisterResult + "="), 23 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbCommandOutput) 24 | ); 25 | try: 26 | return fu0ValueFromCdbHexOutput(sbValue); 27 | except: 28 | raise AssertionError( 29 | "Cannot parse value %s for %s:\r\n%s" % ( 30 | repr(sbValue), 31 | sbRegister, 32 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbCommandOutput) 33 | ) 34 | ); 35 | -------------------------------------------------------------------------------- /cCollateralBugHandler/__init__.py: -------------------------------------------------------------------------------- 1 | from .cCollateralBugHandler import cCollateralBugHandler; -------------------------------------------------------------------------------- /cCollateralBugHandler/cCollateralBugHandler_fbPoisonFlags.py: -------------------------------------------------------------------------------- 1 | 2 | def cCollateralBugHandler_fbPoisonFlags( 3 | oSelf, 4 | oCdbWrapper, 5 | oProcess, 6 | oThread, 7 | sInstruction, 8 | duPoisonedRegisterValue_by_sbName, 9 | u0PointerSizedOriginalValue, 10 | ): 11 | # This instruction will affect flags, so we'll read bits from the poisoned values to use as flags. 12 | asbFlagNames = [b"of", b"sf", b"zf", b"af", b"pf", b"cf"]; 13 | # Accumulate the flag bits into a single value 14 | uCurrentFlagsValue = 0; 15 | for uIndex in range(len(asbFlagNames)): 16 | sbFlagName = asbFlagNames[uIndex]; 17 | u0FlagValue = oThread.fu0GetRegister(sbFlagName); 18 | if u0FlagValue is None: 19 | oCdbWrapper.fFireCallbacks( 20 | "Bug cannot be ignored", 21 | "cannot read current flag %s value" % repr(sbFlagName)[1:], 22 | ); 23 | return False; 24 | uCurrentFlagsValue += u0FlagValue << uIndex; 25 | # Get a poisoned value to replace the flags: 26 | uPoisonedFlagsValue = oSelf.fuGetPoisonedValue( 27 | oProcess = oProcess, 28 | oWindowsAPIThread = oProcess.foGetWindowsAPIThreadForId(oThread.uId), 29 | sDestination = "flags(%s)" % ", ".join(str(sbFlagName, "ascii", "strict") for sbFlagName in asbFlagNames), 30 | sInstruction = sInstruction, 31 | i0CurrentValue = uCurrentFlagsValue, 32 | uBits = len(asbFlagNames), 33 | u0PointerSizedOriginalValue = u0PointerSizedOriginalValue, 34 | ); 35 | # Set the flags in the dict. 36 | for uIndex in range(len(asbFlagNames)): 37 | sFlagName = asbFlagNames[uIndex]; 38 | duPoisonedRegisterValue_by_sbName[sFlagName] = (uPoisonedFlagsValue >> uIndex) & 1; 39 | return True; -------------------------------------------------------------------------------- /cCollateralBugHandler/cCollateralBugHandler_fbPoisonRegister.py: -------------------------------------------------------------------------------- 1 | def cCollateralBugHandler_fbPoisonRegister( 2 | oSelf, 3 | oProcess, 4 | oThread, 5 | sInstruction, 6 | duPoisonedRegisterValue_by_sbName, 7 | u0PointerSizedOriginalValue, 8 | sbRegisterName, 9 | uSizeInBits, 10 | ): 11 | uPoisonValue = oSelf.fuGetPoisonedValue( 12 | oProcess = oProcess, 13 | oWindowsAPIThread = oProcess.foGetWindowsAPIThreadForId(oThread.uId), 14 | sDestination = str(b"register %s" % sbRegisterName, "ascii", "strict"), 15 | sInstruction = sInstruction, 16 | i0CurrentValue = oThread.fu0GetRegister(sbRegisterName), 17 | uBits = uSizeInBits, 18 | u0PointerSizedOriginalValue = u0PointerSizedOriginalValue, 19 | ); 20 | # print "Faked read %d bits (0x%X) into %d bits %s" % (uSourceSize, uPoisonValue, uDestinationSizeInBits, sbRegisterName); 21 | duPoisonedRegisterValue_by_sbName[sbRegisterName] = uPoisonValue; 22 | return True; -------------------------------------------------------------------------------- /cFunction.py: -------------------------------------------------------------------------------- 1 | class cFunction(object): 2 | def __init__(oSelf, oModule, sbSymbol): 3 | oSelf.oModule = oModule; 4 | oSelf.sbSymbol = sbSymbol; 5 | oSelf.sbCdbId = b"%s!%s" % (oModule.sbCdbId, sbSymbol); 6 | oSelf.sbName = b"%s!%s" % (oModule.sb0BinaryName or b"", sbSymbol); 7 | # Replace complex template stuff with "<...>" to make a symbol easier to read. 8 | asbComponents = [b""]; 9 | for uChar in sbSymbol: 10 | if uChar == ord("<"): 11 | asbComponents.append(b""); 12 | elif uChar == ord(">"): 13 | if len(asbComponents) == 1: 14 | asbComponents[-1] += b">"; # this is not closing a "<". 15 | else: 16 | sbTemplate = asbComponents.pop(); # discard contents of template 17 | asbComponents[-1] += b"<...>"; 18 | else: 19 | asbComponents[-1] += bytes((uChar,)); 20 | oSelf.sbSimpifiedSymbol = b"<".join(asbComponents); 21 | oSelf.sbSimplifiedName = b"%s!%s" % (oModule.sb0SimplifiedName or b"???", oSelf.sbSimpifiedSymbol); 22 | oSelf.sbUniqueName = b"%s!%s" % (oModule.sb0UniqueName or b"???", sbSymbol); 23 | -------------------------------------------------------------------------------- /cTimeout.py: -------------------------------------------------------------------------------- 1 | class cTimeout(object): 2 | def __init__(oTimeout, sDescription, nFireAtOrAfterApplicationRunTimeInSeconds, f0Callback = None, txCallbackArguments = tuple()): 3 | oTimeout.sDescription = sDescription; 4 | oTimeout.__nFireAtOrAfterApplicationRunTimeInSeconds = nFireAtOrAfterApplicationRunTimeInSeconds; 5 | oTimeout.__f0Callback = f0Callback; 6 | oTimeout.__txCallbackArguments = txCallbackArguments; 7 | 8 | def fbShouldFire(oTimeout, nApplicationRunTimeInSeconds): 9 | return nApplicationRunTimeInSeconds >= oTimeout.__nFireAtOrAfterApplicationRunTimeInSeconds; 10 | 11 | def fFire(oTimeout, oCdbWrapper): 12 | if oTimeout.__f0Callback: 13 | oTimeout.__f0Callback(oCdbWrapper, *oTimeout.__txCallbackArguments); -------------------------------------------------------------------------------- /cVerifierStopDetector/__init__.py: -------------------------------------------------------------------------------- 1 | from .cVerifierStopDetector import cVerifierStopDetector; 2 | 3 | __all__ = [ 4 | "cVerifierStopDetector", 5 | ]; -------------------------------------------------------------------------------- /dsDebuggingToolsPath_sISA.py: -------------------------------------------------------------------------------- 1 | import os; 2 | from mWindowsAPI import oSystemInfo; 3 | 4 | dasPotentialDebuggingToolsPaths_sISA = {"x86": [], "x64": []}; 5 | 6 | # Add "cdb", "cdb_x86" and "cdb_x64" environment variables if provided: 7 | sDebuggingToolsEnvironmentVariable = os.getenv("DebuggingTools"); 8 | if sDebuggingToolsEnvironmentVariable: 9 | dasPotentialDebuggingToolsPaths_sISA[oSystemInfo.sOSISA].append(sDebuggingToolsEnvironmentVariable.strip('"')); 10 | sDebuggingToolsEnvironmentVariable_x86 = os.getenv("DebuggingTools_x86"); 11 | if sDebuggingToolsEnvironmentVariable_x86: 12 | dasPotentialDebuggingToolsPaths_sISA["x86"].append(sDebuggingToolsEnvironmentVariable_x86.strip('"')); 13 | sDebuggingToolsEnvironmentVariable_x64 = os.getenv("DebuggingTools_x64"); 14 | if sDebuggingToolsEnvironmentVariable_x64: 15 | dasPotentialDebuggingToolsPaths_sISA["x64"].append(sDebuggingToolsEnvironmentVariable_x64.strip('"')); 16 | 17 | # Add default installation paths: 18 | sProgramFilesPath_x86 = os.getenv("ProgramFiles(x86)") or os.getenv("ProgramFiles"); 19 | sProgramFilesPath_x64 = os.getenv("ProgramW6432"); 20 | dasPotentialDebuggingToolsPaths_sISA["x86"].extend([ 21 | os.path.join(sProgramFilesPath_x86, "Windows Kits", "10", "Debuggers", "x86"), 22 | os.path.join(sProgramFilesPath_x86, "Windows Kits", "8.1", "Debuggers", "x86"), 23 | os.path.join(sProgramFilesPath_x86, "Debugging Tools for Windows (x86)"), 24 | ]); 25 | if oSystemInfo.sOSISA == "x64": 26 | dasPotentialDebuggingToolsPaths_sISA["x64"].extend([ 27 | os.path.join(sProgramFilesPath_x64, "Windows Kits", "10", "Debuggers", "x64"), 28 | os.path.join(sProgramFilesPath_x64, "Windows Kits", "8.1", "Debuggers", "x64"), 29 | os.path.join(sProgramFilesPath_x86, "Windows Kits", "10", "Debuggers", "x64"), 30 | os.path.join(sProgramFilesPath_x86, "Windows Kits", "8.1", "Debuggers", "x64"), 31 | os.path.join(sProgramFilesPath_x64, "Debugging Tools for Windows (x64)"), 32 | ]); 33 | 34 | dsDebuggingToolsPath_sISA = {}; 35 | for (sISA, asPotentialDebuggingToolsPaths) in dasPotentialDebuggingToolsPaths_sISA.items(): 36 | for sPotentialDebuggingToolsPath in asPotentialDebuggingToolsPaths: 37 | if os.path.isdir(sPotentialDebuggingToolsPath): 38 | dsDebuggingToolsPath_sISA[sISA] = sPotentialDebuggingToolsPath; 39 | break; 40 | -------------------------------------------------------------------------------- /dxProductDetails.json: -------------------------------------------------------------------------------- 1 | { 2 | "a0sDebugAdditionalProductNames": [ 3 | "mConsole", 4 | "mDebugOutput", 5 | "mFileSystemItem" 6 | ], 7 | "a0sDependentOnProductNames": [ 8 | "mDateTime", 9 | "mHumanReadable", 10 | "mMultiThreading", 11 | "mNotProvided", 12 | "mProductDetails", 13 | "mRegistry", 14 | "mWindowsAPI", 15 | "mWindowsSDK" 16 | ], 17 | "asProductTypes": [ 18 | "Python module" 19 | ], 20 | "o0Repository": { 21 | "sStructureVersion": "2021-07-02 11:16", 22 | "sType": "GitHub", 23 | "sbBranch": "master", 24 | "sbRepositoryName": "mBugId", 25 | "sbUserName": "SkyLined" 26 | }, 27 | "o0TrialPeriodDuration": "+1m", 28 | "oProductVersion": "2025-05-23 12:34", 29 | "s0PythonModuleName": "mBugId", 30 | "sProductAuthor": "SkyLined", 31 | "sProductName": "mBugId", 32 | "sStructureVersion": "2021-07-02 11:16", 33 | "sb0LicenseServerURL": "https://license.skylined.nl/", 34 | "sb0ProductURL": "https://github.com/SkyLined/mBugId" 35 | } -------------------------------------------------------------------------------- /fnGetDebuggerTimeInSeconds.py: -------------------------------------------------------------------------------- 1 | import datetime, re; 2 | 3 | gasbMonths = [b"Jan", b"Feb", b"Mar", b"Apr", b"May", b"Jun", b"Jul", b"Aug", b"Sep", b"Oct", b"Nov", b"Dec"]; 4 | grbDebuggerTime = re.compile( 5 | rb"^\s*" 6 | rb"(Mon|Tue|Wed|Thu|Fri|Sat|Sun)" # * Weekday 7 | rb"\s+" # whitespace 8 | rb"(" + rb"|".join(gasbMonths) + rb")" # * Month 9 | rb"\s+" # whitespace 10 | rb"(\d+)" # * Day in month 11 | rb"\s+" # whitespace 12 | rb"(\d+):(\d+):(\d+).(\d+)" # * Hour ":" Minute ":" Second "." Millisecond 13 | rb"\s+" # whitespace 14 | rb"(\d+)" # * Year 15 | rb"\s+" # whitespace 16 | rb"\(.*\)" # "(" Timezone ")" 17 | rb"\s*$" 18 | ); 19 | 20 | def fnGetDebuggerTimeInSeconds(sbDebuggerTime): 21 | # Parse .time and .lastevent timestamps; return a number of seconds since an arbitrary but constant starting point in time. 22 | obDebuggerTimeMatch = grbDebuggerTime.match(sbDebuggerTime); 23 | assert obDebuggerTimeMatch, \ 24 | "Cannot parse debugger time: %s" % repr(sbDebuggerTime); 25 | (sbWeekDay, sbMonth, sbDay, sbHour, sbMinute, sbSecond, sbMillisecond, sbYear) = \ 26 | obDebuggerTimeMatch.groups(); 27 | oDateTime = datetime.datetime( 28 | int(sbYear), 29 | gasbMonths.index(sbMonth) + 1, 30 | int(sbDay), 31 | int(sbHour), 32 | int(sbMinute), 33 | int(sbSecond), 34 | int(sbMillisecond.ljust(6, b"0")), 35 | ); 36 | # Convert to a floating point number by calculating the number of seconds since 37 | # an arbitrarily chosen epoch. 38 | return (oDateTime - datetime.datetime(1976,8,28)).total_seconds(); 39 | 40 | -------------------------------------------------------------------------------- /fsGetNumberDescription.py: -------------------------------------------------------------------------------- 1 | from .dxConfig import dxConfig; 2 | 3 | def fsGetNumberDescription(uNumber, sSign = "+"): 4 | # This function converts a number into an architecture independent number. 5 | # e.g. 1 -> 1, 7 -> 4n+3, see dxConfig for more details. 6 | uArchitectureIndependentBugIdBytes = (dxConfig["uArchitectureIndependentBugIdBits"] or 0) / 8; 7 | if uArchitectureIndependentBugIdBytes == 0 or uNumber < uArchitectureIndependentBugIdBytes: 8 | # Architecture independent bug ids are disabled, or the number is too small to require fixing. 9 | if uNumber < 10: 10 | return "%d" % uNumber; 11 | return "0x%X" % uNumber; 12 | sDescription = "%dn" % uArchitectureIndependentBugIdBytes; 13 | uRemainder = uNumber % uArchitectureIndependentBugIdBytes; 14 | if uRemainder: 15 | sDescription += sSign + fsGetNumberDescription(uRemainder); 16 | return sDescription; 17 | -------------------------------------------------------------------------------- /fsNumberOfBytes.py: -------------------------------------------------------------------------------- 1 | def fsNumberOfBytes(uNumberOfBytes): 2 | if uNumberOfBytes == 1: 3 | return "1 byte"; 4 | elif uNumberOfBytes < 10: 5 | return "%d bytes" % uNumberOfBytes; 6 | else: 7 | return "%d/0x%X bytes" % (uNumberOfBytes, uNumberOfBytes); 8 | -------------------------------------------------------------------------------- /fsbGetCPPObjectClassNameFromVFTable.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | from .mCP437 import fsCP437FromBytesString; 4 | 5 | grbVFTableSymbolClassName = re.compile( 6 | rb"^" 7 | rb"[0-9`A-F]+" # number 8 | rb"\s*" # whitespace 9 | rb"[0-9`A-F\?]+" # number 10 | rb"\s+" # whitespace 11 | rb"\w+" rb"!" rb"(.+?)" rb"::`vftable'" # module "!" **classname** "::`vftable'" 12 | rb"\s*$", # optional whitespace 13 | re.I 14 | ) 15 | 16 | def fsbGetCPPObjectClassNameFromVFTable(oProcess, uCPPObjectAddress): 17 | # A C++ object is store in memory starting with a pointer to its virtual function table. 18 | # The symbol for this virtual function table should follow the pattern "module![namespace::]classname::`vftable'" 19 | # We can extract the classname from this symbol. 20 | asbVFTableSymbolOutput = oProcess.fasbExecuteCdbCommand( 21 | sbCommand = b"dps 0x%X L1;" % uCPPObjectAddress, 22 | sb0Comment = b"Get C++ vftable pointer symbol", 23 | bOutputIsInformative = True, 24 | ); 25 | assert len(asbVFTableSymbolOutput) == 1, \ 26 | "Unexpected vftable pointer symbol output:\r\n%s" % "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbVFTableSymbolOutput); 27 | obVFTableSymbolClassNameMatch = grbVFTableSymbolClassName.match(asbVFTableSymbolOutput[0]); 28 | return obVFTableSymbolClassNameMatch.group(1) if obVFTableSymbolClassNameMatch else None; 29 | -------------------------------------------------------------------------------- /ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress.py: -------------------------------------------------------------------------------- 1 | from .fsGetNumberDescription import fsGetNumberDescription; 2 | from .fsNumberOfBytes import fsNumberOfBytes; 3 | 4 | def ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress(uBlockStartAddress, uBlockSize, sBlockType, uAddress): 5 | sSizeId = "[%s]" % fsGetNumberDescription(uBlockSize); 6 | sSizeDescription = "a %s %s block at 0x%X" % (fsNumberOfBytes(uBlockSize), sBlockType, uBlockStartAddress); 7 | 8 | iOffsetFromStartOfBlock = uAddress - uBlockStartAddress; 9 | if iOffsetFromStartOfBlock < 0: 10 | # "-X" means at X bytes before the block 11 | sOffsetId = "-%s" % fsGetNumberDescription(-iOffsetFromStartOfBlock, "-"); 12 | sOffsetDescription = "%s before" % fsNumberOfBytes(-iOffsetFromStartOfBlock); 13 | else: 14 | iOffsetFromEndOfBlock = iOffsetFromStartOfBlock - uBlockSize; 15 | if iOffsetFromEndOfBlock < 0: 16 | # "@X" means at X bytes within the block 17 | sOffsetId = "@%s" % fsGetNumberDescription(iOffsetFromStartOfBlock); 18 | if iOffsetFromStartOfBlock == 0: 19 | sOffsetDescription = "at the start of"; 20 | else: 21 | sOffsetDescription = "%s into" % fsNumberOfBytes(iOffsetFromStartOfBlock); 22 | else: 23 | # "+X" means X bytes beyond the block 24 | sOffsetId = "+%s" % fsGetNumberDescription(iOffsetFromEndOfBlock); 25 | if iOffsetFromEndOfBlock == 0: 26 | sOffsetDescription = "at the end of"; 27 | else: 28 | sOffsetDescription = "%s beyond" % fsNumberOfBytes(iOffsetFromEndOfBlock); 29 | return (sSizeId, sOffsetId, sOffsetDescription, sSizeDescription); 30 | -------------------------------------------------------------------------------- /ftsReportProductHeaderAndFooterHTML.py: -------------------------------------------------------------------------------- 1 | from .mCP437 import fsCP437FromBytesString; 2 | 3 | def ftsReportProductHeaderAndFooterHTML(oProductDetails): 4 | sProductHeaderHTML = ( 5 | "" 6 | "%(sProductName)s version %(sProductVersion)s" 7 | " by %(sProductAuthor)s." 8 | "" 9 | ) % { 10 | "sProductName": oProductDetails.sProductName, 11 | "sProductVersion": oProductDetails.oProductVersion, 12 | "sProductAuthor": oProductDetails.sProductAuthor, 13 | "sProductURL": fsCP437FromBytesString(oProductDetails.sb0ProductURL), 14 | }; 15 | sProductFooter = ( 16 | "" 17 | "This report was generated using " 18 | "%(sProductName)s version %(sProductVersion)s" 19 | " by %(sProductAuthor)s.
" 20 | "
" 21 | ) % { 22 | "sProductName": oProductDetails.sProductName, 23 | "sProductVersion": oProductDetails.oProductVersion, 24 | "sProductAuthor": oProductDetails.sProductAuthor, 25 | "sProductURL": fsCP437FromBytesString(oProductDetails.sb0ProductURL), 26 | }; 27 | return (sProductHeaderHTML, sProductFooter); 28 | -------------------------------------------------------------------------------- /ftuLimitedAndAlignedMemoryDumpStartAddressAndSize.py: -------------------------------------------------------------------------------- 1 | from .dxConfig import dxConfig; 2 | 3 | # This code: 4 | # 1) limits memory dumps to reasonable sizes by moving the start and/or end address, while making sure the memory 5 | # around the most interesting address is always in the dump, preferably near the middle. 6 | # 2) Rounds the start address down and the size up to align both with the pointer size of the ISA. 7 | def ftuLimitedAndAlignedMemoryDumpStartAddressAndSize(uMostInterestingAddress, uPointerSize, uMemoryDumpStartAddress, uMemoryDumpSize): 8 | # Calculate start and end address aligned to pointer size: 9 | uMemoryDumpEndAddress = uMemoryDumpStartAddress + uMemoryDumpSize; 10 | uPointerSizeMask = uPointerSize - 1; 11 | uMemoryDumpStartAddress -= uMemoryDumpStartAddress & uPointerSizeMask; # decrease to align 12 | if uMemoryDumpEndAddress & uPointerSizeMask: 13 | uMemoryDumpEndAddress -= uMemoryDumpStartAddress & uPointerSizeMask; # decrease to align 14 | uMemoryDumpEndAddress += uPointerSize; # increase again to include original end address. 15 | # Recalculate the size of the memory area to be dumped: 16 | uMemoryDumpSize = uMemoryDumpEndAddress - uMemoryDumpStartAddress; 17 | 18 | if uMemoryDumpSize > dxConfig["uMaxMemoryDumpSize"]: 19 | # Yes, we'll need to reduce the size of the memory dump; try to find out what parts are farthest away 20 | # from the exception address and remove those. uMemoryDumpStartAddress and/or uMemoryDumpSize are updated. 21 | uMemoryDumpSizeBeforeMostInterestingAddress = uMostInterestingAddress - uMemoryDumpStartAddress; 22 | uMemoryDumpSizeAfterMostInterestingAddress = uMemoryDumpEndAddress - uMostInterestingAddress; 23 | # Regardless of where we remove bytes from the dump, the size will become the maximum size: 24 | uMemoryDumpSize = dxConfig["uMaxMemoryDumpSize"]; 25 | if uMemoryDumpSizeBeforeMostInterestingAddress < dxConfig["uMaxMemoryDumpSize"] / 2: 26 | # The size before the address is reasonable: by reducing the total size, we reduced the size after the 27 | # address and the end address must be updated: 28 | uMemoryDumpEndAddress = uMemoryDumpStartAddress + uMemoryDumpSize; 29 | elif uMemoryDumpSizeAfterMostInterestingAddress < dxConfig["uMaxMemoryDumpSize"] / 2: 30 | # The size after the address is reasonable: reduce the size before the address by increasing the start 31 | # address so that the dump still ends at the same address after having reduced its size: 32 | uMemoryDumpStartAddress = uMemoryDumpEndAddress - uMemoryDumpSize; 33 | else: 34 | # The size before and after the address are both too large: the memory dump will have the most interesting 35 | # address in the middle. 36 | uMemoryDumpStartAddress = int(round(uMostInterestingAddress - dxConfig["uMaxMemoryDumpSize"] / 2)); 37 | # Align new start address: 38 | uMemoryDumpStartAddress -= uMemoryDumpStartAddress & uPointerSizeMask; # decrease to align 39 | uMemoryDumpEndAddress = uMemoryDumpStartAddress + uMemoryDumpSize; 40 | assert uMemoryDumpStartAddress + uMemoryDumpSize == uMemoryDumpEndAddress, \ 41 | "Math is wrong: 0x%X + 0x%X != 0x%X" % (uMemoryDumpStartAddress, uMemoryDumpSize, uMemoryDumpEndAddress); 42 | assert uMemoryDumpSize <= dxConfig["uMaxMemoryDumpSize"], \ 43 | "Math is wrong: memory dump size (0x%X) is larger than the maximum (0x%X)" % (uMemoryDumpSize, dxConfig["uMaxMemoryDumpSize"]); 44 | # return updated, aligned start address and limited, aligned size 45 | return uMemoryDumpStartAddress, uMemoryDumpSize; 46 | 47 | -------------------------------------------------------------------------------- /fu0ValueFromCdbHexOutput.py: -------------------------------------------------------------------------------- 1 | def fu0ValueFromCdbHexOutput(sb0BytesString): 2 | return int(sb0BytesString.replace(b"`", b""), 16) if sb0BytesString else None; -------------------------------------------------------------------------------- /lgtm.yml: -------------------------------------------------------------------------------- 1 | queries: 2 | - include: "*" 3 | - exclude: py/not-named-self 4 | -------------------------------------------------------------------------------- /mBugReport/__init__.py: -------------------------------------------------------------------------------- 1 | from .cBugReport import cBugReport; 2 | 3 | __all__ = [ 4 | "cBugReport", 5 | ]; -------------------------------------------------------------------------------- /mBugReport/cBugReport_foAnalyzeException_STATUS_BREAKPOINT.py: -------------------------------------------------------------------------------- 1 | def cBugReport_foAnalyzeException_STATUS_BREAKPOINT(oBugReport, oProcess, oWindowsAPIThread, oException): 2 | oProcess.oCdbWrapper.oASanErrorDetector.fAddInformationToBugReport(oBugReport, oProcess, oWindowsAPIThread); 3 | return oBugReport; 4 | -------------------------------------------------------------------------------- /mBugReport/cBugReport_foAnalyzeException_STATUS_FAILFAST_OOM_EXCEPTION.py: -------------------------------------------------------------------------------- 1 | def cBugReport_foAnalyzeException_STATUS_FAILFAST_OOM_EXCEPTION(oBugReport, oProcess, oWindowsAPIThread, oException): 2 | return oBugReport; 3 | -------------------------------------------------------------------------------- /mBugReport/cBugReport_foAnalyzeException_STATUS_FAIL_FAST_EXCEPTION.py: -------------------------------------------------------------------------------- 1 | def cBugReport_foAnalyzeException_STATUS_FAIL_FAST_EXCEPTION(oBugReport, oProcess, oWindowsAPIThread, oException): 2 | # cdb does not known this exception and reports "Unknown exception (code 0xC0000602)" as the description. 3 | oBugReport.s0BugDescription = "Fail fast exception (code 0x%X)" % oException.uCode; 4 | return oBugReport; 5 | -------------------------------------------------------------------------------- /mBugReport/cBugReport_foAnalyzeException_STATUS_STOWED_EXCEPTION.py: -------------------------------------------------------------------------------- 1 | from ..cStowedException import cStowedException; 2 | 3 | def cBugReport_foAnalyzeException_STATUS_STOWED_EXCEPTION(oBugReport, oProcess, oWindowsAPIThread, oException): 4 | # Parameter[0] = paStowedExceptionInformationArray; 5 | # Parameter[1] = uStowedExceptionInformationArrayLength; 6 | assert len(oException.auParameters) == 2, \ 7 | "Unexpected number of WinRT language exception parameters (%d vs 2)" % len(oException.auParameters); 8 | # Get the stowed exceptions and replace information in the bug report: 9 | aoStowedExceptions = cStowedException.faoCreateForListAddressAndCount(oProcess, 10 | oException.auParameters[0], 11 | oException.auParameters[1], 12 | ); 13 | oBugReport.s0BugTypeId = "Stowed[%s]" % ",".join([oStowedException.sTypeId for oStowedException in aoStowedExceptions]); 14 | oBugReport.s0BugDescription = ", ".join([oStowedException.sDescription for oStowedException in aoStowedExceptions]); 15 | oBugReport.s0SecurityImpact = ", ".join([ 16 | oStowedException.s0SecurityImpact 17 | for oStowedException in aoStowedExceptions 18 | if oStowedException.s0SecurityImpact 19 | ]) or None; 20 | return oBugReport; 21 | -------------------------------------------------------------------------------- /mBugReport/cBugReport_foAnalyzeException_WRT_ORIGINATE_ERROR_EXCEPTION.py: -------------------------------------------------------------------------------- 1 | import json; 2 | 3 | def cBugReport_foAnalyzeException_WRT_ORIGINATE_ERROR_EXCEPTION(oBugReport, oProcess, oWindowsAPIThread, oException): 4 | # See documentation of RoOriginateError at https://msdn.microsoft.com/en-us/library/br224651(v=vs.85).aspx 5 | # Parameter[0] = HRESULT error; 6 | # Parameter[1] = length of HSTRING message; 7 | # Parameter[2] = pointer to HSTRING message; 8 | assert len(oException.auParameters) == 3, \ 9 | "Unexpected number of RoOriginateError exception parameters (%d vs 3)" % len(oException.auParameters); 10 | hResult = oException.auParameters[0]; 11 | # uMessageLength = oException.auParameters[1]; 12 | uMessageAddress = oException.auParameters[2]; 13 | # The message is '\0' terminated, so no need to use uMessageLength. 14 | # We could assert if it's incorrect, but I don't see much use in that other than to prevent the target from 15 | # crashing us. 16 | if oException.bApplicationCannotHandleException: 17 | s0Message = oProcess.fs0ReadNullTerminatedStringForAddress( 18 | uAddress = uMessageAddress, 19 | bUnicode = True, 20 | ); 21 | # Get the stowed exceptions and replace information in the bug report: 22 | oBugReport.s0BugTypeId = "WRTOriginate[0x%X]" % hResult; 23 | oBugReport.s0BugDescription = "A Windows Run-Time Originate error was thrown with error code %X and message %s." % \ 24 | (hResult, json.dumps(s0Message) if s0Message else ""); 25 | oBugReport.s0SecurityImpact = "Unknown"; 26 | else: 27 | # This is not a bug: 28 | oBugReport.s0BugTypeId = None; 29 | oBugReport.s0BugDescription = None; 30 | return oBugReport; 31 | -------------------------------------------------------------------------------- /mBugReport/cBugReport_fs0GetRegistersBlockHTML.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import oSystemInfo; 2 | 3 | from ..dxConfig import dxConfig; 4 | from ..mCP437 import fsCP437HTMLFromBytesString; 5 | 6 | from .fsGetHTMLForValue import fsGetHTMLForValue; 7 | from .sBlockHTMLTemplate import sBlockHTMLTemplate; 8 | 9 | def cBugReport_fs0GetRegistersBlockHTML(oBugReport, oProcess, oWindowsAPIThread): 10 | # Create and add registers block 11 | a0txRegisters = oProcess.fa0txGetRegistersForThreadId(oWindowsAPIThread.uId); 12 | if a0txRegisters is None: 13 | return None; 14 | atxRegisters = a0txRegisters; 15 | asRegistersTableHTML = []; 16 | for (sbRegisterName, uRegisterValue, uBitSize, s0Details) in atxRegisters: 17 | sRegisterName = fsCP437HTMLFromBytesString(sbRegisterName); 18 | asRegistersTableHTML.extend([ 19 | '', 20 | '', sRegisterName, '', 21 | ' = ', 22 | '', fsGetHTMLForValue(uRegisterValue, uBitSize), '', 23 | '', s0Details or "", '', 24 | '\n', 25 | ]); 26 | if uRegisterValue < 1 << (oProcess.uPointerSize * 8): 27 | o0VirtualAllocation = oProcess.fo0GetVirtualAllocationForAddress(uRegisterValue); 28 | if o0VirtualAllocation and o0VirtualAllocation.bAllocated: 29 | oBugReport.fAddMemoryDump( 30 | uStartAddress = uRegisterValue - dxConfig["uRegisterPointerPreDumpSizeInPointers"] * oProcess.uPointerSizeInBytes, 31 | uEndAddress = uRegisterValue + dxConfig["uRegisterPointerPostDumpSizeInPointers"] * oProcess.uPointerSizeInBytes, 32 | asAddressDetailsHTML = ["%s = %s" % (sRegisterName, fsGetHTMLForValue(uRegisterValue, oProcess.uPointerSizeInBits))], 33 | ); 34 | # Add memory remarks for anything that is not (close to) NULL. 35 | # Calculate the positive equivalent of the signed value for the register. 36 | uNegativeRegisterValue = {"x86": 1 << 32, "x64": 1 << 64}[oProcess.sISA] - uRegisterValue; 37 | if ( 38 | uRegisterValue > oSystemInfo.uAllocationAddressGranularity 39 | and uNegativeRegisterValue > oSystemInfo.uAllocationAddressGranularity 40 | ): 41 | oBugReport.fAddMemoryRemark( 42 | "%s=%s" % ( 43 | sRegisterName, 44 | fsGetHTMLForValue(uRegisterValue, oProcess.uPointerSizeInBits), 45 | ), 46 | uRegisterValue, 47 | None, 48 | ); 49 | return sBlockHTMLTemplate % { 50 | "sName": "Registers", 51 | "sCollapsed": "Collapsed", 52 | "sContent": "\n%s
" % "".join(asRegistersTableHTML), 53 | }; 54 | 55 | -------------------------------------------------------------------------------- /mBugReport/fsGetHTMLForValue.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | guNumberOfCharsBetweenTicks = 8; 4 | 5 | def fsGetHTMLForValue(uValue, uBitSize): 6 | sValueHex = "%%0%dX" % (uBitSize >> 2) % uValue; 7 | sHumanReadableValue = "0x" + "`".join([ 8 | sValueHex[uIndex:uIndex + guNumberOfCharsBetweenTicks] 9 | for uIndex in range(0, len(sValueHex), guNumberOfCharsBetweenTicks) 10 | ]); 11 | (sValuePadding, sValueWithoutPadding) = re.match(r"^(0x[0`]*)([0-9A-F`]+?)$", sHumanReadableValue).groups(); 12 | return ( 13 | '%s' + 14 | '%s' 15 | ) % (sValuePadding, sValueWithoutPadding); 16 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/__init__.py: -------------------------------------------------------------------------------- 1 | from .fUpdateReportForProcessThreadAccessViolationTypeIdAddressAndOptionalSize \ 2 | import fUpdateReportForProcessThreadAccessViolationTypeIdAddressAndOptionalSize; 3 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/fbUpdateReportForCollateralPoisonPointer.py: -------------------------------------------------------------------------------- 1 | from ...fsGetNumberDescription import fsGetNumberDescription; 2 | 3 | def fbUpdateReportForCollateralPoisonPointer( 4 | oCdbWrapper, 5 | oBugReport, 6 | oProcess, 7 | oThread, 8 | sViolationTypeId, 9 | sViolationVerb, 10 | uAccessViolationAddress, 11 | ): 12 | i0Offset = oCdbWrapper.oCollateralBugHandler.fi0GetOffsetForPoisonedAddress(oProcess, uAccessViolationAddress); 13 | if i0Offset is None: 14 | # This is not near the poisoned address used by collateral 15 | return False; 16 | iOffset = i0Offset; 17 | sSign = iOffset < 0 and "-" or "+"; 18 | sOffset = "%s%s" % (sSign, fsGetNumberDescription(abs(iOffset), sSign)); 19 | oBugReport.s0BugTypeId = "AV%s:Poison%s" % (sViolationTypeId, sOffset); 20 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while attempting to %s memory at 0x%X using a poisoned value provided by cBugId." % \ 21 | (uAccessViolationAddress, sViolationVerb, uAccessViolationAddress); 22 | oBugReport.s0SecurityImpact = "Highly likely to be an exploitable security issue if your exploit can poison this value."; 23 | # This address most likely came from a previous access violation we ignored 24 | # through collateral bug handling, so it makes sense to assume an attacker 25 | # could control the value of the address and avoid this AV too: 26 | oCdbWrapper.oCollateralBugHandler.fSetIgnoreExceptionFunction(lambda oCollateralBugHandler: 27 | oCollateralBugHandler.fbIgnoreAccessViolationException( 28 | oCdbWrapper, oProcess, oThread, sViolationTypeId, uAccessViolationAddress, 29 | ) 30 | ); 31 | return True; 32 | 33 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/fbUpdateReportForGuardPagePointer.py: -------------------------------------------------------------------------------- 1 | from ...ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress import ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress; 2 | 3 | def fbUpdateReportForGuardPagePointer( 4 | oCdbWrapper, 5 | oBugReport, 6 | oProcess, 7 | oThread, 8 | sViolationTypeId, 9 | sViolationVerb, 10 | uAccessViolationAddress, 11 | oVirtualAllocation, 12 | ): 13 | if not oVirtualAllocation.bGuard: 14 | return False; 15 | # No memory is allocated in this area, but is is reserved 16 | (sBlockSizeId, sBlockOffsetId, sBlockOffsetDescription, sBlockSizeDescription) = \ 17 | ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress( 18 | uBlockStartAddress = oVirtualAllocation.uStartAddress, 19 | uBlockSize = oVirtualAllocation.uSize, 20 | sBlockType = "guard page", 21 | uAddress = uAccessViolationAddress, 22 | ); 23 | oBugReport.s0BugTypeId = "AV%s:Guard%s%s" % (sViolationTypeId, sBlockSizeId, sBlockOffsetId); 24 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while %s a guard page at 0x%X-0x%X." % \ 25 | (uAccessViolationAddress, sViolationVerb, oVirtualAllocation.uStartAddress, \ 26 | oVirtualAllocation.uStartAddress + oVirtualAllocation.uSize); 27 | oBugReport.s0SecurityImpact = "Unlikely to be an exploitable security issue; guard pages are allocated to trigger " \ 28 | "this type of access violation, so this appears to be a by-design error handling."; 29 | # Guard Pages are created to detect when an application is accessing memory 30 | # out-of-bounds, so it seems this access violation cannot be avoided. We will 31 | # not set up a collateral bug handler to try to see what happens if we can 32 | # avoid the AV. 33 | return True; 34 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/fbUpdateReportForInvalidPointer.py: -------------------------------------------------------------------------------- 1 | def fbUpdateReportForInvalidPointer( 2 | oCdbWrapper, 3 | oBugReport, 4 | oProcess, 5 | oThread, 6 | sViolationTypeId, 7 | sViolationVerb, 8 | uAccessViolationAddress, 9 | oVirtualAllocation, 10 | ): 11 | # See if the address is valid: 12 | if not oVirtualAllocation.bInvalid: 13 | return False; 14 | oBugReport.s0BugTypeId = "AV%s:Invalid" % sViolationTypeId; 15 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while attempting to %s memory at invalid address 0x%X." % \ 16 | (uAccessViolationAddress, sViolationVerb, uAccessViolationAddress); 17 | oBugReport.s0SecurityImpact = "Potentially exploitable security issue, if the address can be controlled."; 18 | # You normally cannot allocate memory at an invalid address, but it is not 19 | # normal for a program to use an invalid pointer, so the address may be based 20 | # on attacker controlled data: 21 | oCdbWrapper.oCollateralBugHandler.fSetIgnoreExceptionFunction(lambda oCollateralBugHandler: 22 | oCollateralBugHandler.fbIgnoreAccessViolationException( 23 | oCdbWrapper, oProcess, oThread, sViolationTypeId, uAccessViolationAddress, 24 | ) 25 | ); 26 | return True; 27 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/fbUpdateReportForNULLPointer.py: -------------------------------------------------------------------------------- 1 | from ...fsGetNumberDescription import fsGetNumberDescription; 2 | from mWindowsAPI import oSystemInfo; 3 | 4 | def fbUpdateReportForNULLPointer( 5 | oCdbWrapper, 6 | oBugReport, 7 | oProcess, 8 | oThread, 9 | sViolationTypeId, 10 | sViolationVerb, 11 | uAccessViolationAddress, 12 | ): 13 | if uAccessViolationAddress == 0: 14 | sOffset = ""; 15 | elif uAccessViolationAddress < oSystemInfo.uAllocationAddressGranularity: 16 | sOffset = "+%s" % fsGetNumberDescription(uAccessViolationAddress, "+"); 17 | else: 18 | uAccessViolationNegativeOffset = {"x86": 1 << 32, "x64": 1 << 64}[oProcess.sISA] - uAccessViolationAddress; 19 | if uAccessViolationNegativeOffset >= oSystemInfo.uAllocationAddressGranularity: 20 | return False; 21 | sOffset = "-%s" % fsGetNumberDescription(uAccessViolationNegativeOffset, "-"); 22 | oBugReport.s0BugTypeId = "AV%s:NULL%s" % (sViolationTypeId, sOffset); 23 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while attempting to %s memory at 0x%X using a NULL pointer." % \ 24 | (uAccessViolationAddress, sViolationVerb, uAccessViolationAddress); 25 | oBugReport.s0SecurityImpact = None; 26 | # You normally cannot allocate memory at address 0, so it is impossible for an exploit to avoid this exception. 27 | # Therefore there is no collateral bug handling. 28 | return True; 29 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/fbUpdateReportForReservedPointer.py: -------------------------------------------------------------------------------- 1 | from ...ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress import ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress; 2 | 3 | def fbUpdateReportForReservedPointer( 4 | oCdbWrapper, 5 | oBugReport, 6 | oProcess, 7 | oThread, 8 | sViolationTypeId, 9 | sViolationVerb, 10 | uAccessViolationAddress, 11 | oVirtualAllocation, 12 | ): 13 | if not oVirtualAllocation.bReserved: 14 | return False; 15 | # No memory is allocated in this area, but is is reserved 16 | (sBlockSizeId, sBlockOffsetId, sBlockOffsetDescription, sBlockSizeDescription) = \ 17 | ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress( 18 | uBlockStartAddress = oVirtualAllocation.uStartAddress, 19 | uBlockSize = oVirtualAllocation.uSize, 20 | sBlockType = "reserved memory", 21 | uAddress = uAccessViolationAddress, 22 | ); 23 | oBugReport.s0BugTypeId = "AV%s:Reserved%s%s" % (sViolationTypeId, sBlockSizeId, sBlockOffsetId); 24 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while %s reserved but unallocated memory at 0x%X-0x%X." % \ 25 | (uAccessViolationAddress, sViolationVerb, oVirtualAllocation.uStartAddress, \ 26 | oVirtualAllocation.uStartAddress + oVirtualAllocation.uSize); 27 | oBugReport.s0SecurityImpact = "Potentially exploitable security issue, if the address can be controlled, or " \ 28 | "memory be allocated at the address rather than reserved."; 29 | # Attacker might be able to get the application to allocate memory here, so we 30 | # can try to ignore the exception and see what happens next: 31 | oCdbWrapper.oCollateralBugHandler.fSetIgnoreExceptionFunction(lambda oCollateralBugHandler: 32 | oCollateralBugHandler.fbIgnoreAccessViolationException( 33 | oCdbWrapper, oProcess, oThread, sViolationTypeId, uAccessViolationAddress, 34 | ) 35 | ); 36 | return True; 37 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/fbUpdateReportForStackPointer.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import cVirtualAllocation; 2 | 3 | from ...fsGetNumberDescription import fsGetNumberDescription; 4 | 5 | def fbUpdateReportForStackPointer( 6 | oCdbWrapper, 7 | oBugReport, 8 | oProcess, 9 | oThread, 10 | sViolationTypeId, 11 | sViolationVerb, 12 | uAccessViolationAddress, 13 | ): 14 | u0StackTopAddress = oThread.u0StackTopAddress; 15 | u0StackBottomAddress = oThread.u0StackBottomAddress; 16 | if u0StackTopAddress is None or u0StackBottomAddress is None: 17 | return None; 18 | uStackTopAddress = u0StackTopAddress; 19 | uStackBottomAddress = u0StackBottomAddress; 20 | oStackVirtualAllocation = cVirtualAllocation(oProcess.uId, oThread.u0StackTopAddress - 1); 21 | if ( 22 | oStackVirtualAllocation.uStartAddress > u0StackBottomAddress or 23 | oStackVirtualAllocation.uEndAddress != uStackTopAddress 24 | ): 25 | # This makes little sense, so let's not try to handle it. 26 | return False; 27 | # See if the address is near the allocation for the stack for the current thread: 28 | uOffsetFromTopOfStack = uAccessViolationAddress - oStackVirtualAllocation.uEndAddress; 29 | uOffsetFromBottomOfStack = oStackVirtualAllocation.uEndAddress - uAccessViolationAddress; 30 | if uOffsetFromTopOfStack >= 0 and uOffsetFromTopOfStack <= oStackVirtualAllocation.uPageSize: 31 | oBugReport.s0BugTypeId = "AV%s:Stack+%s" % (sViolationTypeId, fsGetNumberDescription(uOffsetFromTopOfStack)); 32 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while attempting to %s memory at 0x%X; %d/0x%X bytes above the top of the stack memory allocation at 0x%X." % \ 33 | (uAccessViolationAddress, sViolationVerb, uAccessViolationAddress, uOffsetFromTopOfStack, uOffsetFromTopOfStack, oStackVirtualAllocation.uEndAddress); 34 | elif uOffsetFromBottomOfStack >= 0 and uOffsetFromBottomOfStack <= oStackVirtualAllocation.uPageSize: 35 | oBugReport.s0BugTypeId = "AV%s:Stack-%s" % (sViolationTypeId, fsGetNumberDescription(uOffsetFromBottomOfStack)); 36 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while attempting to %s memory at 0x%X; %d/0x%X bytes below the bottom of the stack memory allocation at 0x%X." % \ 37 | (uAccessViolationAddress, sViolationVerb, uAccessViolationAddress, uOffsetFromBottomOfStack, uOffsetFromBottomOfStack, oStackVirtualAllocation.uStartAddress); 38 | else: 39 | return False; 40 | oBugReport.s0SecurityImpact = "Potentially exploitable security issue."; 41 | # We assume that there are Guard Pages around the stack, so this exception is 42 | # never going to be avoidable. This is why we do not set a collateral bug 43 | # handler here. 44 | return True; 45 | -------------------------------------------------------------------------------- /mBugReport/mAccessViolation/fbUpdateReportForUnallocatedPointer.py: -------------------------------------------------------------------------------- 1 | def fbUpdateReportForUnallocatedPointer( 2 | oCdbWrapper, 3 | oBugReport, 4 | oProcess, 5 | oThread, 6 | sViolationTypeId, 7 | sViolationVerb, 8 | uAccessViolationAddress, 9 | oVirtualAllocation, 10 | ): 11 | if not oVirtualAllocation.bFree: 12 | return False; 13 | # No memory is allocated in this area 14 | oBugReport.s0BugTypeId = "AV%s:Unallocated" % sViolationTypeId; 15 | oBugReport.s0BugDescription = "An Access Violation exception happened at 0x%X while attempting to %s unallocated memory at 0x%X." % \ 16 | (uAccessViolationAddress, sViolationVerb, uAccessViolationAddress); 17 | oBugReport.s0SecurityImpact = "Potentially exploitable security issue, if the address can be controlled, or memory be allocated at the address."; 18 | # Attacker might be able to have the access point to allocated memory, so we 19 | # can try to ignore the exception and see what happens next: 20 | oCdbWrapper.oCollateralBugHandler.fSetIgnoreExceptionFunction(lambda oCollateralBugHandler: 21 | oCollateralBugHandler.fbIgnoreAccessViolationException( 22 | oCdbWrapper, oProcess, oThread, sViolationTypeId, uAccessViolationAddress, 23 | ) 24 | ); 25 | return True; 26 | -------------------------------------------------------------------------------- /mBugReport/sBlockHTMLTemplate.py: -------------------------------------------------------------------------------- 1 | sBlockHTMLTemplate = """ 2 |
3 |

%(sName)s

4 |
5 | %(sContent)s 6 |
7 |
⇓ click on the title of a section to open or close it.
8 |
9 | """.lstrip("\r\n"); 10 | 11 | -------------------------------------------------------------------------------- /mBugTranslations/ASan.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | def fDisableDetectionOfAccessViolations(oCdbWrapper, oBugReport): 4 | oCdbWrapper.fbFireCallbacks("ASan detected"); 5 | oCdbWrapper.fasbExecuteCdbCommand( 6 | sbCommand = b"sxd av", 7 | sb0Comment = b"Disable handling of exceptions because ASan throws too many", 8 | ); 9 | 10 | aoBugTranslations = [ 11 | # AVs during initialization --> expected, not a bug. 12 | cBugTranslation( 13 | srzOriginalBugTypeId = r"AVW:Reserved\[(0x)?[\dA-F]+n?\]@\d+", 14 | azs0rbAppliesOnlyToTopStackFrame = [ 15 | rb".*!__asan::FastPoisonShadow", 16 | ], 17 | s0zTranslatedBugTypeId = None, 18 | f0Callback = fDisableDetectionOfAccessViolations 19 | ), 20 | # IllegalInstruction --> ASan 21 | cBugTranslation( 22 | srzOriginalBugTypeId = r"IllegalInstruction", 23 | azs0rbAppliesOnlyToTopStackFrame = [ 24 | rb".*!__sanitizer::Trap", 25 | ], 26 | s0zTranslatedBugTypeId = "ASan", 27 | # This is a backup in case cAsanErrorDetector does not detect and handle the ASan debug output we normally expect. 28 | s0zTranslatedBugDescription = "ASan triggered an illegal instruction to indicate it detected an issue which cBugId does not recognize.", 29 | s0zTranslatedSecurityImpact = "The security implications of this issue are unknown", 30 | ), 31 | # ASan --> (hide irrelevant frames only) 32 | cBugTranslation( 33 | srzOriginalBugTypeId = "ASan", 34 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 35 | rb".*!agent::asan::\w+.*", 36 | rb".*!_*asan_\w+.*", 37 | rb".*!__asan::\w+.*", 38 | rb".*!__sanitizer::\w+.*", 39 | rb".*!uprv_realloc_60", 40 | ], 41 | ), 42 | ]; -------------------------------------------------------------------------------- /mBugTranslations/Cpp.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # * -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb".*!_?CxxThrowException", 8 | rb".*!abort", 9 | ], 10 | ), 11 | # Breakpoint -> OOM 12 | cBugTranslation( 13 | srzOriginalBugTypeId = r"Breakpoint", 14 | azs0rbAppliesOnlyToTopStackFrame = [ 15 | rb".*!malloc", 16 | ], 17 | s0zTranslatedBugTypeId = "OOM", 18 | s0zTranslatedBugDescription = "The application triggered a breakpoint exception to indicate it was unable to allocate enough memory.", 19 | s0zTranslatedSecurityImpact = None, 20 | ), 21 | # C++:std::bad_alloc -> OOM 22 | cBugTranslation( 23 | srzOriginalBugTypeId = r"C\+\+:std::bad_alloc", 24 | s0zTranslatedBugTypeId = "OOM", 25 | s0zTranslatedBugDescription = "The application triggered a C++ std::bad_alloc exception to indicate it was unable to allocate enough memory.", 26 | s0zTranslatedSecurityImpact = None, 27 | ), 28 | # AppExit -> PureCall 29 | cBugTranslation( 30 | srzOriginalBugTypeId = r"AppExit", 31 | azs0rbAppliesOnlyToTopStackFrame = [ 32 | rb".*!_?purecall", 33 | ], 34 | s0zTranslatedBugTypeId = "PureCall", 35 | s0zTranslatedBugDescription = "Pure virtual function call (R6025).", 36 | s0zTranslatedSecurityImpact = "This is a potentially exploitable security issue", 37 | ), 38 | # PureCall -> hide irrelevant frames 39 | cBugTranslation( 40 | srzOriginalBugTypeId = r"PureCall", 41 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 42 | rb".*!_?purecall", 43 | ], 44 | ), 45 | # StackExhaustion (hide irrelevant frames) 46 | cBugTranslation( 47 | srzOriginalBugTypeId = r"StackExhaustion", 48 | azs0rbAppliesOnlyToTopStackFrame = [ 49 | rb".*!__?chkstk", 50 | ], 51 | ), 52 | # hide irrelevant heap management frames 53 | cBugTranslation( 54 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 55 | rb".*!(m|re)alloc", 56 | rb".*!mem(chr|cmp|cpy|move|set)", 57 | rb".*!(.+::)?operator (delete|new(\[\])?)", 58 | rb".*!__scrt_throw_std_bad_alloc", 59 | rb".*!str(n?cat|r?chr|n?cmp|n?cpy|len|str)", 60 | rb".*!std::_.*", 61 | rb".*!std::allocator<.+>::.*", 62 | rb".*!std::basic_string<.+>::.*", 63 | rb".*!std::vector<.+>::.*", 64 | ], 65 | ), 66 | ]; 67 | -------------------------------------------------------------------------------- /mBugTranslations/Firefox.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Breakpoint -> hide irrelevant stack frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"(mozglue|xul)\.dll!Abort", 8 | rb"(mozglue|xul)\.dll!arena_\w+", 9 | rb"(mozglue|xul)\.dll!(.*::)?\w+alloc(<.+>|::.*)?", 10 | rb"(mozglue|xul)\.dll!collections::vec::Vec<.+>::reserve<.+>", 11 | rb"(mozglue|xul)\.dll!moz_abort", 12 | rb"(mozglue|xul)\.dll!mozalloc_abort", 13 | ], 14 | ), 15 | # Breakpoint -> OOM 16 | cBugTranslation( 17 | srzOriginalBugTypeId = r"Breakpoint", 18 | azs0rbAppliesOnlyToTopStackFrame = [ 19 | rb"mozglue\.dll!arena_run_split", 20 | rb"mozglue\.dll!mozalloc_handle_oom", 21 | rb"mozglue\.dll!pages_commit", 22 | rb"xul\.dll!js::CrashAtUnhandlableOOM", 23 | rb"xul\.dll!js::AutoEnterOOMUnsafeRegion::crash", 24 | rb"xul\.dll!NS_ABORT_OOM", 25 | ], 26 | s0zTranslatedBugTypeId = "OOM", 27 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate it was unable to allocate enough memory.", 28 | s0zTranslatedSecurityImpact = None, 29 | ), 30 | # IllegalInstruction -> OOM 31 | cBugTranslation( 32 | srzOriginalBugTypeId = r"IllegalInstruction", 33 | azs0rbAppliesOnlyToTopStackFrame = [ 34 | rb"xul\.dll!alloc::oom::default_oom_handler", 35 | rb"xul\.dll!alloc::heap::\{\{impl\}\}::oom", 36 | ], 37 | s0zTranslatedBugTypeId = "OOM", 38 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate it was unable to allocate enough memory.", 39 | s0zTranslatedSecurityImpact = None, 40 | ), 41 | # IllegalInstruction without symbol -> ignore 42 | # JIT compiled JavaScript uses it to signal stuff, e.g. MacroAssembler::wasmTrapInstruction. 43 | # JIT compiled Javascript is generated assembly, hence there is no symbol. 44 | cBugTranslation( 45 | srzOriginalBugTypeId = r"IllegalInstruction", 46 | azs0rbAppliesOnlyToTopStackFrame = [ 47 | None, 48 | ], 49 | s0zTranslatedBugTypeId = None, 50 | bDebug = True, 51 | ), 52 | # Breakpoint -> Assert 53 | cBugTranslation( 54 | srzOriginalBugTypeId = r"Breakpoint", 55 | azs0rbAppliesOnlyToTopStackFrame = [ 56 | rb"xul\.dll!NS_DebugBreak", 57 | ], 58 | s0zTranslatedBugTypeId = "Assert", 59 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate an assertion failed.", 60 | s0zTranslatedSecurityImpact = "Unlikely to be exploitable, unless you can find a way to avoid this breakpoint.", 61 | ), 62 | ]; 63 | -------------------------------------------------------------------------------- /mBugTranslations/RTC.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = []; 4 | aoBugTranslations.append(cBugTranslation( 5 | srzOriginalBugTypeId = r"Breakpoint", 6 | azs0rbAppliesOnlyToTopStackFrame = [ 7 | rb".*!failwithmessage", 8 | rb".*!_RTC_StackFailure", 9 | ], 10 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 11 | rb".*!_RTC_CheckStackVars\d*", 12 | ], 13 | s0zTranslatedBugTypeId = "OOBW:Stack", 14 | s0zTranslatedBugDescription = "The Windows Run-Time detected that a stack variable was modified, which suggests an out-of-bounds write on the stack.", 15 | s0zTranslatedSecurityImpact = "Potentially exploitable security issue", 16 | )); 17 | -------------------------------------------------------------------------------- /mBugTranslations/SafeInt.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # IntegerOverflow -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb".*!SafeIntExceptionHandler<.+>::SafeIntOnOverflow", 8 | rb".*!msl::utilities::SafeIntErrorPolicy_SafeIntException::SafeIntOnOverflow", 9 | ], 10 | ), 11 | cBugTranslation( 12 | srzOriginalBugTypeId = r"C\+\+:msl::utilities::SafeIntException", 13 | s0zTranslatedBugTypeId = "SafeInt", 14 | s0zTranslatedBugDescription = "The application attempted to store an integer value in an integer type that cannot contain this value.", 15 | s0zTranslatedSecurityImpact = None, 16 | ), 17 | cBugTranslation( 18 | srzOriginalBugTypeId = r"SafeInt", 19 | azs0rbAppliesOnlyToTopStackFrame = [ 20 | rb".*!msl::utilities::SafeInt<.+>::operator\+\+", 21 | ], 22 | s0zTranslatedBugTypeId = "IntegerOverflow", 23 | s0zTranslatedBugDescription = "The application attempted to increase an integer above its maxium value.", 24 | s0zTranslatedSecurityImpact = None, 25 | ), 26 | cBugTranslation( 27 | srzOriginalBugTypeId = r"SafeInt", 28 | azs0rbAppliesOnlyToTopStackFrame = [ 29 | rb".*!msl::utilities::SafeInt<.+>::operator\-\-", 30 | ], 31 | s0zTranslatedBugTypeId = "IntegerUnderflow", 32 | s0zTranslatedBugDescription = "The application attempted to decrease an integer below its minimum value.", 33 | s0zTranslatedSecurityImpact = None, 34 | ), 35 | cBugTranslation( 36 | srzOriginalBugTypeId = r"SafeInt", 37 | azs0rbAppliesOnlyToTopStackFrame = [ 38 | rb".*!msl::utilities::details::MultiplicationHelper<.+>::Multiply", 39 | ], 40 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 41 | rb".*!msl::utilities::SafeInt<...>::operator\*=<.+>", 42 | ], 43 | s0zTranslatedBugTypeId = "IntegerTruncation", 44 | s0zTranslatedBugDescription = "The application attempted to store the result of a multiplication in an integer that cannot contain this value.", 45 | s0zTranslatedSecurityImpact = None, 46 | ), 47 | cBugTranslation( 48 | srzOriginalBugTypeId = r"SafeInt", 49 | azs0rbAppliesOnlyToTopStackFrame = [ 50 | rb".*!msl::utilities::details::LargeIntRegMultiply<.+>::RegMultiply", 51 | ], 52 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 53 | rb".*!msl::utilities::details::LargeIntRegMultiply<.*>::.*", 54 | rb".*!msl::utilities::details::MultiplicationHelper<.*>::.*", 55 | rb".*!msl::utilities::SafeInt<...>::operator\*=<.+>", 56 | ], 57 | s0zTranslatedBugTypeId = "IntegerTruncation", 58 | s0zTranslatedBugDescription = "The application attempted to store the result of a multiplication in an integer that cannot contain this value.", 59 | s0zTranslatedSecurityImpact = None, 60 | ), 61 | cBugTranslation( 62 | srzOriginalBugTypeId = r"SafeInt", 63 | azs0rbAppliesOnlyToTopStackFrame = [ 64 | rb".*!msl::utilities::details::SafeCastHelper<.+>::Cast", 65 | ], 66 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 67 | rb".*!msl::utilities::SafeInt<.+>::SafeInt<.+><.+>", 68 | rb".*!msl::utilities::SafeInt<.+>::operator=<.+>", 69 | ], 70 | s0zTranslatedBugTypeId = "IntegerTruncation", 71 | s0zTranslatedBugDescription = "The application attempted to store a value in an integer that cannot contain this value.", 72 | s0zTranslatedSecurityImpact = None, 73 | ), 74 | ]; 75 | -------------------------------------------------------------------------------- /mBugTranslations/SlashGS.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = []; 4 | # OOBW@Stack (hide irrelevant frames only) 5 | aoBugTranslations.append(cBugTranslation( 6 | srzOriginalBugTypeId = r"OOBW:Stack", 7 | azs0rbAppliesOnlyToTopStackFrame = [ 8 | rb".*!__security_check_cookie", 9 | ], 10 | )); 11 | -------------------------------------------------------------------------------- /mBugTranslations/V8.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # AVE@NULL -> Assert 5 | cBugTranslation( 6 | srzOriginalBugTypeId = r"AVE@NULL", 7 | azs0rbAppliesOnlyToTopStackFrame = [ 8 | rb".*!v8::base::OS::Abort", 9 | ], 10 | s0zTranslatedBugTypeId = "Assert", 11 | s0zTranslatedBugDescription = "The application caused an access violation by calling NULL to indicate an assertion failed.", 12 | s0zTranslatedSecurityImpact = None, 13 | ), 14 | # AVE@NULL -> Assert 15 | cBugTranslation( 16 | srzOriginalBugTypeId = r"AVE@NULL", 17 | azs0rbAppliesOnlyToTopStackFrame = [ 18 | rb".*!V8_Fatal", 19 | ], 20 | s0zTranslatedBugTypeId = "Assert", 21 | s0zTranslatedBugDescription = "The application caused an access violation by calling NULL to indicate an assertion failed.", 22 | s0zTranslatedSecurityImpact = None, 23 | ), 24 | # Assert -> ignore functions 25 | cBugTranslation( 26 | srzOriginalBugTypeId = r"Assert", 27 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 28 | rb".*!V8_Fatal", 29 | rb".*!v8::base::OS::Abort", 30 | rb".*!v8::MaybeLocal<.+>::ToLocalChecked", 31 | rb".*!v8::Utils::ReportApiFailure", 32 | rb".*!v8::Utils::ApiCheck", 33 | rb".*!v8::V8::ToLocalEmpty", 34 | rb".*!v8::MaybeLocal", 35 | ], 36 | ), 37 | # Assert -> OOM 38 | cBugTranslation( 39 | srzOriginalBugTypeId = r"Assert", 40 | azs0rbAppliesOnlyToTopStackFrame = [ 41 | rb".*!v8::internal::V8::FatalProcessOutOfMemory", 42 | ], 43 | s0zTranslatedBugTypeId = "OOM", 44 | s0zTranslatedBugDescription = "The application caused a fatal exception to indicate it was unable to allocate enough memory.", 45 | s0zTranslatedSecurityImpact = None, 46 | ), 47 | # Assert -> OOM 48 | cBugTranslation( 49 | srzOriginalBugTypeId = r"Assert", 50 | azs0rbAppliesOnlyToTopStackFrame = [ 51 | rb".*!v8::Utils::ReportOOMFailure", 52 | ], 53 | s0zTranslatedBugTypeId = "OOM", 54 | s0zTranslatedBugDescription = "The application caused a fatal exception to indicate it was unable to allocate enough memory.", 55 | s0zTranslatedSecurityImpact = None, 56 | ), 57 | ]; 58 | -------------------------------------------------------------------------------- /mBugTranslations/__init__.py: -------------------------------------------------------------------------------- 1 | from .mBugTranslations import *; 2 | -------------------------------------------------------------------------------- /mBugTranslations/chakra_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Breakpoint -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"chakra\.dll!ReportFatalException", 8 | rb"chakra\.dll!Js::Exception::RaiseIfScriptActive", 9 | rb"chakra\.dll!Js::JavascriptError::ThrowOutOfMemoryError", 10 | rb"chakra\.dll!Js::Throw::OutOfMemory", 11 | rb"chakra\.dll!Memory::HeapAllocator::Alloc", 12 | ], 13 | ), 14 | cBugTranslation( 15 | srzOriginalBugTypeId = r"Breakpoint", 16 | azs0rbAppliesOnlyToTopStackFrame = [ 17 | rb"chakra\.dll!Js::Throw::FatalInternalError", 18 | ], 19 | s0zTranslatedBugTypeId = "Assert", 20 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate a fatal error was detected.", 21 | s0zTranslatedSecurityImpact = None, 22 | ), 23 | # Breakpoint -> OOM 24 | cBugTranslation( 25 | srzOriginalBugTypeId = r"Breakpoint", 26 | azs0rbAppliesOnlyToTopStackFrame = [ 27 | rb"chakra\.dll!MarkStack_OOM_fatal_error", 28 | rb"chakra\.dll!JavascriptDispatch_OOM_fatal_error", 29 | rb"chakra\.dll!OutOfMemory_fatal_error", 30 | rb"chakra\.dll!Js::JavascriptError::ThrowOutOfMemoryError", 31 | rb"chakra\.dll!Js::JavascriptExceptionOperators::ThrowOutOfMemory", 32 | ], 33 | s0zTranslatedBugTypeId = "OOM", 34 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate it was unable to allocate enough memory.", 35 | s0zTranslatedSecurityImpact = None, 36 | ), 37 | ]; 38 | -------------------------------------------------------------------------------- /mBugTranslations/clr_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # * -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"clr\.dll!RaiseTheExceptionInternalOnly", 8 | rb"clr\.dll!IL_Throw", 9 | ], 10 | ), 11 | ]; -------------------------------------------------------------------------------- /mBugTranslations/combase_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"combase\.dll!SendReport", 8 | rb"combase\.dll!RoOriginateError", 9 | rb"combase\.dll!RoFailFastWithErrorContextInternal2", 10 | rb".*!RoFailFastWithErrorContext", # This function is actually in the binary that contains the code that triggered it. 11 | ], 12 | ), 13 | ]; 14 | -------------------------------------------------------------------------------- /mBugTranslations/conhost_exe.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # * -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"conhost\.exe!o_terminate", 8 | rb"conhost\.exe!_scrt_unhandled_exception_filter", 9 | ], 10 | ), 11 | ]; 12 | -------------------------------------------------------------------------------- /mBugTranslations/corpol_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = []; 4 | # AVE@Arbitrary -> Ignored 5 | aoBugTranslations.append(cBugTranslation( 6 | # corpol.dll can test if DEP is enabled by storing a RET instruction in RW memory and calling it. This causes an 7 | # access violation if DEP is enabled, which is caught and handled. Therefore this exception should be ignored: 8 | srzOriginalBugTypeId = r"AVE@Arbitrary", 9 | azs0rbAppliesOnlyToTopStackFrame = [ 10 | rb"\(unknown\)", # The location where the RET instruction is stored is not inside a module and has no symbol. 11 | rb"corpol\.dll!IsNxON", 12 | ], 13 | s0zTranslatedBugTypeId = None, # This is not a bug; allow the application to continue running. 14 | s0zTranslatedBugDescription = None, 15 | s0zTranslatedSecurityImpact = None, 16 | )); 17 | -------------------------------------------------------------------------------- /mBugTranslations/edgecontent_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Assert -> OOM 5 | cBugTranslation( 6 | srzOriginalBugTypeId = r"Assert", 7 | azs0rbAppliesOnlyToTopStackFrame = [ 8 | rb"edgecontent\.dll!`anonymous namespace'::MemoryLimitWatchdogThreadProc", 9 | ], 10 | s0zTranslatedBugTypeId = "OOM", 11 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate it was unable to allocate enough memory.", 12 | s0zTranslatedSecurityImpact = None, 13 | ), 14 | ]; 15 | -------------------------------------------------------------------------------- /mBugTranslations/edgehtml_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # OOM -> hide irrelevant frames 5 | cBugTranslation( 6 | srzOriginalBugTypeId = r"OOM", 7 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 8 | rb"edgehtml\.dll!Streams::Chunk<.+>::InternalAlloc", 9 | ], 10 | ), 11 | ]; 12 | -------------------------------------------------------------------------------- /mBugTranslations/iso.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Assert -> OOM 5 | cBugTranslation( 6 | srzOriginalBugTypeId = r"Assert(:HRESULT)?", 7 | azs0rbAppliesOnlyToTopStackFrame = [ 8 | rb".*!CIsoMalloc::_InitializeEntry", 9 | ], 10 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 11 | rb".*!CIsoScope::_?Alloc\w+", 12 | rb".*!IsoAllocMessageBuffer", 13 | ], 14 | s0zTranslatedBugTypeId = "OOM", 15 | s0zTranslatedBugDescription = "The application triggered a fail fast application exit to indicate it was unable to allocate enough memory.", 16 | s0zTranslatedSecurityImpact = "Unlikely to be exploitable, unless you can find a way to avoid this breakpoint.", 17 | ), 18 | ]; 19 | -------------------------------------------------------------------------------- /mBugTranslations/jscript9_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Breakpoint -> ignore stack 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"jscript9\.dll!ReportFatalException", 8 | ], 9 | ), 10 | cBugTranslation( 11 | srzOriginalBugTypeId = r"Breakpoint", 12 | azs0rbAppliesOnlyToTopStackFrame = [ 13 | rb"jscript9\.dll!JavascriptDispatch_OOM_fatal_error", 14 | ], 15 | s0zTranslatedBugTypeId = "OOM", 16 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate it was unable to allocate enough memory.", 17 | s0zTranslatedSecurityImpact = None, 18 | ), 19 | ]; 20 | -------------------------------------------------------------------------------- /mBugTranslations/kernel32_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # * -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"kernel32\.dll!RaiseExceptionStub", 8 | rb"kernel32\.dll!HeapFreeStub", 9 | ], 10 | ), 11 | ]; 12 | -------------------------------------------------------------------------------- /mBugTranslations/kernelbase_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | cBugTranslation( 5 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 6 | # There are a lot of helper functions that clutter the stack without 7 | # providing insight into the issue. These are all hidden by BugId: 8 | rb"kernelbase\.dll!DebugBreak", 9 | rb"kernelbase\.dll!FindClose", 10 | rb"kernelbase\.dll!LocalFree", 11 | rb"kernelbase\.dll!Raise.*Exception", 12 | rb"kernelbase\.dll!UnhandledExceptionFilter", 13 | rb"kernelbase\.dll!TerminateProcessOnMemoryExhaustion", 14 | ], 15 | ), 16 | ]; 17 | -------------------------------------------------------------------------------- /mBugTranslations/mBugTranslations.py: -------------------------------------------------------------------------------- 1 | from .fApplyBugTranslationsToBugReport import fApplyBugTranslationsToBugReport; 2 | __all__ = [ 3 | "fApplyBugTranslationsToBugReport", 4 | ]; 5 | -------------------------------------------------------------------------------- /mBugTranslations/mshtml_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Breakpoint -> OOM 5 | cBugTranslation( 6 | srzOriginalBugTypeId = r"Breakpoint", 7 | azs0rbAppliesOnlyToTopStackFrame = [ 8 | rb"mshtml\.dll!ReportFatalException", 9 | rb"mshtml\.dll!MarkStack_OOM_fatal_error", 10 | ], 11 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 12 | rb"mshtml\.dll!Memory::.*", 13 | rb"mshtml\.dll!MemoryProtection::.*", 14 | ], 15 | s0zTranslatedBugTypeId = "OOM", 16 | s0zTranslatedBugDescription = "The application triggered a breakpoint to indicate it was unable to allocate enough memory.", 17 | s0zTranslatedSecurityImpact = None, 18 | ), 19 | ]; -------------------------------------------------------------------------------- /mBugTranslations/ntdll_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | cBugTranslation( 5 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 6 | # There are a lot of helper functions that clutter the stack without 7 | # providing insight into the issue. These are all hidden by BugId: 8 | rb"ntdll\.dll!_C_specific_handler", 9 | rb"ntdll\.dll!DbgBreakPoint", 10 | rb"ntdll\.dll!DbgUiRemoteBreakin", 11 | rb"ntdll\.dll!FindNodeOrParent", 12 | rb"ntdll\.dll!Ki.*UserExceptionDispatcher", 13 | rb"ntdll\.dll!.*HandleInvalidUserCallTarget", 14 | rb"ntdll\.dll!RtlDispatchException", 15 | rb"ntdll\.dll!Rtl.*CriticalSection", 16 | rb"ntdll\.dll!RtlFailFast\d*", 17 | rb"ntdll\.dll!RtlInsertElementGenericTableAvl", 18 | rb"ntdll\.dll!Rtl.*(Allocate|Free).*Heap.*\w*", 19 | rb"ntdll\.dll!RtlpExecuteHandlerForException", 20 | rb"ntdll\.dll!RtlpFreeDebugInfo", 21 | rb"ntdll\.dll!RtlRaiseException", 22 | rb"ntdll\.dll!RtlReportCriticalFailure", 23 | rb"ntdll\.dll!RtlUserThreadStart\$filt\$0", 24 | rb"ntdll\.dll!v?DbgPrint.*", 25 | ], 26 | ), 27 | cBugTranslation( 28 | # Breakpoint when intializing process -> ignore. 29 | srzOriginalBugTypeId = r"Breakpoint", 30 | azs0rbAppliesOnlyToTopStackFrame = [ 31 | rb"ntdll\.dll!LdrpDoDebuggerBreak", 32 | rb"ntdll\.dll!LdrpInitializeProcess", 33 | ], 34 | s0zTranslatedBugTypeId = None, 35 | ), 36 | # Breakpoint -> HeapCorrupt 37 | cBugTranslation( 38 | srzOriginalBugTypeId = r"Breakpoint", 39 | azs0rbAppliesOnlyToTopStackFrame = [ 40 | rb"ntdll\.dll!RtlpHeapHandleError", 41 | rb"ntdll\.dll!RtlpBreakPointHeap", 42 | ], 43 | s0zTranslatedBugTypeId = "HeapCorrupt", 44 | s0zTranslatedBugDescription = "A breakpoint was triggered to indicate heap corruption was detected", 45 | s0zTranslatedSecurityImpact = "This is probably an exploitable security issue", 46 | ), 47 | # AVR@Reserved -> AVR@CFG 48 | cBugTranslation( 49 | srzOriginalBugTypeId = r"AVR@(?:Reserved|Invalid)", 50 | azs0rbAppliesOnlyToTopStackFrame = [ 51 | rb"ntdll\.dll!LdrpDispatchUserCallTarget", 52 | rb"ntdll\.dll!LdrpValidateUserCallTarget(?:BitMapCheck|ES)?", 53 | ], 54 | s0zTranslatedBugTypeId = "AVR@CFG", 55 | s0zTranslatedBugDescription = "The process attempted to call a function using an invalid function pointer, " \ 56 | "which caused an access violation exception in Control Flow Guard. This is often caused by a NULL pointer.", 57 | s0zTranslatedSecurityImpact = "Unlikely to be an exploitable security issue, unless you can control the invalid function pointer", 58 | ), 59 | # AVE:NULL @ ntdll.dll!NtWow64IsProcessorFeaturePresent -> ignore 60 | cBugTranslation( 61 | srzOriginalBugTypeId = r"AVE:NULL", 62 | azs0rbAppliesOnlyToTopStackFrame = [ 63 | rb"ntdll\.dll!NtWow64IsProcessorFeaturePresent", 64 | ], 65 | s0zTranslatedBugTypeId = None, 66 | ), 67 | # AVW:NULL @ ntdll.dll!RtlpWaitOnCriticalSection -> ignore 68 | cBugTranslation( 69 | srzOriginalBugTypeId = r"AVW:NULL(\+.*)?", 70 | azs0rbAppliesOnlyToTopStackFrame = [ 71 | rb"ntdll\.dll!RtlpWaitOnCriticalSection", 72 | ], 73 | s0zTranslatedBugTypeId = None, 74 | ), 75 | ]; -------------------------------------------------------------------------------- /mBugTranslations/oledb32_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Apparently this code will try to see if a string is NULL terminated by 5 | # scanning memory until it sees a NULL, regardless of the size of the buffer 6 | # This code is wrapped in an exception handler to detect when it is reading 7 | # memory out of bounds. This is of course a very bad idea, but it is how 8 | # things work in MS Office... sigh. 9 | cBugTranslation( 10 | srzOriginalBugTypeId = r"OOBR\[.*\].*", 11 | azs0rbAppliesOnlyToTopStackFrame = [ 12 | rb"oledb32\.dll!SafeCheckWCharNullTermination", 13 | ], 14 | s0zTranslatedBugTypeId = None, 15 | ), 16 | ]; -------------------------------------------------------------------------------- /mBugTranslations/ucrtbase_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # * -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb"ucrtbase\.dll!abort", 8 | rb"ucrtbase\.dll!terminate", 9 | rb"ucrtbase\.dll!__crt_state_management::wrapped_invoke<.+>", 10 | ], 11 | ), 12 | ]; 13 | -------------------------------------------------------------------------------- /mBugTranslations/verifier_dll.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # Everything reported through a verifier stop should get the verifier calls removed, as these are not relevant to 5 | # the bug; they are only the messenger. 6 | cBugTranslation( 7 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 8 | rb"verifier\.dll!.*", 9 | ], 10 | ), 11 | ]; 12 | -------------------------------------------------------------------------------- /mBugTranslations/wil.py: -------------------------------------------------------------------------------- 1 | from .cBugTranslation import cBugTranslation; 2 | 3 | aoBugTranslations = [ 4 | # * -> hide irrelevant frames 5 | cBugTranslation( 6 | azs0rbAdditionalIrrelevantStackFrameSymbols = [ 7 | rb".*!wil::details::DebugBreak", 8 | rb".*!wil::details::in1diag3::Throw_.+", 9 | rb".*!wil::details::ReportFailure_.+", 10 | rb".*!wil::details::ThrowResultExceptionInternal", 11 | rb".*!wil::details::WilDynamicLoadRaiseFailFastException", 12 | rb".*!wil::details::WilFailFast", 13 | rb".*!wil::details::WilRaiseFailFastException", 14 | ], 15 | ), 16 | # AppExit -> Assert 17 | cBugTranslation( 18 | azs0rbAppliesOnlyToTopStackFrame = [ 19 | rb".*!wil::details::ReportFailure", 20 | ], 21 | s0zTranslatedBugTypeId = "Assert", 22 | s0zTranslatedBugDescription = "The application triggered a fail fast application exit to indicate an assertion failed.", 23 | s0zTranslatedSecurityImpact = "Unlikely to be exploitable.", 24 | ), 25 | # Assert -> Assert:Win32 26 | cBugTranslation( 27 | azs0rbAppliesOnlyToTopStackFrame = [ 28 | rb".*!wil::details::(?:ReportFailure|in1diag3::_?FailFast)_Win32", 29 | ], 30 | s0zTranslatedBugTypeId = "Assert:Win32", 31 | ), 32 | # Assert -> Assert:HRESULT 33 | cBugTranslation( 34 | azs0rbAppliesOnlyToTopStackFrame = [ 35 | rb".*!wil::details::(?:ReportFailure|in1diag3::_?FailFast)_Hr", 36 | ], 37 | s0zTranslatedBugTypeId = "Assert:HRESULT", 38 | ), 39 | # Assert -> Assert:Unexpected 40 | cBugTranslation( 41 | azs0rbAppliesOnlyToTopStackFrame = [ 42 | rb".*!wil::details::(?:ReportFailure|in1diag3::_?FailFast)_Unexpected", 43 | ], 44 | s0zTranslatedBugTypeId = "Assert:Unexpected", 45 | ), 46 | # Assert -> OOM 47 | cBugTranslation( 48 | azs0rbAppliesOnlyToTopStackFrame = [ 49 | rb".*!wil::details::(?:ReportFailure|in1diag3::_?FailFast)_NullAlloc", 50 | ], 51 | s0zTranslatedBugTypeId = "OOM", 52 | s0zTranslatedBugDescription = "The application was unable to allocate enough memory.", 53 | ), 54 | # Assert:HRESULT -> OOM 55 | cBugTranslation( 56 | azs0rbAppliesOnlyToTopStackFrame = [ 57 | rb".*!wil::details::in1diag3::_?FailFast_NullAlloc", 58 | ], 59 | s0zTranslatedBugTypeId = "OOM", 60 | s0zTranslatedBugDescription = "The application was unable to allocate enough memory.", 61 | ), 62 | ]; 63 | -------------------------------------------------------------------------------- /mDisassembler/__init__.py: -------------------------------------------------------------------------------- 1 | from .cDisassembly import cDisassembly; 2 | from .cInstruction import cInstruction; 3 | from .fo0GetDisassemblyForProcessStartAddressAndNumberOfBytes import fo0GetDisassemblyForProcessStartAddressAndNumberOfBytes; 4 | from .fo0GetDisassemblyForProcessStartAddressAndNumberOfInstructions import fo0GetDisassemblyForProcessStartAddressAndNumberOfInstructions; 5 | from .fo0GetInstructionForProcessAndAddress import fo0GetInstructionForProcessAndAddress; 6 | from .fo0GetInstructionForProcessAndBeforeAddress import fo0GetInstructionForProcessAndBeforeAddress; 7 | 8 | __all__ = [ 9 | "cDisassembly", 10 | "cInstruction", 11 | "fo0GetDisassemblyForProcessStartAddressAndNumberOfBytes", 12 | "fo0GetDisassemblyForProcessStartAddressAndNumberOfInstructions", 13 | "fo0GetInstructionForProcessAndAddress", 14 | "fo0GetInstructionForProcessAndBeforeAddress", 15 | ]; -------------------------------------------------------------------------------- /mDisassembler/cDisassembly.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertTypes; 2 | 3 | from .cInstruction import cInstruction; 4 | 5 | class cDisassembly(object): 6 | def __init__(oSelf, aoInstructions): 7 | fAssertTypes({ 8 | "aoInstructions": (aoInstructions, [cInstruction]), 9 | }); 10 | oSelf.__aoInstructions = aoInstructions; 11 | 12 | @property 13 | def uLength(oSelf): 14 | return len(oSelf.__aoInstructions); 15 | 16 | def foGetInstruction(oSelf, uIndex): 17 | fAssertTypes({ 18 | "uIndex": (uIndex, int), 19 | }); 20 | return oSelf.__aoInstructions[uIndex]; 21 | 22 | def fo0GetInstructionAtAddress(oSelf, uAddress): 23 | for oInstruction in oSelf.__aoInstructions: 24 | if oInstruction.uAddress == uAddress: 25 | return oInstruction; 26 | return None; 27 | 28 | def __len__(oSelf): 29 | return len(oSelf.__aoInstructions); 30 | 31 | def __str__(oSelf): 32 | return "\r\n".join( 33 | str(oInstruction) 34 | for oInstruction in oSelf.__aoInstructions 35 | ); 36 | -------------------------------------------------------------------------------- /mDisassembler/cInstruction.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertType, fAssertTypes; 2 | from mWindowsAPI import fsHexNumber; 3 | 4 | class cInstruction(object): 5 | def __init__(oSelf, uAddress, sbBytes, tsbPrefixes, sbName, tsbArguments): 6 | fAssertTypes({ 7 | "uAddress": (uAddress, int), 8 | "sbBytes": (sbBytes, bytes), 9 | "tsbPrefixes": (tsbPrefixes, tuple), 10 | "sbName": (sbName, bytes), 11 | "tsbArguments": (tsbArguments, tuple), 12 | }); 13 | for uIndex in range(len(tsbPrefixes)): 14 | fAssertType(f"tsbPrefixes[{uIndex}]", tsbPrefixes[uIndex], bytes); 15 | for uIndex in range(len(tsbArguments)): 16 | fAssertType(f"tsbArguments[{uIndex}]", tsbArguments[uIndex], bytes); 17 | oSelf.__uAddress = uAddress; 18 | oSelf.__sbBytes = sbBytes; 19 | oSelf.__tsbPrefixes = tsbPrefixes; 20 | oSelf.__sbName = sbName; 21 | oSelf.__tsbArguments = tsbArguments; 22 | 23 | @property 24 | def uAddress(oSelf): 25 | return oSelf.__uAddress; 26 | 27 | @property 28 | def sbBytes(oSelf): 29 | return oSelf.__sbBytes; 30 | @property 31 | def sBytes(oSelf): 32 | return " ".join("%02X" % uByte for uByte in oSelf.__sbBytes); 33 | @property 34 | def uSize(oSelf): 35 | return len(oSelf.sbBytes); 36 | 37 | @property 38 | def tsbPrefixes(oSelf): 39 | return oSelf.__tsbPrefixes; 40 | @property 41 | def sPrefixes(oSelf): 42 | return str(b" ".join(oSelf.__tsbPrefixes), "ascii", "strict"); 43 | 44 | @property 45 | def sbName(oSelf): 46 | return oSelf.__sbName; 47 | @property 48 | def sName(oSelf): 49 | return str(oSelf.__sbName, "ascii", "strict"); 50 | 51 | @property 52 | def tsbArguments(oSelf): 53 | return oSelf.__tsbArguments; # Does not prevent overwriting them! 54 | @property 55 | def sArguments(oSelf): 56 | return str(b", ".join(oSelf.__tsbArguments), "ascii", "strict"); 57 | 58 | @property 59 | def sInstruction(oSelf): 60 | return "%s%-7s %s" % ( 61 | (oSelf.sPrefixes + " ") if oSelf.__tsbPrefixes else "", 62 | oSelf.sName, 63 | oSelf.sArguments 64 | ); 65 | 66 | def __str__(oSelf): 67 | return "%-10s | %-20s | %s" % ( 68 | fsHexNumber(oSelf.uAddress), 69 | oSelf.sBytes, 70 | oSelf.sInstruction, 71 | ); 72 | -------------------------------------------------------------------------------- /mDisassembler/fo0GetDisassemblyForProcessStartAddressAndNumberOfBytes.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertTypes; 2 | 3 | from mWindowsAPI import fsHexNumber; 4 | from .fo0GetDisassemblyForProcessAndCdbCommand import fo0GetDisassemblyForProcessAndCdbCommand; 5 | 6 | def fo0GetDisassemblyForProcessStartAddressAndNumberOfBytes( 7 | oProcess, 8 | uStartAddress, 9 | uNumberOfBytes, 10 | ): 11 | fAssertTypes({ 12 | "uStartAddress": (uStartAddress, int), 13 | "uNumberOfBytes": (uNumberOfBytes, int), 14 | }); 15 | assert 0 <= uNumberOfBytes < 0x1000, \ 16 | "Request to disassemble %d bytes seems a little excessive!" % uNumberOfBytes; 17 | return fo0GetDisassemblyForProcessAndCdbCommand( 18 | oProcess, 19 | sbCommand = b"u 0x%X 0x%X" % (uStartAddress, uStartAddress + uNumberOfBytes), 20 | sbComment = b"Disassemble %d bytes at %s" % (uNumberOfBytes, bytes(fsHexNumber(uStartAddress), "ascii", "strict")), 21 | ); 22 | 23 | -------------------------------------------------------------------------------- /mDisassembler/fo0GetDisassemblyForProcessStartAddressAndNumberOfInstructions.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertTypes; 2 | 3 | from mWindowsAPI import fsHexNumber; 4 | 5 | from .fo0GetDisassemblyForProcessAndCdbCommand import fo0GetDisassemblyForProcessAndCdbCommand; 6 | 7 | def fo0GetDisassemblyForProcessStartAddressAndNumberOfInstructions( 8 | oProcess, 9 | uStartAddress, 10 | uNumberOfInstructions, 11 | ): 12 | fAssertTypes({ 13 | "uStartAddress": (uStartAddress, int), 14 | "uNumberOfInstructions": (uNumberOfInstructions, int), 15 | }); 16 | assert 0 <= uNumberOfInstructions < 0x100, \ 17 | "Request to disassemble %d instructions seems a little excessive!" % uNumberOfInstructions; 18 | return fo0GetDisassemblyForProcessAndCdbCommand( 19 | oProcess, 20 | sbCommand = b"u 0x%X L%d" % (uStartAddress, uNumberOfInstructions), 21 | sbComment = b"Disassemble %d instructions at %s" % (uNumberOfInstructions, bytes(fsHexNumber(uStartAddress), "ascii", "strict")), 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /mDisassembler/fo0GetInstructionForProcessAndAddress.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertTypes; 2 | 3 | from mWindowsAPI import fsHexNumber; 4 | 5 | from .fo0GetDisassemblyForProcessAndCdbCommand import fo0GetDisassemblyForProcessAndCdbCommand; 6 | 7 | def fo0GetInstructionForProcessAndAddress( 8 | oProcess, 9 | uAddress, 10 | ): 11 | fAssertTypes({ 12 | "uAddress": (uAddress, int), 13 | }); 14 | o0Disassembly = fo0GetDisassemblyForProcessAndCdbCommand( 15 | oProcess, 16 | sbCommand = b"u 0x%X L%d" % (uAddress, 1), 17 | sbComment = b"Disassemble instruction at %s" % (bytes(fsHexNumber(uAddress), "ascii", "strict"),), 18 | ); 19 | return o0Disassembly.foGetInstruction(0) if o0Disassembly and len(o0Disassembly) == 1 else None; 20 | 21 | -------------------------------------------------------------------------------- /mDisassembler/fo0GetInstructionForProcessAndBeforeAddress.py: -------------------------------------------------------------------------------- 1 | from mNotProvided import fAssertTypes; 2 | 3 | from mWindowsAPI import fsHexNumber; 4 | 5 | from .fo0GetDisassemblyForProcessAndCdbCommand import fo0GetDisassemblyForProcessAndCdbCommand; 6 | 7 | def fo0GetInstructionForProcessAndBeforeAddress( 8 | oProcess, 9 | uAddress, 10 | ): 11 | fAssertTypes({ 12 | "uAddress": (uAddress, int), 13 | }); 14 | # Disassemble the 32 bytes before the address; we're hoping that is enough to 15 | # "align" the disassembly by the time it gets to the instruction we want 16 | # but this is not guaranteed. 17 | o0Disassembly = fo0GetDisassemblyForProcessAndCdbCommand( 18 | oProcess, 19 | sbCommand = b"u 0x%X 0x%X" % (uAddress - 32, uAddress), 20 | sbComment = b"Disassemble instructions before %s" % (bytes(fsHexNumber(uAddress), "ascii", "strict"),), 21 | ); 22 | if o0Disassembly is None or o0Disassembly.uLength == 0: 23 | return None; 24 | # Check if one of the instructions in the disassembly ends at the address. 25 | # It is not guaranteed that that was indeed the last executed instruction 26 | # but it is likely (sorry, no idea how likely). 27 | for uIndex in range(len(o0Disassembly)): 28 | oInstruction = o0Disassembly.foGetInstruction(uIndex); 29 | if oInstruction.uAddress + oInstruction.uSize == uAddress: 30 | return oInstruction; 31 | return None; 32 | 33 | -------------------------------------------------------------------------------- /mExceptions.py: -------------------------------------------------------------------------------- 1 | class cNoAccessToProcessException(Exception): 2 | def __init__(oSelf, uProcessId: int): 3 | oSelf.uProcessId = uProcessId; 4 | Exception.__init__(oSelf, "The process with id %d/0x%X cannot be accessed." % (uProcessId, uProcessId)); 5 | -------------------------------------------------------------------------------- /mExports.py: -------------------------------------------------------------------------------- 1 | from .cBugId import cBugId; 2 | 3 | __all__ = [ 4 | "cBugId", 5 | ]; -------------------------------------------------------------------------------- /mHeapManager/__init__.py: -------------------------------------------------------------------------------- 1 | from .cPageHeapManagerData import cPageHeapManagerData; 2 | from .cWindowsHeapManagerData import cWindowsHeapManagerData; 3 | 4 | __all__ = [ 5 | "cPageHeapManagerData", 6 | "cWindowsHeapManagerData", 7 | ]; -------------------------------------------------------------------------------- /mHeapManager/cPageHeapManagerData/__init__.py: -------------------------------------------------------------------------------- 1 | from .cPageHeapManagerData import cPageHeapManagerData; 2 | 3 | __all__ = [ 4 | "cPageHeapManagerData", 5 | ]; -------------------------------------------------------------------------------- /mHeapManager/cPageHeapManagerData/fo0GetAllocationHeaderForVirtualAllocationAndPointerSize.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import fsHexNumber; 2 | 3 | from .mPageHeapStructuresAndStaticValues import ( 4 | DPH_ALLOCATION_HEADER32, 5 | DPH_ALLOCATION_HEADER64, 6 | auValidPageHeapAllocationHeaderMarkers, 7 | ); 8 | 9 | def fo0GetAllocationHeaderForVirtualAllocationAndPointerSize(oHeapBlockVirtualAllocation, uPointerSize, bDebugOutput): 10 | assert oHeapBlockVirtualAllocation.bAllocated, \ 11 | "Please check oHeapBlockVirtualAllocation.bAllocated == True before making this call\r\n%s" % \ 12 | str(oHeapBlockVirtualAllocation); 13 | # A page heap allocation for a heap block starts with a DPH_ALLOCATION_HEADER structure: 14 | DPH_ALLOCATION_HEADER = {4: DPH_ALLOCATION_HEADER32, 8: DPH_ALLOCATION_HEADER64}[uPointerSize]; 15 | oAllocationHeader = oHeapBlockVirtualAllocation.foReadStructureForOffset( 16 | cStructure = DPH_ALLOCATION_HEADER, 17 | uOffset = 0, 18 | ); 19 | if not oAllocationHeader.uMarker in auValidPageHeapAllocationHeaderMarkers: 20 | if bDebugOutput: 21 | print("cPageHeapManagerData: Allocation Header marker %s not valid in %s => None\r\n%s" % ( 22 | fsHexNumber(oAllocationHeader.uMarker.fuGetValue()), 23 | oHeapBlockVirtualAllocation, 24 | "\r\n".join(oAllocationHeader.fasDump()), 25 | )); 26 | return None; 27 | # Maybe this should be enabled in a "strict" setting, as I would like to know what other values are common. But I've 28 | # missed bugs because this assertion terminated cBugId while reporting one, which is not good. 29 | # if hasattr(oAllocationHeader, "uPadding"): 30 | # assert oAllocationHeader.uPadding in auValidPageHeapAllocationHeaderPaddings, \ 31 | # "Page heap allocation header padding has unhandled value 0x%X (expected %s):\r\n%s" % \ 32 | # (oAllocationHeader.uPadding, " or ".join(["0x%X" % uValidMarker for uValidMarker in auValidPageHeapAllocationHeaderPaddings]), 33 | # "\r\n".join(oAllocationHeader.fasDump())); 34 | if bDebugOutput: 35 | print(("┌─ oAllocationHeader ").ljust(80, "─")); 36 | for sLine in oAllocationHeader.fasDump(): 37 | print("│ %s" % sLine); 38 | print("└".ljust(80, "─")); 39 | return oAllocationHeader; 40 | -------------------------------------------------------------------------------- /mHeapManager/cPageHeapManagerData/fo0GetPageHeapBlockForProcessAndAddress.py: -------------------------------------------------------------------------------- 1 | from mWindowsAPI import cVirtualAllocation, fsHexNumber; 2 | 3 | from .mPageHeapStructuresAndStaticValues import ( 4 | DPH_HEAP_BLOCK32, 5 | DPH_HEAP_BLOCK64, 6 | ); 7 | 8 | def fo0GetPageHeapBlockForProcessAndAddress(oProcess, uPageHeapBlockStartAddress, bDebugOutput = False): 9 | # DPH_HEAP_BLOCK structures are stored sequentially in a virtual allocation. 10 | oPageHeapBlockVirtualAllocation = cVirtualAllocation( 11 | uProcessId = oProcess.uId, 12 | uAddress = uPageHeapBlockStartAddress, 13 | ); 14 | if not oPageHeapBlockVirtualAllocation.bAllocated: 15 | if bDebugOutput: print( 16 | "cPageHeapManagerData fo0GetPageHeapBlockForProcessAndAddress: "\ 17 | "no memory allocated at page heap block address %s." % ( 18 | fsHexNumber(uPageHeapBlockStartAddress), 19 | )); 20 | # The memory for this heap block has been freed: we cannot determine the 21 | # location for the DPH_ALLOCATION_HEADER structure, and thus cannot 22 | # provide any result. 23 | return None; 24 | # Try to read the page heap allocation information 25 | DPH_HEAP_BLOCK = {4: DPH_HEAP_BLOCK32, 8: DPH_HEAP_BLOCK64}[oProcess.uPointerSize]; 26 | o0PageHeapBlock = oProcess.fo0ReadStructureForAddress( 27 | DPH_HEAP_BLOCK, 28 | uPageHeapBlockStartAddress, 29 | ); 30 | if bDebugOutput: 31 | if o0PageHeapBlock: 32 | print( 33 | "cPageHeapManagerData fo0GetPageHeapBlockForProcessAndAddress: " \ 34 | "page heap block:" 35 | ); 36 | print(("┌─ DPH_HEAP_BLOCK ").ljust(80, "─")); 37 | for sLine in o0PageHeapBlock.fasDump(): 38 | print("│ %s" % sLine); 39 | print("└".ljust(80, "─")); 40 | else: 41 | print( 42 | "cPageHeapManagerData fo0GetPageHeapBlockForProcessAndAddress: " \ 43 | "page heap block vould not be read from address %s." % ( 44 | fsHexNumber(uPageHeapBlockStartAddress), 45 | ) 46 | ); 47 | return o0PageHeapBlock; 48 | -------------------------------------------------------------------------------- /mHeapManager/cWindowsHeapManagerData.py: -------------------------------------------------------------------------------- 1 | from .iHeapManagerData import iHeapManagerData; 2 | 3 | class cWindowsHeapManagerData(iHeapManagerData): 4 | def __init__(oSelf, 5 | oVirtualAllocation, 6 | uHeapEntryStartAddress, 7 | uHeapEntrySize, 8 | uHeapBlockStartAddress, 9 | uHeapBlockSize, 10 | bAllocated, 11 | ): 12 | oSelf.oVirtualAllocation = oVirtualAllocation; 13 | 14 | oSelf.uHeapEntryStartAddress = uHeapEntryStartAddress; 15 | oSelf.uHeapEntrySize = uHeapEntrySize; 16 | oSelf.uHeapEntryEndAddress = uHeapEntryStartAddress + uHeapEntrySize; 17 | 18 | oSelf.uHeapBlockStartAddress = uHeapBlockStartAddress; 19 | oSelf.uHeapBlockSize = uHeapBlockSize; 20 | oSelf.uHeapBlockEndAddress = uHeapBlockStartAddress + uHeapBlockSize; 21 | 22 | oSelf.bAllocated = bAllocated; 23 | oSelf.bFreed = not bAllocated; 24 | 25 | oSelf.bCorruptionDetected = False; 26 | 27 | if oSelf.uHeapEntrySize and oSelf.uHeapEntryEndAddress == uHeapBlockStartAddress: 28 | # The heap entry is right before the heap block; include both in the dump 29 | oSelf.uMemoryDumpStartAddress = uHeapEntryStartAddress; 30 | oSelf.uMemoryDumpEndAddress = oSelf.uHeapBlockEndAddress; 31 | else: 32 | oSelf.uMemoryDumpStartAddress = uHeapBlockStartAddress; 33 | oSelf.uMemoryDumpEndAddress = oSelf.uHeapBlockEndAddress; 34 | # Convenience 35 | oSelf.uMemoryDumpSize = oSelf.uMemoryDumpEndAddress - oSelf.uMemoryDumpStartAddress; 36 | 37 | def fatxGetMemoryRemarks(oSelf): 38 | return [tx for tx in [ 39 | ("Allocation start", oSelf.oVirtualAllocation.uStartAddress, None), 40 | oSelf.uHeapBlockHeaderSize and 41 | ("Heap block header start", oSelf.uHeapBlockHeaderStartAddress, None), 42 | oSelf.uHeapBlockHeaderSize and 43 | ("Heap block header end", oSelf.uHeapBlockHeaderEndAddress, None), 44 | ("Heap block start", oSelf.uHeapBlockStartAddress, None), 45 | ("Heap block end", oSelf.uHeapBlockEndAddress, None), 46 | ("Allocation end", oSelf.oVirtualAllocation.uEndAddress, None), 47 | ] if tx]; -------------------------------------------------------------------------------- /mHeapManager/iHeapManagerData.py: -------------------------------------------------------------------------------- 1 | from ..ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress import \ 2 | ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress; 3 | 4 | class iHeapManagerData(object): 5 | def __init__(oSelf, 6 | oVirtualAllocation, 7 | uHeapBlockStartAddress, 8 | uHeapBlockSize, 9 | bAllocated, 10 | ): 11 | oSelf.oVirtualAllocation = oVirtualAllocation; 12 | 13 | oSelf.uHeapBlockStartAddress = uHeapBlockStartAddress; 14 | oSelf.uHeapBlockSize = uHeapBlockSize; 15 | oSelf.uHeapBlockEndAddress = uHeapBlockStartAddress + uHeapBlockSize; 16 | 17 | oSelf.bAllocated = bAllocated; 18 | oSelf.bFreed = not bAllocated; 19 | 20 | oSelf.bCorruptionDetected = False; 21 | 22 | oSelf.uMemoryDumpStartAddress = uHeapBlockStartAddress - oSelf.uHeapBlockHeaderSize; 23 | oSelf.uMemoryDumpEndAddress = ( 24 | oSelf.uAllocationEndPaddingEndAddress if oSelf.uAllocationEndPaddingSize 25 | else oSelf.uHeapBlockEndAddress 26 | ); 27 | oSelf.uMemoryDumpSize = oSelf.uMemoryDumpEndAddress - oSelf.uMemoryDumpStartAddress; 28 | 29 | def ftsGetIdAndDescriptionForAddress(oSelf, uAddress): 30 | return ftsGetMemoryBlockSizeAndOffsetIdAndDescriptionForAddress( 31 | uBlockStartAddress = oSelf.uHeapBlockStartAddress, 32 | uBlockSize = oSelf.uHeapBlockSize, 33 | sBlockType = "%sheap" % ("freed " if oSelf.bFreed else "",), 34 | uAddress = uAddress, 35 | ); 36 | 37 | def fatxGetMemoryRemarks(oSelf): 38 | return [tx for tx in [ 39 | ("Allocation start", oSelf.oVirtualAllocation.uStartAddress, None), 40 | ("Heap block start", oSelf.uHeapBlockStartAddress, None), 41 | ("Heap block end", oSelf.uHeapBlockEndAddress, None), 42 | ("Allocation end", oSelf.oVirtualAllocation.uEndAddress, None), 43 | ] if tx]; 44 | -------------------------------------------------------------------------------- /mModule/__init__.py: -------------------------------------------------------------------------------- 1 | from .cModule import cModule; 2 | 3 | __all__ = [ 4 | "cModule", 5 | ]; -------------------------------------------------------------------------------- /mProcess/__init__.py: -------------------------------------------------------------------------------- 1 | from .cProcess import cProcess; 2 | 3 | __all__ = [ 4 | "cProcess", 5 | ]; -------------------------------------------------------------------------------- /mProcess/cProcess_fEnsurePageHeapIsEnabled.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | from mRegistry import cRegistryHiveKey; 4 | 5 | # Cache which binaries have page heap enabled/disabled. this assumes that the user does not modify the page heap 6 | # settings while BugId is running. 7 | gdbPageHeapEnabled_by_sBinaryName = {}; 8 | guRequiredFlags = 0x02109870; 9 | 10 | def cProcess_fEnsurePageHeapIsEnabled(oProcess): 11 | if oProcess.bPageHeapEnabled is not None: 12 | return; # We have ensured this before. 13 | if oProcess.sBinaryName in gdbPageHeapEnabled_by_sBinaryName: 14 | oProcess.bPageHeapEnabled = gdbPageHeapEnabled_by_sBinaryName[oProcess.sBinaryName]; 15 | return; 16 | oRegistryHiveKey = cRegistryHiveKey( 17 | sHiveName = "HKLM", 18 | sKeyPath = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\%s" % oProcess.sBinaryName, 19 | ); 20 | o0GlobalFlags = oRegistryHiveKey.fo0GetValueForName("GlobalFlag"); 21 | if o0GlobalFlags and o0GlobalFlags.sTypeName == "REG_SZ" and re.match("^0x[0-9a-fA-F]{8}$", o0GlobalFlags.xValue): 22 | uValue = int(o0GlobalFlags.xValue[2:], 16); 23 | if uValue & guRequiredFlags == guRequiredFlags: 24 | # Page heap is enabled with all the required options: 25 | gdbPageHeapEnabled_by_sBinaryName[oProcess.sBinaryName] = True; 26 | oProcess.bPageHeapEnabled = True; 27 | return; 28 | # Page heap is not enabled or not all the required options are enabled 29 | gdbPageHeapEnabled_by_sBinaryName[oProcess.sBinaryName] = False; 30 | oProcess.bPageHeapEnabled = False; 31 | # The "id" cdb uses to identify modules in symbols is normally based on the name of the module binary file. 32 | # However, for unknown reasons, cdb will sometimes use "imageXXXXXXXX", where XXXXXXXX is the hex address at 33 | # which the module is loaded (this has only been seen on x86 so far). In such cases, page heap appears to be 34 | # disabled; I believe whatever keeps cdb from determining the module binary's file name is also keeping page heap 35 | # from doing the same. In such cases, it appears that page heap cannot be enabled by the user, so we'll report it 36 | # with the second argument as "False" (not preventable). 37 | # NOTE: DISABLED TO AVOID ACCESSING oMainModule before the process is fully loaded and since `cModule.sbCdbId` 38 | # has been removed. 39 | bPreventable = True;#re.match(rb"image[0-9a-f]{8}", oProcess.oMainModule.sbCdbId, re.I) is None; 40 | # Report it 41 | if not oProcess.oCdbWrapper.fbFireCallbacks("Page heap not enabled", oProcess, bPreventable): 42 | # This is fatal if it's preventable and there is no callback handler 43 | assert not bPreventable, \ 44 | "Full page heap is not enabled for %s in process 0x%X." % (oProcess.sBinaryName, oProcess.uId); 45 | -------------------------------------------------------------------------------- /mProcess/cProcess_fLoadSymbols.py: -------------------------------------------------------------------------------- 1 | def cProcess_fLoadSymbols(oSelf): 2 | # Running this more than once would make no sense. 3 | if oSelf.oCdbWrapper.bDoNotLoadSymbols: 4 | return; 5 | if oSelf.bSymbolsLoaded: 6 | return; 7 | oSelf.fasbExecuteCdbCommand( 8 | sbCommand = b".symopt+ 0x80000000", 9 | sb0Comment = b"Enable symbol loading debug messages", 10 | ); 11 | for oModule in oSelf.doModule_by_uStartAddress.values(): 12 | oModule.fLoadSymbols(); 13 | oSelf.fasbExecuteCdbCommand( 14 | sbCommand = b"lm a 0x%X" % oModule.uStartAddress, 15 | sb0Comment = b"Enable symbol loading debug messages", 16 | ); 17 | oSelf.fasbExecuteCdbCommand( 18 | sbCommand = b".symopt- 0x80000000", 19 | sb0Comment = b"Disable symbol loading debug messages", 20 | ); 21 | oSelf.bSymbolsLoaded = True; 22 | -------------------------------------------------------------------------------- /mProcess/cProcess_fa0txGetRegistersForThreadId.py: -------------------------------------------------------------------------------- 1 | from .dttxRelevantRegisters_by_sISA import dttxRelevantRegisters_by_sISA; 2 | from ..mCP437 import fsCP437FromBytesString; 3 | 4 | def cProcess_fa0txGetRegistersForThreadId(oProcess, uThreadId): 5 | oWindowsAPIThread = oProcess.oWindowsAPIProcess.foGetThreadForId(uThreadId); 6 | d0uRegisterValue_by_sbName = oWindowsAPIThread.fd0uGetRegisterValueByName(); 7 | if d0uRegisterValue_by_sbName is None: 8 | return None; 9 | duRegisterValue_by_sbName = d0uRegisterValue_by_sbName; 10 | atxRegisters = []; 11 | for (sbRegisterName, uBitSize, bFindDetails) in dttxRelevantRegisters_by_sISA[oProcess.sISA]: 12 | uRegisterValue = duRegisterValue_by_sbName[sbRegisterName]; 13 | s0Details = None; 14 | if bFindDetails: 15 | s0Details = oProcess.fs0GetDetailsForAddress(uRegisterValue); 16 | atxRegisters.append((sbRegisterName, uRegisterValue, uBitSize, s0Details)); 17 | return atxRegisters; 18 | -------------------------------------------------------------------------------- /mProcess/cProcess_fasbGetStack.py: -------------------------------------------------------------------------------- 1 | import os, re; 2 | 3 | from ..dxConfig import dxConfig; 4 | 5 | def cProcess_fasbGetStack(oProcess, sbGetStackCommand): 6 | # Get the stack, which should make sure all relevant symbols are loaded or at least marked as requiring loading. 7 | # Noisy symbol loading is turned on during the command, so there will be symbol loading debug messages in between 8 | # the stack output, which makes the stack hard to parse: it is therefore discarded and the command is executed 9 | # again later (without noisy symbol loading) when symbols are loaded. 10 | # This only makes sense if we're using symbol servers, so we can download the symbols again if they fail. 11 | oProcess.fLoadSymbols(); 12 | # Get the stack for real. At this point, no output from symbol loader is expected or handled. 13 | return oProcess.fasbExecuteCdbCommand( 14 | sbCommand = sbGetStackCommand, 15 | sb0Comment = b"Get stack", 16 | bOutputIsInformative = True, 17 | ); 18 | -------------------------------------------------------------------------------- /mProcess/cProcess_fo0GetFunctionForAddress.py: -------------------------------------------------------------------------------- 1 | def cProcess_fo0GetFunctionForAddress(oProcess, uAddress): 2 | sb0Symbol = oProcess.fsb0GetSymbolForAddress(uAddress, b"Get function symbol for address 0x%X" % uAddress); 3 | if sb0Symbol is None: 4 | return None; 5 | ( 6 | u0Address, 7 | sb0UnloadedModuleFileName, o0Module, u0ModuleOffset, 8 | o0Function, i0OffsetFromStartOfFunction 9 | ) = oProcess.ftxSplitSymbolOrAddress(sb0Symbol); 10 | return o0Function; -------------------------------------------------------------------------------- /mProcess/cProcess_fo0GetModuleForCdbId.py: -------------------------------------------------------------------------------- 1 | 2 | gbDebugOutput = False; 3 | 4 | def cProcess_fo0GetModuleForCdbId(oProcess, sbCdbId): 5 | # First check if we have cached this cdb id: 6 | for oModule in oProcess.doModule_by_uStartAddress.values(): 7 | if oModule.fbIsCdbIdCached() and oModule.sbCdbId == sbCdbId: 8 | return oModule; 9 | # TODO remove this once issue 131 is fixed. 10 | # https://github.com/SkyLined/BugId/issues/131 11 | asbLoadedModulesInCdbForDebugging = oProcess.fasbExecuteCdbCommand( 12 | b"lm;", 13 | b"Get list of loaded modules for debugging" 14 | ); 15 | asLoadedModulesByStartAddressForDebugging = [ 16 | "%s: %s (%s)" % ( 17 | oModule.uStartAddress, 18 | oModule.s0BinaryName or "", 19 | oModule.s0BinaryPath or "", 20 | ) 21 | for oModule in oProcess.doModule_by_uStartAddress.values() 22 | ]; 23 | # No; try to find the start address of the module for this cdb id: 24 | u0StartAddress = oProcess.fu0GetAddressForSymbol(sbCdbId); 25 | assert u0StartAddress is not None, \ 26 | "cdb does not appear to know the address for module symbol '%s'!?" % sbCdbId; 27 | uStartAddress = u0StartAddress; 28 | # No; try to get the module for the start address: 29 | o0Module = oProcess.fo0GetModuleForStartAddress(uStartAddress); 30 | assert o0Module is not None, \ 31 | "cdb reports module '%s' to be at address 0x%x but there is no module there!?" % ( 32 | sbCdbId, 33 | uStartAddress 34 | ); 35 | oModule = o0Module; 36 | if gbDebugOutput: 37 | print("cdb id %s (address %s) => %s" % ( 38 | repr(sbCdbId)[1:], 39 | fsHexNumber(uStartAddress), 40 | oModule, 41 | )); 42 | 43 | if oModule.sbCdbId != sbCdbId: 44 | # cdb ids may have aliases because life isn't hard enough without them. 45 | u0ModuleSymbolStartAddress = oProcess.fu0GetAddressForSymbol(oModule.sbCdbId); 46 | assert uStartAddress == u0ModuleSymbolStartAddress, \ 47 | "got unexpected module cdb id and address: requested %s=>%s, got %s=>%s (module at %s)" % ( 48 | repr(sbCdbId)[1:], 49 | fsHexNumber(uStartAddress), 50 | oModule.sbCdbId, 51 | fsHexNumber(u0ModuleSymbolStartAddress) if u0ModuleSymbolStartAddress is not None else "", 52 | fsHexNumber(oModule.uStartAddress), 53 | ); 54 | # Change the cdb id of the module to the one we were using already: 55 | oModule.sbCdbId = sbCdbId; 56 | return oModule; 57 | -------------------------------------------------------------------------------- /mProcess/cProcess_fo0GetPageHeapManagerDataForAddressNearHeapBlock.py: -------------------------------------------------------------------------------- 1 | from ..mHeapManager import cPageHeapManagerData; 2 | from ..dxConfig import dxConfig; 3 | 4 | def cProcess_fo0GetPageHeapManagerDataForAddressNearHeapBlock(oProcess, uAddressNearHeapBlock): 5 | if uAddressNearHeapBlock <= dxConfig["uMaxAddressOffset"]: 6 | return None; # Considered a NULL pointer; 7 | if uAddressNearHeapBlock >= (1 << ({"x86": 32, "x64": 64}[oProcess.sISA])): 8 | return None; # Value is not in the valid address range 9 | if oProcess.sISA == "x64" and uAddressNearHeapBlock >> 48 not in (0, 0xFFFF): 10 | # x64 supports 48 bits of address space for user and kernel mode 11 | # The upper 16 bits must be all 0 or all 1 for an address to be valid. 12 | return None; 13 | # TODO: In theory this should always work but it doesn't so I've added 14 | # an exception handler to work around it. I should find out when and why 15 | # it doesn't work and handle that correctly. 16 | try: 17 | return cPageHeapManagerData.fo0GetForProcessAndAddressNearHeapBlock( 18 | oProcess, 19 | uAddressNearHeapBlock = uAddressNearHeapBlock, 20 | ); 21 | except OSError: 22 | return None; -------------------------------------------------------------------------------- /mProcess/cProcess_fs0GetDetailsForAddress.py: -------------------------------------------------------------------------------- 1 | from ..mCP437 import fsCP437FromBytesString; 2 | 3 | def cProcess_fs0GetDetailsForAddress(oProcess, uAddress): 4 | sb0Symbol = oProcess.fsb0GetSymbolForAddress(uAddress, b"address 0x%X" % uAddress); 5 | if sb0Symbol: 6 | return fsCP437FromBytesString(sb0Symbol); 7 | o0HeapManagerData = oProcess.fo0GetHeapManagerDataForAddressNearHeapBlock( 8 | uAddressNearHeapBlock = uAddress, 9 | ); 10 | if o0HeapManagerData: 11 | (sSizeId, sOffsetId, sOffsetDescription, sSizeDescription) = \ 12 | o0HeapManagerData.ftsGetIdAndDescriptionForAddress(uAddress); 13 | return "%s %s" % (sOffsetDescription, sSizeDescription); 14 | return None; -------------------------------------------------------------------------------- /mProcess/cProcess_fsb0GetSymbolForAddress.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | from ..dxConfig import dxConfig; 4 | from ..fu0ValueFromCdbHexOutput import fu0ValueFromCdbHexOutput; 5 | from ..rbSymbolOrAddress import rbSymbolOrAddress; 6 | from ..mCP437 import fsCP437FromBytesString; 7 | 8 | grbAddress = re.compile( 9 | rb"\A" 10 | rb"[0-9`a-f]+" # address 11 | rb"\Z" 12 | ); 13 | grbSymbolWithOrWithoutAddress = re.compile( 14 | rb"\A" 15 | rb"(.*?)" # symbol 16 | rb"(?:" # optional { 17 | rb"\s+" rb"\" # whitespace "" 18 | rb")?" # } 19 | rb"(?:" # optional { 20 | rb"\s+" rb"\(" # whitespace "(" 21 | rb"\w+" # module cdb id 22 | rb"\+0x" # "+0x" 23 | rb"(?:[0-9`a-f]+?)" # offset 24 | rb"\)" # ")" 25 | rb")?" # } 26 | rb"(?:" # optional { 27 | rb"\s+" rb"\(" # whitespace "(" 28 | rb"(?:[0-9`a-f]+?)" # address 29 | rb"\)" # ")" 30 | rb")?" # } 31 | rb"\Z" 32 | ); 33 | 34 | 35 | def cProcess_fsb0GetSymbolForAddress(oProcess, uAddress, sbAddressDescription): 36 | oProcess.fLoadSymbols(); 37 | # Output for an invalid (NULL) pointer: 38 | # >00000000 39 | # Output for a module without symbol information (in x64 debugger): 40 | # >nmozglue+0xf0c4 (73f1f0c4) 41 | # Output for a valid symbol (in x86 debugger, notice different header aligning): 42 | # >ntdll!DbgBreakPoint (77ec1250) 43 | if uAddress < dxConfig["uMaxAddressOffset"]: 44 | return None; # quick return for NULL pointers 45 | sbGetSymbolCommand = b'.printf "%%y\\n", 0x%X;' % uAddress; 46 | asbSymbolOutput = oProcess.fasbExecuteCdbCommand( 47 | sbCommand = sbGetSymbolCommand, 48 | sb0Comment = b"Get symbol for %s" % (sbAddressDescription,), 49 | ); 50 | # If the output contains more than one line, it must be caused by symbol loading; try again. 51 | assert len(asbSymbolOutput) == 1, \ 52 | "Invalid symbol output:\n%s" % "\n".join(repr(sbLine) for sbLine in asbSymbolOutput); 53 | # If there is no symbol at the address, cdb will output the address truncated 54 | # to the size of a pointer for the ISA. If we detect this, we return None: 55 | # (See https://github.com/SkyLined/BugId/issues/131) 56 | bOutputLooksLikeANumber = grbAddress.match(asbSymbolOutput[0]); 57 | if bOutputLooksLikeANumber: 58 | uSymbolOutputValue = fu0ValueFromCdbHexOutput(asbSymbolOutput[0]); 59 | uPointerSizeMask = (1 << oProcess.uPointerSizeInBits) - 1 60 | if uAddress & uPointerSizeMask == uSymbolOutputValue: 61 | return None; 62 | oSymbolWithOrWithoutAddressMatch = grbSymbolWithOrWithoutAddress.match(asbSymbolOutput[0]); 63 | assert oSymbolWithOrWithoutAddressMatch, \ 64 | "This should always match!"; # By design - something is very broken if not. 65 | # See if the output contains something we recognize as a symbol; return it. 66 | sbSymbol = oSymbolWithOrWithoutAddressMatch.group(1); 67 | obSymbolOrAddressMatch = rbSymbolOrAddress.match(sbSymbol); 68 | if obSymbolOrAddressMatch: 69 | ( 70 | sb0Address, 71 | sb0UnloadedModuleFileName, sb0UnloadedModuleOffset, 72 | sb0ModuleCdbIdOrAddress, sb0ModuleOffset, 73 | sb0FunctionSymbol, sbPlusOrMinusOffset, sb0OffsetInFunction, 74 | ) = obSymbolOrAddressMatch.groups(); 75 | if sb0Address is None: 76 | return sbSymbol; 77 | raise AssertionError( 78 | "Cannot process get symbol output for address 0x%X:\r\n%s" % ( 79 | uAddress, 80 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbSymbolOutput), 81 | ) 82 | ); 83 | -------------------------------------------------------------------------------- /mProcess/cProcess_ftxSplitSymbolOrAddress.py: -------------------------------------------------------------------------------- 1 | from ..fu0ValueFromCdbHexOutput import fu0ValueFromCdbHexOutput; 2 | from ..rbSymbolOrAddress import rbSymbolOrAddress; 3 | 4 | def cProcess_ftxSplitSymbolOrAddress(oProcess, sbSymbolOrAddress): 5 | obSymbolOrAddressMatch = rbSymbolOrAddress.match(sbSymbolOrAddress); 6 | assert obSymbolOrAddressMatch, \ 7 | "Symbol or address does not match a known format: %s" % repr(sbSymbolOrAddress); 8 | ( 9 | sb0Address, 10 | sb0UnloadedModuleFileName, sb0UnloadedModuleOffset, 11 | sb0ModuleCdbIdOrAddress, sb0ModuleOffset, 12 | sb0FunctionSymbol, sbPlusOrMinusOffset, sb0OffsetInFunction, 13 | ) = obSymbolOrAddressMatch.groups(); 14 | u0Address = None; 15 | o0Module = None; 16 | u0ModuleOffset = fu0ValueFromCdbHexOutput(sb0ModuleOffset); 17 | o0Function = None; 18 | i0OffsetFromStartOfFunction = None; 19 | if sb0Address is not None: 20 | u0Address = fu0ValueFromCdbHexOutput(sb0Address); 21 | elif sb0UnloadedModuleFileName is not None: 22 | # sb0UnloadedModuleFileName is returned without modification 23 | u0ModuleOffset = fu0ValueFromCdbHexOutput(sb0UnloadedModuleOffset); 24 | elif sb0ModuleCdbIdOrAddress == b"SharedUserData": 25 | # "ShareUserData" is a symbol outside of any module that gets used as a module name in cdb. 26 | # Any value referencing it will be converted to an address: 27 | u0Address = oProcess.fu0GetAddressForSymbol(b"%s!%s" % (sb0ModuleCdbIdOrAddress, sb0FunctionSymbol)); 28 | if u0Address and u0ModuleOffset: u0Address += u0ModuleOffset; 29 | else: 30 | sbModuleCdbId = sb0ModuleCdbIdOrAddress; 31 | o0Module = oModule = oProcess.fo0GetModuleForCdbId(sbModuleCdbId); 32 | if sb0FunctionSymbol is not None: 33 | o0Function = oModule.foGetOrCreateFunctionForSymbol(sb0FunctionSymbol); 34 | i0OffsetFromStartOfFunction = ( 35 | 0 if sb0OffsetInFunction is None else 36 | fu0ValueFromCdbHexOutput(sb0OffsetInFunction) * (1 if sbPlusOrMinusOffset == b"+" else -1) 37 | ); 38 | return ( 39 | u0Address, 40 | sb0UnloadedModuleFileName, o0Module, u0ModuleOffset, 41 | o0Function, i0OffsetFromStartOfFunction 42 | ); -------------------------------------------------------------------------------- /mProcess/cProcess_fu0GetAddressForSymbol.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | from ..fu0ValueFromCdbHexOutput import fu0ValueFromCdbHexOutput; 4 | from ..mCP437 import fsCP437FromBytesString; 5 | 6 | grbInvalidSymbolErrors = re.compile(rb"^(Couldn't resolve error at .*|Ambiguous symbol error at '.+')$"); 7 | 8 | def cProcess_fu0GetAddressForSymbol(oSelf, sbSymbol): 9 | assert sbSymbol[0] not in b"0123456789", \ 10 | "You should not pass a number (%s) as a symbol" % repr(sbSymbol)[1:]; 11 | # Get the address of the symbol without the offset: 12 | asbCommandOutput = oSelf.fasbExecuteCdbCommand( 13 | sbCommand = b'.printf "%%p\\n", @!"%s";' % sbSymbol, 14 | sb0Comment = b"Get address for symbol", 15 | ); 16 | if len(asbCommandOutput) == 2 and asbCommandOutput[0].startswith(b"Unable to read dynamic function table entry at "): 17 | # It looks like we can safely ignore this error: the next line should contain the value. 18 | asbCommandOutput.pop(0); 19 | else: 20 | assert len(asbCommandOutput) == 1, \ 21 | "Expected only one line in value result:\r\n%s" % \ 22 | "\r\n".join(fsCP437FromBytesString(sbLine) for sbLine in asbCommandOutput); 23 | if grbInvalidSymbolErrors.match(asbCommandOutput[0]): 24 | return None; 25 | return fu0ValueFromCdbHexOutput(asbCommandOutput[0]); 26 | -------------------------------------------------------------------------------- /mProcess/dttxRelevantRegisters_by_sISA.py: -------------------------------------------------------------------------------- 1 | dttxRelevantRegisters_by_sISA = { 2 | # NAME SIZE SYMBOL 3 | "x86": ( 4 | (b"eip", 32, True), 5 | (b"eax", 32, True), 6 | (b"ebx", 32, True), 7 | (b"ecx", 32, True), 8 | (b"edx", 32, True), 9 | (b"esi", 32, True), 10 | (b"edi", 32, True), 11 | (b"esp", 32, True), 12 | (b"ebp", 32, True), 13 | (b"xmm0", 128, False), 14 | (b"xmm1", 128, False), 15 | (b"xmm2", 128, False), 16 | (b"xmm3", 128, False), 17 | (b"xmm4", 128, False), 18 | (b"xmm5", 128, False), 19 | (b"xmm6", 128, False), 20 | (b"xmm7", 128, False), 21 | ), 22 | "x64": ( 23 | (b"rip", 64, True), 24 | (b"rax", 64, True), 25 | (b"rbx", 64, True), 26 | (b"rcx", 64, True), 27 | (b"rdx", 64, True), 28 | (b"rsi", 64, True), 29 | (b"rdi", 64, True), 30 | (b"rsp", 64, True), 31 | (b"rbp", 64, True), 32 | (b"r8", 64, True), 33 | (b"r9", 64, True), 34 | (b"r10", 64, True), 35 | (b"r11", 64, True), 36 | (b"r12", 64, True), 37 | (b"r13", 64, True), 38 | (b"r14", 64, True), 39 | (b"r15", 64, True), 40 | (b"xmm0", 128, False), 41 | (b"xmm1", 128, False), 42 | (b"xmm2", 128, False), 43 | (b"xmm3", 128, False), 44 | (b"xmm4", 128, False), 45 | (b"xmm5", 128, False), 46 | (b"xmm6", 128, False), 47 | (b"xmm7", 128, False), 48 | (b"xmm8", 128, False), 49 | (b"xmm9", 128, False), 50 | (b"xmm10", 128, False), 51 | (b"xmm11", 128, False), 52 | (b"xmm12", 128, False), 53 | (b"xmm13", 128, False), 54 | (b"xmm14", 128, False), 55 | (b"xmm15", 128, False), 56 | ), 57 | } -------------------------------------------------------------------------------- /mSourceCodeLinks/Chrome.py: -------------------------------------------------------------------------------- 1 | from .cSourceCodeLink import cSourceCodeLink; 2 | 3 | srbBasePath = rb"\w+:(\\\w+)*\\(%s)\\src\\" % rb"|".join([ 4 | rb"build\\slave\\(win-asan|syzygy_official|win_upload_clang)\\build", 5 | rb"win_asan_release", 6 | rb"tmp\w+\\w", # ASan builds... 7 | rb"win(64)?_(pgo|clang)", 8 | 9 | ]); 10 | 11 | aoSourceCodeLinks = [ 12 | cSourceCodeLink( # Blink 13 | srbPathHeader = srbBasePath + rb"third_party\\webkit\\source\\", 14 | sbFileOnlyURLTemplate = b"https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/%(path)s", 15 | sbFileAndLineNumberURLTemplate = b"https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/%(path)s#%(line_number)s", 16 | ), 17 | cSourceCodeLink( # syzygy 18 | srbPathHeader = srbBasePath + rb"syzygy\\", 19 | sbFileOnlyURLTemplate = b"https://github.com/google/syzygy/blob/master/syzygy/%(path)s", 20 | sbFileAndLineNumberURLTemplate = b"https://github.com/google/syzygy/blob/master/syzygy/%(path)s#L%(line_number)s", 21 | ), 22 | cSourceCodeLink( # V8 23 | srbPathHeader = srbBasePath + rb"v8\\", 24 | sbFileOnlyURLTemplate = b"https://chromium.googlesource.com/v8/v8.git/+/master/%(path)s", 25 | sbFileAndLineNumberURLTemplate = b"https://chromium.googlesource.com/v8/v8.git/+/master/%(path)s#%(line_number)s", 26 | ), 27 | cSourceCodeLink( # ASan/LLVM 28 | srbPathHeader = srbBasePath + rb"third_party\\llvm\\projects\\compiler-rt\\", 29 | sbFileOnlyURLTemplate = b"https://github.com/llvm-mirror/compiler-rt/tree/master/%(path)s", 30 | sbFileAndLineNumberURLTemplate = b"https://github.com/llvm-mirror/compiler-rt/tree/master/%(path)s#L%(line_number)s", 31 | ), 32 | # Anything else is assumed to be part of chromium. This is very likely not correct, as there are bound to be more 33 | # exceptions, so please report if you find any! 34 | # It is correct for the following folders: content, ipc, (list is not yet exhaustive). 35 | cSourceCodeLink( 36 | srbPathHeader = srbBasePath, 37 | sbFileOnlyURLTemplate = b"https://chromium.googlesource.com/chromium/src/+/master/%(path)s", 38 | sbFileAndLineNumberURLTemplate = b"https://chromium.googlesource.com/chromium/src/+/master/%(path)s#%(line_number)s", 39 | ), 40 | ]; -------------------------------------------------------------------------------- /mSourceCodeLinks/Firefox.py: -------------------------------------------------------------------------------- 1 | from .cSourceCodeLink import cSourceCodeLink; 2 | 3 | # Available options: 4 | # http://searchfox.org/mozilla-central/source/# 5 | # https://dxr.mozilla.org/mozilla-central/source/# 6 | # On windows, all paths in the symbol files are lowercased. Unfortunately, both of these options do not accept paths 7 | # with incorrect casing, but will give a 404. The only reasonable solution I've found is to search for the file using 8 | # dxr.mozilla.org: this allows you to automatically redirect to the correct file. Unfortunately, I have not found a 9 | # way to pass it a line number as well. 10 | 11 | aoSourceCodeLinks = [ 12 | cSourceCodeLink( # base 13 | srbPathHeader = rb"c:\\builds\\moz2_slave\\m-rel-w32-00000000000000000000\\build\\src\\", 14 | sbFileOnlyURLTemplate = b"https://dxr.mozilla.org/mozilla-central/search?q=path:%(path)s&redirect=true", 15 | ), 16 | ]; -------------------------------------------------------------------------------- /mSourceCodeLinks/__init__.py: -------------------------------------------------------------------------------- 1 | from .fsb0GetSourceCodeLinkURLForPath import fsb0GetSourceCodeLinkURLForPath; 2 | -------------------------------------------------------------------------------- /mSourceCodeLinks/cBugIdTests.py: -------------------------------------------------------------------------------- 1 | from .cSourceCodeLink import cSourceCodeLink; 2 | 3 | aoSourceCodeLinks = [ 4 | cSourceCodeLink( # Blink 5 | srbPathHeader = rb"\w+:(\\\w+)*\\cBugId\\tests\\src\\", 6 | sbFileOnlyURLTemplate = b"https://github.com/SkyLined/cBugId/tree/master/Tests/src/%(path)s", 7 | sbFileAndLineNumberURLTemplate = b"https://github.com/SkyLined/cBugId/tree/master/Tests/src/%(path)s#L%(line_number)s", 8 | ), 9 | ]; -------------------------------------------------------------------------------- /mSourceCodeLinks/cSourceCodeLink.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | class cSourceCodeLink(object): 4 | def __init__(oSelf, srbPathHeader, sbFileOnlyURLTemplate, sbFileAndLineNumberURLTemplate = None): 5 | oSelf.rbPathHeader = re.compile(srbPathHeader, re.I); 6 | oSelf.sbFileOnlyURLTemplate = sbFileOnlyURLTemplate; 7 | oSelf.sbFileAndLineNumberURLTemplate = sbFileAndLineNumberURLTemplate or sbFileOnlyURLTemplate; 8 | 9 | def fsbGetURL(oSelf, sbPath, u0LineNumber): 10 | oMatch = oSelf.rbPathHeader.match(sbPath); 11 | if not oMatch: 12 | return; 13 | sbURLTemplate = oSelf.sbFileOnlyURLTemplate if u0LineNumber is None else oSelf.sbFileAndLineNumberURLTemplate; 14 | return sbURLTemplate % { 15 | b"path": sbPath[oMatch.end():].replace(b"\\", b"/"), 16 | b"line_number": None if u0LineNumber is None else b"%d" % u0LineNumber, 17 | }; 18 | -------------------------------------------------------------------------------- /mSourceCodeLinks/fsb0GetSourceCodeLinkURLForPath.py: -------------------------------------------------------------------------------- 1 | from .cBugIdTests import aoSourceCodeLinks as aoSourceCodeLinks_cBugIdTests; 2 | from .Chrome import aoSourceCodeLinks as aoSourceCodeLinks_Chrome; 3 | from .Firefox import aoSourceCodeLinks as aoSourceCodeLinks_Firefox; 4 | 5 | aoSourceCodeLinks = ( 6 | aoSourceCodeLinks_cBugIdTests 7 | + aoSourceCodeLinks_Chrome 8 | + aoSourceCodeLinks_Firefox 9 | ); 10 | 11 | def fsb0GetSourceCodeLinkURLForPath(sbPath, uLineNumber): 12 | for oSourceCodeLink in aoSourceCodeLinks: 13 | sb0LinkURL = oSourceCodeLink.fsbGetURL(sbPath, uLineNumber); 14 | if sb0LinkURL: 15 | return sb0LinkURL; 16 | return None; 17 | -------------------------------------------------------------------------------- /mStack/__init__.py: -------------------------------------------------------------------------------- 1 | from .cStack import cStack; 2 | 3 | __all__ = [ 4 | "cStack", 5 | ]; -------------------------------------------------------------------------------- /rbSymbolOrAddress.py: -------------------------------------------------------------------------------- 1 | import re; 2 | 3 | rbSymbolOrAddress = re.compile( 4 | rb"\A\s*" # optional whitespace 5 | rb"(?:" # either { 6 | rb"(?:0x)?" # optional { "0x" } 7 | rb"([0-9`a-f]+)" # <<
>> 8 | rb"|" # } or { 9 | rb">> 11 | rb">" # ">" 12 | rb"(?:" # optional{ 13 | rb"\+0x0*" rb"([0-9`a-f]+?)" # "+0x" "0"... <<>> 14 | rb")?" # } 15 | rb"|" # } or { 16 | rb"(\w+)" # <<>> 17 | rb"(?:" # optional either { 18 | rb"\+0x0*" rb"([0-9`a-f]+?)" # "+0x" "0"... <<>> 19 | rb"|" # } or { 20 | rb"!" rb"(.+?)" # "!" <<>> 21 | rb"(?:" # optional { 22 | rb"([\+\-])" rb"0x0*" rb"([0-9`a-f]+?)" # ["+" or "-"] "0x" "0"... <<>> 23 | rb")?" # } 24 | rb")?" # } 25 | rb")" # } 26 | rb"\s*\Z" # optional whitespace 27 | ); 28 | --------------------------------------------------------------------------------