diff --git a/README.md b/README.md index a0dcc18..46922c8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # udmp-parser: A Cross-Platform C++ parser library for Windows user minidumps ![Build status](https://github.com/0vercl0k/udmp-parser/workflows/Builds/badge.svg) +[![Downloads](https://static.pepy.tech/badge/udmp-parser/month)](https://pepy.tech/project/udmp-parser) This is a cross-platform (Windows / Linux / OSX / x86 / x64) C++ library that parses Windows user [minidump](https://docs.microsoft.com/en-us/windows/win32/debug/minidump-files) dumps (`.dump /m` and **not** `.dump /f` in WinDbg usermode). diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3cfba4f..cbf3f80 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,7 @@ project( udmp-parser DESCRIPTION "A Cross-Platform C++ parser library for Windows user minidumps." HOMEPAGE_URL https://github.com/0vercl0k/udmp-parser - VERSION 0.4 + VERSION 0.5.0 ) set(PROJECT_AUTHOR 0vercl0k) diff --git a/src/lib/udmp-parser.h b/src/lib/udmp-parser.h index 21c71a8..6814911 100644 --- a/src/lib/udmp-parser.h +++ b/src/lib/udmp-parser.h @@ -75,9 +75,9 @@ namespace fs = std::filesystem; namespace udmpparser { #ifdef NDEBUG -void DbgPrintf(const char *Format, ...) { (void)Format; } +static void DbgPrintf(const char *Format, ...) { (void)Format; } #else -void DbgPrintf(const char *Format, ...) { +static void DbgPrintf(const char *Format, ...) { va_list ArgList; va_start(ArgList, Format); vfprintf(stderr, Format, ArgList); @@ -226,7 +226,7 @@ struct Context64_t { uint128_t Xmm13; uint128_t Xmm14; uint128_t Xmm15; - uint8_t Padding[0x60]; + std::array Padding; std::array VectorRegister; uint64_t VectorControl; uint64_t DebugControl; diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index d7607bb..4f2fabf 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -24,7 +24,13 @@ list(APPEND CMAKE_PREFIX_PATH "${NB_DIR}") find_package(nanobind CONFIG REQUIRED) -nanobind_add_module(udmp_parser NB_STATIC src/udmp_parser.cc) +set(CMAKE_CXX_STANDARD 20) + +nanobind_add_module(udmp_parser NB_STATIC src/udmp_parser_utils.cc src/udmp_parser.cc) + +if(MSVC) + target_link_libraries(udmp_parser PRIVATE DbgHelp.lib) +endif(MSVC) if(BUILD_PYTHON_PACKAGE) # @@ -32,6 +38,7 @@ if(BUILD_PYTHON_PACKAGE) # target_include_directories(udmp_parser PRIVATE ../lib) install(TARGETS udmp_parser LIBRARY DESTINATION .) + install(DIRECTORY udmp_parser-stubs DESTINATION .) else() # # This is the general case, when built from the root cmakefile diff --git a/src/python/pyproject.toml b/src/python/pyproject.toml index 8188a49..2f7a236 100644 --- a/src/python/pyproject.toml +++ b/src/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build" [project] name = "udmp-parser" -version = "0.4.3" +version = "0.5.0" description = "A Cross-Platform C++ parser library for Windows user minidumps." readme = "README.md" requires-python = ">=3.8" @@ -21,6 +21,9 @@ dependencies = [] [project.urls] Homepage = "https://github.com/0vercl0k/udmp-parser" +[project.scripts] +generate_minidump = "udmp_parser.utils:generate_minidump_from_command_line" + [tool.isort] profile = "black" @@ -29,9 +32,7 @@ wheel.py-api = "cp312" minimum-version = "0.4" build-dir = "build/{wheel_tag}" cmake.minimum-version = "3.20" -cmake.args = [ - "-DBUILD_PYTHON_PACKAGE:BOOL=ON", -] +cmake.args = ["-DBUILD_PYTHON_PACKAGE:BOOL=ON"] [tool.cibuildwheel] build-verbosity = 1 diff --git a/src/python/src/udmp_parser.cc b/src/python/src/udmp_parser.cc index b6a0ad6..90dc340 100644 --- a/src/python/src/udmp_parser.cc +++ b/src/python/src/udmp_parser.cc @@ -4,7 +4,7 @@ // Released under MIT License, by 0vercl0k - 2023 // // With contribution from: -// * hugsy - (github.com / hugsy) +// * hugsy - (github.com/hugsy) // #include "udmp-parser.h" @@ -23,7 +23,12 @@ namespace nb = nanobind; +void udmp_parser_utils_module(nb::module_ &m); + NB_MODULE(udmp_parser, m) { + + udmp_parser_utils_module(m); + nb::enum_(m, "ProcessorArch") .value("X86", udmpparser::ProcessorArch_t::X86) .value("ARM", udmpparser::ProcessorArch_t::ARM) @@ -193,6 +198,14 @@ NB_MODULE(udmp_parser, m) { .def_ro("StreamType", &udmpparser::dmp::Directory_t::StreamType) .def_ro("Location", &udmpparser::dmp::Directory_t::Location); + nb::class_(m, "MemoryInfoListStream") + .def_ro("SizeOfHeader", + &udmpparser::dmp::MemoryInfoListStream_t::SizeOfHeader) + .def_ro("SizeOfEntry", + &udmpparser::dmp::MemoryInfoListStream_t::SizeOfEntry) + .def_ro("NumberOfEntries", + &udmpparser::dmp::MemoryInfoListStream_t::NumberOfEntries); + nb::class_(m, "Memory64ListStreamHdr") .def_ro("NumberOfMemoryRanges", @@ -315,7 +328,7 @@ NB_MODULE(udmp_parser, m) { .def("__repr__", &udmpparser::MemBlock_t::to_string); ; - nb::class_(m, "Modules") + nb::class_(m, "Module") .def(nb::init(), nb::rv_policy::take_ownership) @@ -374,91 +387,4 @@ NB_MODULE(udmp_parser, m) { .def_ro_static("major", &udmpparser::Version::Major) .def_ro_static("minor", &udmpparser::Version::Minor) .def_ro_static("release", &udmpparser::Version::Release); - - auto utils = m.def_submodule("utils", "Helper functions"); - utils.def( - "TypeToString", - [](const uint32_t Type) -> std::string { - switch (Type) { - case 0x2'00'00: { - return "MEM_PRIVATE"; - } - case 0x4'00'00: { - return "MEM_MAPPED"; - } - case 0x1'00'00'00: { - return "MEM_IMAGE"; - } - } - return ""; - }, - "Get a string representation of the memory type"); - - utils.def( - "StateToString", - [](const uint32_t State) { - switch (State) { - case 0x10'00: { - return "MEM_COMMIT"; - } - - case 0x20'00: { - return "MEM_RESERVE"; - } - - case 0x1'00'00: { - return "MEM_FREE"; - } - } - return ""; - }, - "Get a string representation of the memory state"); - - utils.def( - "ProtectionToString", - [](const uint32_t Protection) { - struct { - const char *Name = nullptr; - uint32_t Mask = 0; - } Flags[] = { - {"PAGE_NOACCESS", 0x01}, - {"PAGE_READONLY", 0x02}, - {"PAGE_READWRITE", 0x04}, - {"PAGE_WRITECOPY", 0x08}, - {"PAGE_EXECUTE", 0x10}, - {"PAGE_EXECUTE_READ", 0x20}, - {"PAGE_EXECUTE_READWRITE", 0x40}, - {"PAGE_EXECUTE_WRITECOPY", 0x80}, - {"PAGE_GUARD", 0x100}, - {"PAGE_NOCACHE", 0x200}, - {"PAGE_WRITECOMBINE", 0x400}, - {"PAGE_TARGETS_INVALID", 0x4000'0000}, - }; - std::stringstream ss; - uint32_t KnownFlags = 0; - - for (const auto &Flag : Flags) { - if ((Protection & Flag.Mask) == 0) { - continue; - } - - ss << Flag.Name << ","; - KnownFlags |= Flag.Mask; - } - - const uint32_t MissingFlags = (~KnownFlags) & Protection; - if (MissingFlags) { - ss << std::hex << "0x" << MissingFlags; - } - - std::string ProtectionString = ss.str(); - if (ProtectionString.size() > 1 && - ProtectionString[ProtectionString.size() - 1] == ',') { - ProtectionString = - ProtectionString.substr(0, ProtectionString.size() - 1); - } - - return ProtectionString; - }, - "Get a string representation of the memory protection"); } diff --git a/src/python/src/udmp_parser_utils.cc b/src/python/src/udmp_parser_utils.cc new file mode 100644 index 0000000..27f2e43 --- /dev/null +++ b/src/python/src/udmp_parser_utils.cc @@ -0,0 +1,172 @@ +// +// This file is part of udmp-parser project +// +// Released under MIT License, by 0vercl0k - 2023 +// +// With contribution from: +// * hugsy - (github.com/hugsy) +// + +#include "udmp-parser.h" + +#include +#include +#include +#include +#include + +namespace nb = nanobind; + +#ifdef _WIN32 +#include +#include + +bool GenerateMinidumpFromProcessId( + const uint32_t TargetPid, const std::filesystem::path &MiniDumpFilePath) { + const HANDLE File = + CreateFileA(MiniDumpFilePath.string().c_str(), GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (File == INVALID_HANDLE_VALUE) { + return false; + } + + const HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, false, TargetPid); + if (Process == INVALID_HANDLE_VALUE) { + CloseHandle(File); + return false; + } + + MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo = {}; + const auto Flags = MINIDUMP_TYPE::MiniDumpWithFullMemory | + MINIDUMP_TYPE::MiniDumpWithDataSegs | + MINIDUMP_TYPE::MiniDumpScanMemory | + MINIDUMP_TYPE::MiniDumpWithHandleData | + MINIDUMP_TYPE::MiniDumpWithFullMemoryInfo; + + const auto Success = + MiniDumpWriteDump(Process, TargetPid, File, MINIDUMP_TYPE(Flags), + &ExceptionInfo, nullptr, nullptr); + + CloseHandle(Process); + CloseHandle(File); + return Success; +} +#endif + +void udmp_parser_utils_module(nb::module_ &m) { + auto utils = m.def_submodule("utils", "Helper functions"); + + utils.def( + "TypeToString", + [](const uint32_t Type) -> std::string { + switch (Type) { + case 0x2'00'00: { + return "MEM_PRIVATE"; + } + case 0x4'00'00: { + return "MEM_MAPPED"; + } + case 0x1'00'00'00: { + return "MEM_IMAGE"; + } + } + return ""; + }, + "Get a string representation of the memory type"); + + utils.def( + "StateToString", + [](const uint32_t State) { + switch (State) { + case 0x10'00: { + return "MEM_COMMIT"; + } + + case 0x20'00: { + return "MEM_RESERVE"; + } + + case 0x1'00'00: { + return "MEM_FREE"; + } + } + return ""; + }, + "Get a string representation of the memory state"); + + utils.def( + "ProtectionToString", + [](const uint32_t Protection) { + struct { + const char *Name = nullptr; + uint32_t Mask = 0; + } Flags[] = { + {"PAGE_NOACCESS", 0x01}, + {"PAGE_READONLY", 0x02}, + {"PAGE_READWRITE", 0x04}, + {"PAGE_WRITECOPY", 0x08}, + {"PAGE_EXECUTE", 0x10}, + {"PAGE_EXECUTE_READ", 0x20}, + {"PAGE_EXECUTE_READWRITE", 0x40}, + {"PAGE_EXECUTE_WRITECOPY", 0x80}, + {"PAGE_GUARD", 0x100}, + {"PAGE_NOCACHE", 0x200}, + {"PAGE_WRITECOMBINE", 0x400}, + {"PAGE_TARGETS_INVALID", 0x4000'0000}, + }; + std::stringstream ss; + uint32_t KnownFlags = 0; + + for (const auto &Flag : Flags) { + if ((Protection & Flag.Mask) == 0) { + continue; + } + + ss << Flag.Name << ","; + KnownFlags |= Flag.Mask; + } + + const uint32_t MissingFlags = (~KnownFlags) & Protection; + if (MissingFlags) { + ss << std::hex << "0x" << MissingFlags; + } + + std::string ProtectionString = ss.str(); + if (ProtectionString.size() > 1 && + ProtectionString[ProtectionString.size() - 1] == ',') { + ProtectionString = + ProtectionString.substr(0, ProtectionString.size() - 1); + } + + return ProtectionString; + }, + "Get a string representation of the memory protection"); + +#if defined(_WIN32) + utils.def("generate_minidump", GenerateMinidumpFromProcessId, "TargetPid", + "MiniDumpFilePath", + "Generate a minidump for TargetPid and save it to the given path. " + "Returns true on success."); + + utils.def( + "generate_minidump_from_command_line", + []() -> bool { + nb::module_ sys = nb::module_::import_("sys"); + nb::list argv = sys.attr("argv"); + if (!argv.is_valid()) { + return false; + } + + if (argv.size() != 3) { + return false; + } + + auto a1 = nb::str(nb::handle(argv[1])); + const auto TargetPid = uint32_t(std::atol(a1.c_str())); + auto a2 = nb::str(nb::handle(argv[2])); + return GenerateMinidumpFromProcessId(TargetPid, a2.c_str()); + }, + "Generate a minidump for the target TargetPid, write it to the given " + "path. Returns true on success."); +#endif // _WIN32 +} diff --git a/src/python/tests/utils.py b/src/python/tests/utils.py index 6ac47ed..6b19fd2 100644 --- a/src/python/tests/utils.py +++ b/src/python/tests/utils.py @@ -13,6 +13,8 @@ import time from typing import Optional, Tuple +import udmp_parser + def get_process_id(process_name: str): kernel32 = ctypes.WinDLL("kernel32") @@ -63,104 +65,6 @@ class PROCESSENTRY32(ctypes.Structure): return res -def generate_minidump(process_id: int, dump_file_path: pathlib.Path) -> bool: - kernel32 = ctypes.WinDLL("kernel32") - dbghelp = ctypes.WinDLL("dbghelp") - - # Constants - INVALID_HANDLE_VALUE = -1 - CREATE_ALWAYS = 2 - PROCESS_ALL_ACCESS = 0x1F0FFF - GENERIC_WRITE = 0x40000000 - FILE_ATTRIBUTE_NORMAL = 0x80 - - MiniDumpNormal = 0x00000000 - MiniDumpWithDataSegs = 0x00000001 - MiniDumpWithFullMemory = 0x00000002 - MiniDumpWithHandleData = 0x00000004 - MiniDumpScanMemory = 0x00000010 - MiniDumpWithFullMemoryInfo = 0x00000800 - - class MINIDUMP_EXCEPTION_INFORMATION(ctypes.Structure): - _fields_ = [ - ("ThreadId", ctypes.c_ulong), - ("ExceptionPointers", ctypes.POINTER(ctypes.c_void_p)), - ("ClientPointers", ctypes.c_ulong), - ] - - class MINIDUMP_CALLBACK_INFORMATION(ctypes.Structure): - _fields_ = [ - ("CallbackRoutine", ctypes.c_void_p), - ("CallbackParam", ctypes.c_void_p), - ] - - class MINIDUMP_USER_STREAM(ctypes.Structure): - _fields_ = [ - ("Type", ctypes.c_ulong), - ("BufferSize", ctypes.c_ulong), - ("Buffer", ctypes.POINTER(ctypes.c_void_p)), - ] - - class MINIDUMP_USER_STREAM_INFORMATION(ctypes.Structure): - _fields_ = [ - ("UserStreamCount", ctypes.c_ulong), - ("UserStreamArray", ctypes.POINTER(MINIDUMP_USER_STREAM)), - ("Reserved0", ctypes.c_ulong), - ("Reserved1", ctypes.c_void_p), - ] - - MiniDumpWriteDump = dbghelp.MiniDumpWriteDump - MiniDumpWriteDump.argtypes = [ - ctypes.c_void_p, - ctypes.c_ulong, - ctypes.c_void_p, - ctypes.c_ulong, - ctypes.POINTER(MINIDUMP_EXCEPTION_INFORMATION), - ctypes.POINTER(MINIDUMP_USER_STREAM_INFORMATION), - ctypes.POINTER(MINIDUMP_CALLBACK_INFORMATION), - ] - MiniDumpWriteDump.restype = ctypes.c_bool - - hProcess = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, process_id) - - if not hProcess: - return False - - bSuccess = False - hFile = kernel32.CreateFileW( - str(dump_file_path.absolute()), - GENERIC_WRITE, - 0, - None, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - None, - ) - - if hFile != INVALID_HANDLE_VALUE: - flags = ( - MiniDumpWithFullMemory - | MiniDumpWithDataSegs - | MiniDumpScanMemory - | MiniDumpWithHandleData - | MiniDumpWithFullMemoryInfo - ) - bSuccess = MiniDumpWriteDump( - hProcess, - process_id, - hFile, - flags, - None, - None, - None, - ) - - kernel32.CloseHandle(hFile) - - kernel32.CloseHandle(hProcess) - return bSuccess - - def generate_minidump_from_process_name( process_name: str = "explorer.exe", output_dir: pathlib.Path = pathlib.Path(".") ) -> Optional[Tuple[int, pathlib.Path]]: @@ -170,7 +74,7 @@ def generate_minidump_from_process_name( dump_file_path = output_dir / f"minidump-{process_name}-{int(time.time())}.dmp" - if not generate_minidump(process_id, dump_file_path): + if not udmp_parser.utils.generate_minidump(process_id, dump_file_path): return None print(f"Minidump generated successfully: PID={process_id} -> {dump_file_path}") diff --git a/src/python/udmp_parser-stubs/__init__.pyi b/src/python/udmp_parser-stubs/__init__.pyi new file mode 100644 index 0000000..a9a780a --- /dev/null +++ b/src/python/udmp_parser-stubs/__init__.pyi @@ -0,0 +1,404 @@ +import os +from typing import Union, Optional, overload +from enum import Enum, IntEnum +import udmp_parser + + +class ProcessorArch(IntEnum): + X86 = 0 + ARM = 5 + IA64 = 6 + AMD64 = 9 + Unknown = 0xFFFF + + +class Arch(IntEnum): + X86 = 0 + X64 = 1 + + +kWOW64_SIZE_OF_80387_REGISTERS: int = 80 + + +class FloatingSaveArea32: + ControlWord: int + StatusWord: int + TagWord: int + ErrorOffset: int + ErrorSelector: int + DataOffset: int + DataSelector: int + RegisterArea: bytearray # size =kWOW64_SIZE_OF_80387_REGISTERS + Cr0NpxState: int + + +class Context32: + ContextFlags: int + Dr0: int + Dr1: int + Dr2: int + Dr3: int + Dr6: int + Dr7: int + FloatSave: FloatingSaveArea32 + SegGs: int + SegFs: int + SegEs: int + SegDs: int + Edi: int + Esi: int + Ebx: int + Edx: int + Ecx: int + Eax: int + Ebp: int + Eip: int + SegCs: int + EFlags: int + Esp: int + SegSs: int + ExtendedRegisters: bytearray # size =kWOW64_MAXIMUM_SUPPORTED_EXTENSION + + +class uint128_t: + Low: int + High: int + + +class Context64: + P1Home: int + P2Home: int + P3Home: int + P4Home: int + P5Home: int + P6Home: int + ContextFlags: int + MxCsr: int + SegCs: int + SegDs: int + SegEs: int + SegFs: int + SegGs: int + SegSs: int + EFlags: int + Dr0: int + Dr1: int + Dr2: int + Dr3: int + Dr6: int + Dr7: int + Rax: int + Rcx: int + Rdx: int + Rbx: int + Rsp: int + Rbp: int + Rsi: int + Rdi: int + R8: int + R9: int + R10: int + R11: int + R12: int + R13: int + R14: int + R15: int + Rip: int + ControlWord: int + StatusWord: int + TagWord: int + Reserved1: int + ErrorOpcode: int + ErrorOffset: int + ErrorSelector: int + Reserved2: int + DataOffset: int + DataSelector: int + Reserved3: int + MxCsr2: int + MxCsr_Mask: int + FloatRegisters: list[uint128_t] # size =8 + Xmm0: uint128_t + Xmm1: uint128_t + Xmm2: uint128_t + Xmm3: uint128_t + Xmm4: uint128_t + Xmm5: uint128_t + Xmm6: uint128_t + Xmm7: uint128_t + Xmm8: uint128_t + Xmm9: uint128_t + Xmm10: uint128_t + Xmm11: uint128_t + Xmm12: uint128_t + Xmm13: uint128_t + Xmm14: uint128_t + Xmm15: uint128_t + Padding: bytearray # size =0x60 + VectorRegister: list[uint128_t] # size =26 + VectorControl: int + DebugControl: int + LastBranchToRip: int + LastBranchFromRip: int + LastExceptionToRip: int + LastExceptionFromRip: int + + +class Directory: + StreamType: StreamType = StreamType.Unused + LocationDescriptor32_t: int + + +class FileMap: + def InBounds(self, arg0: int, arg1: int, /) -> bool: ... + def MapFile(self, arg: str, /) -> bool: ... + def ViewBase(self) -> int: ... + def __init__(self) -> None: ... + + +class FixedFileInfo: + Signature: int = 0 + StrucVersion: int = 0 + FileVersionMS: int = 0 + FileVersionLS: int = 0 + ProductVersionMS: int = 0 + ProductVersionLS: int = 0 + FileFlagsMask: int = 0 + FileFlags: int = 0 + FileOS: int = 0 + FileType: int = 0 + FileSubtype: int = 0 + FileDateMS: int = 0 + FileDateLS: int = 0 + + +class Header: + Signature: int + Version: int + ImplementationVersion: int + NumberOfStreams: int + StreamDirectoryRva: int + CheckSum: int + Reserved: int + TimeDateStamp: int + Flags: int + ExpectedSignature: int + ValidFlagsMask: int + + def LooksGood(self) -> bool: ... + + def __init__(*args, **kwargs): + """ + Initialize self. See help(type(self)) for accurate signature. + """ + ... + + +class LocationDescriptor32: + DataSize: int + Rva: int + + def __init__(*args, **kwargs): + """ + Initialize self. See help(type(self)) for accurate signature. + """ + ... + + +class LocationDescriptor64: + DataSize: int + Rva: int + + def __init__(*args, **kwargs): + """ + Initialize self. See help(type(self)) for accurate signature. + """ + ... + + +class MemBlock: + BaseAddress: int + AllocationBase: int + AllocationProtect: int + RegionSize: int + State: int + Protect: int + Type: int + Data: int + DataSize: int + + def __init__(self, arg: udmp_parser.MemoryInfo, /) -> None: ... + def __str__(self) -> str: ... + + +class Memory64ListStreamHdr: + StartOfMemoryRange: int + DataSize: int + + +class MemoryDescriptor: + ThreadId: int + SuspendCount: int + PriorityClass: int + Priority: int + Teb: int + Stack: MemoryDescriptor + ThreadContext: LocationDescriptor32 + + +class ThreadEntry: + ThreadId: int + SuspendCount: int + PriorityClass: int + Priority: int + Teb: int + Stack: MemoryDescriptor + ThreadContext: LocationDescriptor32 + + +class Thread_t: + ThreadId: int + SuspendCount: int + PriorityClass: int + Priority: int + Teb: int + Context: Union[UnknownContext, Context32, Context64] + + +class MemoryDescriptor64: + StartOfMemoryRange: int + DataSize: int + + +class MemoryInfoListStream: + SizeOfHeader: int + SizeOfEntry: int + NumberOfEntries: int + + +class MemoryInfo: + BaseAddress: int + AllocationBase: int + AllocationProtect: int + __alignment1: int + RegionSize: int + State: int + Protect: int + Type: int + __alignment2: int + + +class Module: + BaseAddress: int + AllocationBase: int + AllocationProtect: int + RegionSize: int + State: int + Protect: int + Type: int + DataSize: int + + @property + def Data(self) -> int: ... + def __str__(self) -> str: ... + + +class StreamType(IntEnum): + Unused = 0 + ThreadList = 3 + ModuleList = 4 + Exception = 6 + SystemInfo = 7 + Memory64List = 9 + MemoryInfoList = 16 + + +class SystemInfoStream: + ProcessorArchitecture: ProcessorArch + ProcessorLevel: int + ProcessorRevision: int + NumberOfProcessors: int + ProductType: int + MajorVersion: int + MinorVersion: int + BuildNumber: int + PlatformId: int + CSDVersionRva: int + SuiteMask: int + Reserved2: int + + +class ExceptionRecord: + ExceptionCode: int + ExceptionFlags: int + ExceptionRecord: int + ExceptionAddress: int + NumberParameters: int + __unusedAlignment: int + ExceptionInformation: list[int] # size=kEXCEPTION_MAXIMUM_PARAMETERS + + +class ExceptionStream: + ThreadId: int + __alignment: int + ExceptionRecord: ExceptionRecord + ThreadContext: LocationDescriptor32 + + +class UnknownContext: + def __init__(*args, **kwargs): + """ + Initialize self. See help(type(self)) for accurate signature. + """ + ... + + +class UserDumpParser: + def ForegroundThreadId(self) -> Optional[int]: ... + + def GetMemoryBlock(self, arg: int, /) -> udmp_parser.MemBlock: + """ + Access a specific MemoryBlock + """ + ... + + def Memory(self) -> dict[int, udmp_parser.MemBlock]: ... + + def Modules(self) -> dict[int, udmp_parser.Modules]: + """ + Get the minidump modules + """ + ... + + def Parse(self, arg: os.PathLike, /) -> bool: + """ + Parse the minidump given in argument. + """ + ... + + def ReadMemory(self, arg0: int, arg1: int, /) -> Optional[list[int]]: + """ + Read bytes from memory + """ + ... + + def Threads(self) -> dict[int, udmp_parser.Thread]: + """ + Get the minidump threads + """ + ... + + def __init__(self) -> None: ... + + +class version: + def __init__(*args, **kwargs): + """ + Initialize self. See help(type(self)) for accurate signature. + """ + ... + major: int + + minor: int + + release: str diff --git a/src/python/udmp_parser-stubs/utils.pyi b/src/python/udmp_parser-stubs/utils.pyi new file mode 100644 index 0000000..401fad4 --- /dev/null +++ b/src/python/udmp_parser-stubs/utils.pyi @@ -0,0 +1,36 @@ +import os + + +def ProtectionToString(arg: int, /) -> str: + """ + Get a string representation of the memory protection + """ + ... + + +def StateToString(arg: int, /) -> str: + """ + Get a string representation of the memory state + """ + ... + + +def TypeToString(arg: int, /) -> str: + """ + Get a string representation of the memory type + """ + ... + + +def generate_minidump(TargetPid: int, MiniDumpFilePath: os.PathLike) -> bool: + """ + Generate a minidump for TargetPid and save it to the given path. Returns true on success. + """ + ... + + +def generate_minidump_from_command_line() -> bool: + """ + Generate a minidump for the target TargetPid, write it to the given path. Returns true on success. + """ + ...