mirror of
https://github.com/ivellioscolin/pykd.git
synced 2025-04-21 04:13:22 +08:00
1215 lines
32 KiB
Markdown
1215 lines
32 KiB
Markdown
![]() |
# Table of Contents
|
|||
|
* [1. Introduction](#intro)
|
|||
|
* [1.1 General Information](#commoninfo)
|
|||
|
* [1.2 Quick Start](#gettingStarted)
|
|||
|
* [1.3 Building from Source](#Building)
|
|||
|
* [1.4 Manual Installation](#Installing)
|
|||
|
* [1.5 API Changes](#APIchange)
|
|||
|
* [2. Windbg Commands](#windbg)
|
|||
|
* [2.1 Loading the Plugin](#loadplugin)
|
|||
|
* [2.2 Running a Script](#runscript)
|
|||
|
* [2.3 Console Mode](#console)
|
|||
|
* [3. Debugging Management](#debugging)
|
|||
|
* [3.1 Pausing and Resuming Debugging](#break)
|
|||
|
* [3.2 Step-by-Step Execution](#step)
|
|||
|
* [3.3 Debugging from Python Applications](#consoledebug)
|
|||
|
* [3.4 Printing Debug Information](#dbgprint)
|
|||
|
* [3.5 Executing Debugger Commands](#dbgcommand)
|
|||
|
* [3.6 Creating a Crash Dump](#createcrash)
|
|||
|
* [4. Working with Memory and Registers](#memory)
|
|||
|
* [4.1 Accessing General-Purpose Registers](#reg)
|
|||
|
* [4.2 Accessing Model-Specific Registers](#msr)
|
|||
|
* [4.3 Virtual Address Normalization](#addr64)
|
|||
|
* [4.4 Direct Memory Access](#memaccess)
|
|||
|
* [4.5 Memory Access Errors](#memerror)
|
|||
|
* [4.6 Reading Strings from Memory](#memstr)
|
|||
|
* [5. Modules](#modules)
|
|||
|
* [5.1 Module Class](#moduleclass)
|
|||
|
* [5.2 Module Load and Unload Events](#moduleload)
|
|||
|
* [6. Retrieving Symbolic Information](#syminfo)
|
|||
|
* [6.1 Symbol Files (PDB)](#pdbfile)
|
|||
|
* [6.2 Type Information](#types)
|
|||
|
* [7. Typed Variables](#typedVar)
|
|||
|
* [7.1 TypedVar Class](#typedVar)
|
|||
|
* [7.2 Creating an Instance of TypedVar](#typedVarClass)
|
|||
|
* [7.3 TypedVar Class Methods](#typedVarMethod)
|
|||
|
* [7.4 Classes and Structures](#typedVarUDT)
|
|||
|
* [7.5 Arrays and Pointers](#typedVarArray)
|
|||
|
* [7.6 Enumerations](#typedVarEnum)
|
|||
|
* [7.7 Casting to Other Types](#typedVarCast)
|
|||
|
* [8. Processes and Threads](#ProcessThreads)
|
|||
|
* [8.1 User-Mode Threads](#UserModeThreads)
|
|||
|
* [8.2 Kernel-Mode Threads](#KernelModeThreads)
|
|||
|
* [8.3 Kernel-Mode Processes](#KernelModeProcess)
|
|||
|
* [9. Local Variables](#Locals)
|
|||
|
* [9.1 Current Local Variables](#CurrentLocals)
|
|||
|
* [10. Breakpoints](#breakpoints)
|
|||
|
* [10.1 Setting Breakpoints](#setBreakpoints)
|
|||
|
* [10.2 Conditional Breakpoints](#condBreakpoints)
|
|||
|
* [11. Debugging Events](#eventHandler)
|
|||
|
* [12. Disasm Class](#disasm)
|
|||
|
* [API Reference | PYKD 0.2 API Reference]
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 1. Introduction
|
|||
|
|
|||
|
### 1.1 General Information
|
|||
|
The **pykd** project started in 2010. The main motivation for its development was the inconvenience of built-in tools for writing debugging scripts for **WinDbg**. The **Python** language was chosen as an alternative scripting engine for several reasons:
|
|||
|
- Ease of learning the language
|
|||
|
- A large standard library
|
|||
|
- A powerful and convenient framework for creating extension modules
|
|||
|
|
|||
|
**pykd** is a module for the **CPython** interpreter. It is written in **C++** and uses **Boost.Python** to export functions and classes to Python. **pykd** provides access to debugging management on the Windows platform via the **Debug Engine** library and retrieves symbolic information via the **MS DIA** library.
|
|||
|
|
|||
|
It is important to note that **pykd** does not provide direct access to the **COM interfaces** of **Debug Engine** and **MS DIA**. Instead, it implements its own interface, making the development process faster and more convenient (at least, that is the hope).
|
|||
|
|
|||
|
**pykd** can work in two modes:
|
|||
|
1. **As a plugin for WinDbg**, providing commands for running scripts within a debugging session.
|
|||
|
2. **As a standalone module for the Python interpreter**, which is useful for creating automated tools to analyze crash dumps, for example.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 1.2 Quick Start
|
|||
|
For a quick start, it is best to download the **automatic installer**. It will install all necessary components, including **Python** (if it is not already installed).
|
|||
|
|
|||
|
To verify the installation, launch **WinDbg** and start debugging an application or analyzing a dump. Then, load **pykd**:
|
|||
|
```cmd
|
|||
|
.load pykd.pyd
|
|||
|
```
|
|||
|
If no error messages appear, the installation was successful. To double-check that everything works, try running the Python interactive console within **WinDbg**:
|
|||
|
```cmd
|
|||
|
>!pycmd
|
|||
|
Python 2.6.5 (r265:79096, Mar 19 2010, 18:02:59) [MSC v.1500 64 bit (AMD64)] on win32
|
|||
|
Type "help", "copyright", "credits" or "license" for more information.
|
|||
|
(InteractiveConsole)
|
|||
|
>>> print("Hello world!")
|
|||
|
Hello world!
|
|||
|
>>> quit()
|
|||
|
>
|
|||
|
```
|
|||
|
Try running example scripts:
|
|||
|
```cmd
|
|||
|
!py help
|
|||
|
!py samples
|
|||
|
```
|
|||
|
If everything works, you can proceed to writing your own scripts.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 1.3 Building from Source
|
|||
|
|
|||
|
#### Getting the Source Code
|
|||
|
Download the source code from the [repository](http://pykd.codeplex.com/SourceControl/list/changesets).
|
|||
|
|
|||
|
#### Installing Python
|
|||
|
Download the required version from [here](http://www.python.org).
|
|||
|
|
|||
|
#### Installing and Configuring Boost
|
|||
|
Boost can be obtained from [this link](http://www.boost.org). Installation and build instructions are also available there.
|
|||
|
|
|||
|
#### Setting Up Environment Variables
|
|||
|
The following environment variables need to be set for compilation:
|
|||
|
- `$(DIA_SDK_ROOT)` – Path to the **MS DIA** library. It should look like:
|
|||
|
```
|
|||
|
C:\Program Files (x86)\Microsoft Visual Studio 9.0\DIA SDK
|
|||
|
```
|
|||
|
The **MS DIA** library is installed with Visual Studio.
|
|||
|
- `$(DBG_SDK_ROOT)` – Path to the **Debug Engine SDK**:
|
|||
|
```
|
|||
|
C:\Program Files (x86)\Debugging Tools for Windows (x86)\sdk
|
|||
|
```
|
|||
|
The **Debug Engine SDK** is included in **Debugging Tools for Windows** (now part of **Platform SDK**).
|
|||
|
- `$(BOOST_ROOT)` – Path to the **Boost** installation directory.
|
|||
|
- `$(PYTHON_ROOT)` – Path to the **Python** installation directory. The expected directory structure:
|
|||
|
```
|
|||
|
C:\Python26\x86\...
|
|||
|
C:\Python26\x64\...
|
|||
|
```
|
|||
|
If Python installation does not differentiate between x86 and x64, the project file may need modification.
|
|||
|
|
|||
|
#### Building Boost.Python Libraries
|
|||
|
To compile the required **Boost.Python** static libraries, use the following commands:
|
|||
|
```cmd
|
|||
|
bjam --stagedir=stage --with-python stage
|
|||
|
bjam address-model=64 --stagedir=stage64 --with-python stage
|
|||
|
```
|
|||
|
If **bjam** is not installed, download it from [here](http://www.boost.org/users/download/boost_jam_3_1_18.html).
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 1.4 Manual Installation
|
|||
|
|
|||
|
#### Installing pykd.pyd and Required Dependencies
|
|||
|
To install manually, you will need:
|
|||
|
- `pykd.pyd`
|
|||
|
- **C++ Runtime Redistributable** (`vcredist`) matching the version used to build `pykd.pyd`.
|
|||
|
|
|||
|
#### Where to Copy pykd.pyd?
|
|||
|
The location depends on how you intend to use `pykd`:
|
|||
|
1. **As a WinDbg plugin** – Copy `pykd.pyd` to the `winext` directory inside the **WinDbg installation folder**. You may rename it to `pykd.dll` so it can be loaded without specifying an extension:
|
|||
|
```cmd
|
|||
|
kd>.load pykd
|
|||
|
```
|
|||
|
2. **For Python scripting** – Place `pykd.pyd` in a directory where Python can find it. Possible options:
|
|||
|
- Inside Python's `Lib` subdirectory.
|
|||
|
- Any custom directory, added to the `PYTHONPATH` environment variable.
|
|||
|
- Any directory, without modifying `PYTHONPATH`, but always launching Python from that directory.
|
|||
|
|
|||
|
#### Installing vcredist
|
|||
|
Make sure to install the appropriate **C++ Runtime Redistributable (vcredist)**.
|
|||
|
|
|||
|
#### Registering MS DIA
|
|||
|
The **MS DIA** library must be registered after installation. Find the `msdia90.dll` file and execute:
|
|||
|
```cmd
|
|||
|
regsvr32 msdia90.dll
|
|||
|
```
|
|||
|
If you built the module yourself using **Visual Studio**, then **vcredist** is already installed, and **MS DIA** should be registered automatically.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 1.5 API Changes
|
|||
|
|
|||
|
#### `loadModule` Function Removal
|
|||
|
The `loadModule` function has been removed. Use the `module` class constructor instead:
|
|||
|
```python
|
|||
|
# Old way (no longer valid)
|
|||
|
mod = loadModule("mymodule")
|
|||
|
|
|||
|
# New way
|
|||
|
mod = module("mymodule")
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 2. Windbg Commands
|
|||
|
|
|||
|
### 2.1 Loading the Plugin
|
|||
|
To load the **pykd** plugin in **WinDbg**, run:
|
|||
|
```cmd
|
|||
|
kd>.load pykd_path/pykd.pyd
|
|||
|
```
|
|||
|
If `pykd.pyd` is inside the **winext** folder (a subdirectory of **Debugging Tools for Windows**), the path is not needed:
|
|||
|
```cmd
|
|||
|
kd>.load pykd.pyd
|
|||
|
```
|
|||
|
If `pykd.pyd` is renamed to `pykd.dll`, the extension can be omitted:
|
|||
|
```cmd
|
|||
|
kd>.load pykd
|
|||
|
```
|
|||
|
To view loaded extensions in **WinDbg**, use:
|
|||
|
```cmd
|
|||
|
kd>.chain
|
|||
|
```
|
|||
|
To unload the plugin:
|
|||
|
```cmd
|
|||
|
kd>.unload pykd_path/pykd.pyd
|
|||
|
kd>.unload pykd.pyd
|
|||
|
kd>.unload pykd
|
|||
|
```
|
|||
|
To avoid manually loading **pykd** every session, load it once and then save the workspace:
|
|||
|
```cmd
|
|||
|
kd> Save Workspace
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 2.2 Running a Script
|
|||
|
To run a **Python script** using **pykd**, use the `!py` command:
|
|||
|
```cmd
|
|||
|
kd>!py script_path/script_name.py param1 param2 ...
|
|||
|
```
|
|||
|
The `.py` extension can be omitted.
|
|||
|
To avoid specifying full paths, add the script directory to `PYTHONPATH` or modify the Windows registry:
|
|||
|
```
|
|||
|
HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.6\PythonPath
|
|||
|
```
|
|||
|
In Python, command-line parameters are available in `sys.argv`:
|
|||
|
```python
|
|||
|
import sys
|
|||
|
print("Script path: " + sys.argv[0])
|
|||
|
print("param1: " + sys.argv[1])
|
|||
|
print("param2: " + sys.argv[2])
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 2.3 Console Mode
|
|||
|
To launch an interactive **Python console** within **WinDbg**, use:
|
|||
|
```cmd
|
|||
|
kd>!pycmd
|
|||
|
```
|
|||
|
Example session:
|
|||
|
```cmd
|
|||
|
1: kd> !pycmd
|
|||
|
Python 2.6.5 (r265:79096, Mar 19 2010, 18:02:59) [MSC v.1500 64 bit (AMD64)] on win32
|
|||
|
Type "help", "copyright", "credits" or "license" for more information.
|
|||
|
(InteractiveConsole)
|
|||
|
>>>
|
|||
|
```
|
|||
|
The `pykd` module is automatically imported, so you can start using **pykd functions immediately**.
|
|||
|
To **exit the Python console**, use `quit()`. The state of the interpreter is preserved:
|
|||
|
```cmd
|
|||
|
>>> a = 10
|
|||
|
>>> quit()
|
|||
|
1: kd> !pycmd
|
|||
|
Python 2.6.5 (r265:79096, Mar 19 2010, 18:02:59) [MSC v.1500 64 bit (AMD64)] on win32
|
|||
|
Type "help", "copyright", "credits" or "license" for more information.
|
|||
|
(InteractiveConsole)
|
|||
|
>>> print(a)
|
|||
|
10
|
|||
|
>>>
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 3. Debugging Management
|
|||
|
|
|||
|
### 3.1 Pausing and Resuming Debugging
|
|||
|
|
|||
|
In **WinDbg**, you can pause and resume debugging using:
|
|||
|
- **Break (Ctrl + Break)**
|
|||
|
- **Go (F5)**
|
|||
|
|
|||
|
Equivalent **pykd** functions:
|
|||
|
- `go()` – Resumes execution and returns control when the debugger stops.
|
|||
|
- `breakin()` – Forces a break into the debugger.
|
|||
|
|
|||
|
#### Example: Continuous Debugging Loop
|
|||
|
```python
|
|||
|
try:
|
|||
|
while True:
|
|||
|
go()
|
|||
|
print("Break detected")
|
|||
|
except:
|
|||
|
print("Process terminated")
|
|||
|
```
|
|||
|
⚠ **Warning:** `breakin()` is rarely needed. Since scripts usually execute only while the debugger is paused, calling `breakin()` inside a script has no effect.
|
|||
|
To stop debugging from a script, create a separate thread for `breakin()`.
|
|||
|
|
|||
|
⚠ **Do NOT use `breakin()`, `go()`, or `trace()` inside event handlers (e.g., conditional breakpoints).**
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 3.2 Step-by-Step Execution
|
|||
|
|
|||
|
For step debugging (tracing), use:
|
|||
|
- `step()` – Equivalent to **Step Into** in WinDbg.
|
|||
|
- `trace()` – Equivalent to **Step Over** in WinDbg.
|
|||
|
|
|||
|
Both functions may raise a `DbgException` if the debugged process has exited.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 3.3 Debugging from Python Applications
|
|||
|
|
|||
|
If you want to run scripts **outside WinDbg**, you must first create a debugging session.
|
|||
|
If only one session is used, it is **created automatically** with these functions:
|
|||
|
|
|||
|
#### Creating a Debugging Session
|
|||
|
- `loadDump(dumpName)` – Loads a crash dump.
|
|||
|
- `startProcess(imageName)` – Starts a new process in debugging mode.
|
|||
|
- `attachProcess(processId)` – Attaches the debugger to an existing process.
|
|||
|
- `attachKernel(parameterStr)` – Attaches the debugger to a kernel debugging session.
|
|||
|
|
|||
|
#### Detaching or Terminating the Debugged Process
|
|||
|
- `detachProcess(id)` – Detaches the debugger from a process.
|
|||
|
- `killProcess(id)` – Stops debugging and terminates the process.
|
|||
|
|
|||
|
#### Checking Debugger Mode
|
|||
|
To determine if the debugger is analyzing a **memory dump**:
|
|||
|
```python
|
|||
|
if isDumpAnalyzing():
|
|||
|
print("Debugger is analyzing a memory dump.")
|
|||
|
```
|
|||
|
To check if debugging is in **kernel mode**:
|
|||
|
```python
|
|||
|
if isKernelDebugging():
|
|||
|
print("Kernel debugging mode detected.")
|
|||
|
```
|
|||
|
This is useful when scripts depend on **kernel symbols** and should only run in kernel mode.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 3.4 Printing Debug Information
|
|||
|
|
|||
|
Instead of using `print()`, **pykd** provides specialized functions:
|
|||
|
- `dprint(message, dml=False)` – Prints a message without a newline.
|
|||
|
- `dprintln(message, dml=False)` – Prints a message **with a newline**.
|
|||
|
|
|||
|
The `dml` parameter enables **DML (Debug Markup Language)** formatting, which works in **WinDbg only**.
|
|||
|
|
|||
|
#### Example: Using DML for Clickable Output
|
|||
|
```python
|
|||
|
dprintln("<b><u>Click here to reload all symbols:</u></b>", True)
|
|||
|
dprintln("<link cmd=\".reload /f\">Reload Symbols</link>", True)
|
|||
|
```
|
|||
|
This displays a **clickable command** inside WinDbg.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 3.5 Executing Debugger Commands
|
|||
|
|
|||
|
To run **WinDbg commands** from Python, use:
|
|||
|
```python
|
|||
|
dbgCommand("!analyze -v")
|
|||
|
```
|
|||
|
Example:
|
|||
|
```python
|
|||
|
output = dbgCommand("!analyze -v")
|
|||
|
dprint(output)
|
|||
|
```
|
|||
|
|
|||
|
To evaluate an **expression** (similar to the `?` command in WinDbg):
|
|||
|
```python
|
|||
|
expr("@rax + 10")
|
|||
|
```
|
|||
|
|
|||
|
#### Loading & Calling **WinDbg Extensions**
|
|||
|
```python
|
|||
|
extHandle = loadExt("ext_path") # Load an extension
|
|||
|
result = callExt(extHandle, "command", "params") # Call an extension command
|
|||
|
removeExt(extHandle) # Unload extension
|
|||
|
```
|
|||
|
⚠ **Important:** The `ext` class was removed in **pykd 0.2**. Use `loadExt()` instead.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 3.6 Creating a Crash Dump
|
|||
|
|
|||
|
You can save the system state as a **crash dump** for later analysis.
|
|||
|
|
|||
|
#### Creating a Dump File
|
|||
|
```python
|
|||
|
writeDump("C:\\dump\\fulldump.dmp", False) # Full memory dump
|
|||
|
writeDump("C:\\dump\\minidump.dmp", True) # Minidump (smaller)
|
|||
|
```
|
|||
|
The second parameter specifies the **dump type**:
|
|||
|
- `False` = **Full dump** (contains all memory pages).
|
|||
|
- `True` = **Minidump** (smaller, excludes unnecessary memory pages).
|
|||
|
|
|||
|
Crash dumps can be created in **both user-mode and kernel-mode debugging**.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 4. Working with Memory and Registers
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 4.1 Accessing General-Purpose Registers
|
|||
|
|
|||
|
To access **general-purpose registers (GPRs)**, use:
|
|||
|
```python
|
|||
|
* cpuReg reg( regName ) # Access register by name
|
|||
|
* cpuReg reg( regIndex ) # Access register by index
|
|||
|
```
|
|||
|
#### Example: Accessing a Register
|
|||
|
```python
|
|||
|
r = reg("eax")
|
|||
|
print(r / 10 * 234)
|
|||
|
```
|
|||
|
|
|||
|
#### Exception Handling
|
|||
|
Both versions of `reg()` return an instance of the **`cpuReg` class**.
|
|||
|
If the register information **cannot be retrieved**, a `BaseException` is raised.
|
|||
|
|
|||
|
#### `cpuReg` Class Methods
|
|||
|
The `cpuReg` class provides the following methods:
|
|||
|
- `name()` – Returns the register name.
|
|||
|
- `index()` – Returns the register index.
|
|||
|
|
|||
|
#### Arithmetic Operations with Registers
|
|||
|
The `cpuReg` class **supports arithmetic operations directly**, without type conversion:
|
|||
|
```python
|
|||
|
r = reg("eax")
|
|||
|
print(r / 10 * 234)
|
|||
|
```
|
|||
|
|
|||
|
#### Enumerating All Registers
|
|||
|
```python
|
|||
|
import pykd
|
|||
|
|
|||
|
try:
|
|||
|
i = 0
|
|||
|
while True:
|
|||
|
r = pykd.reg(i)
|
|||
|
pykd.dprintln(f"{r.name()} {hex(r)} ({r})")
|
|||
|
i += 1
|
|||
|
except pykd.BaseException:
|
|||
|
pass
|
|||
|
```
|
|||
|
|
|||
|
⚠ **Important:** `pykd` currently supports **only integer registers**.
|
|||
|
FPU, MMX, and SSE registers are **not supported** (planned for future versions).
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 4.2 Accessing Model-Specific Registers (MSR)
|
|||
|
|
|||
|
Use `rdmsr(msrNumber)` to read **MSR registers**:
|
|||
|
```python
|
|||
|
print(findSymbol(rdmsr(0x176)))
|
|||
|
```
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 4.3 Virtual Address Normalization
|
|||
|
|
|||
|
All **pykd** functions return **normalized** 64-bit virtual addresses. On **32-bit** platforms, addresses are **sign-extended** to 64 bits:
|
|||
|
|
|||
|
| Original Address | Normalized Address |
|
|||
|
|-----------------|-------------------|
|
|||
|
| `0x00100000` | `0x0000000000100000` |
|
|||
|
| `0x80100000` | `0xFFFFFFFF80100000` |
|
|||
|
|
|||
|
For comparison, use `addr64()`:
|
|||
|
```python
|
|||
|
import pykd
|
|||
|
nt = pykd.module("nt")
|
|||
|
|
|||
|
if nt > addr64(0x80000000):
|
|||
|
print("NT module is in higher address space")
|
|||
|
```
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 4.4 Direct Memory Access
|
|||
|
|
|||
|
To read **unsigned integers** from memory:
|
|||
|
```python
|
|||
|
ptrByte(va)
|
|||
|
ptrWord(va)
|
|||
|
ptrDWord(va)
|
|||
|
ptrQWord(va)
|
|||
|
```
|
|||
|
For **signed integers**:
|
|||
|
```python
|
|||
|
ptrSignByte(va)
|
|||
|
ptrSignWord(va)
|
|||
|
ptrSignDWord(va)
|
|||
|
ptrSignQWord(va)
|
|||
|
```
|
|||
|
For **architecture-independent** reads:
|
|||
|
```python
|
|||
|
ptrMWord(va)
|
|||
|
ptrSignMWord(va)
|
|||
|
ptrPtr(va)
|
|||
|
```
|
|||
|
To read a block of memory:
|
|||
|
```python
|
|||
|
loadBytes(va, count)
|
|||
|
loadWords(va, count)
|
|||
|
loadDWords(va, count)
|
|||
|
loadQWords(va, count)
|
|||
|
```
|
|||
|
Returns a **list**.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 4.5 Memory Access Errors
|
|||
|
|
|||
|
If a memory access fails, `MemoryException` is raised:
|
|||
|
```python
|
|||
|
try:
|
|||
|
a = ptrByte(0)
|
|||
|
except MemoryException:
|
|||
|
print("Memory exception occurred")
|
|||
|
```
|
|||
|
To check if an address is valid:
|
|||
|
```python
|
|||
|
isValid(va)
|
|||
|
```
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 4.6 Reading Strings from Memory
|
|||
|
|
|||
|
Instead of using `loadBytes()`, **pykd** provides:
|
|||
|
```python
|
|||
|
loadChars(va, count) # Returns ASCII string
|
|||
|
loadWChars(va, count) # Returns Unicode string
|
|||
|
```
|
|||
|
For **null-terminated strings**:
|
|||
|
```python
|
|||
|
loadCStr(va)
|
|||
|
loadWStr(va)
|
|||
|
```
|
|||
|
⚠ **Maximum string length is 64 KB**. Longer strings will raise `MemoryException`.
|
|||
|
|
|||
|
For Windows kernel **UNICODE_STRING** and **ANSI_STRING** structures:
|
|||
|
```python
|
|||
|
loadAnsiString(va)
|
|||
|
loadUnicodeString(va)
|
|||
|
```
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 5. Modules
|
|||
|
|
|||
|
### 5.1 Module Class
|
|||
|
|
|||
|
A **module** is an executable file mapped into memory. A typical program consists of a main module (usually a `.exe` file) and a set of libraries.
|
|||
|
|
|||
|
#### Creating a Module Instance
|
|||
|
|
|||
|
The `module` class has two constructor forms:
|
|||
|
```python
|
|||
|
module(moduleName)
|
|||
|
module(va)
|
|||
|
```
|
|||
|
- The first form creates a module object by name.
|
|||
|
- The second form creates a module object by a virtual address belonging to the module.
|
|||
|
|
|||
|
If the module is not found, a `BaseException` is raised.
|
|||
|
|
|||
|
Example:
|
|||
|
```python
|
|||
|
from pykd import *
|
|||
|
|
|||
|
try:
|
|||
|
ntdll = module("ntdll")
|
|||
|
print(ntdll.name(), hex(ntdll.begin()), hex(ntdll.size()))
|
|||
|
except BaseException:
|
|||
|
print("Module not found")
|
|||
|
```
|
|||
|
|
|||
|
#### Retrieving Module Information
|
|||
|
|
|||
|
Use these methods of the `module` class:
|
|||
|
- `name()` – Returns the module's name.
|
|||
|
- `image()` – Returns the module's executable file name.
|
|||
|
- `pdb()` – Returns the full path to the PDB (symbol) file.
|
|||
|
- `begin()` – Returns the module's base virtual address.
|
|||
|
- `end()` – Returns the module's end virtual address.
|
|||
|
- `checksum()` – Returns the module's checksum.
|
|||
|
- `timestamp()` – Returns the module's timestamp.
|
|||
|
- `getVersion()` – Returns the module's version as a tuple, e.g., `(1, 0, 6452, 0)`.
|
|||
|
- `queryVersion(valueName)` – Retrieves a specific version resource value.
|
|||
|
|
|||
|
#### Loading and Accessing Symbols
|
|||
|
|
|||
|
To load symbol information, use:
|
|||
|
```python
|
|||
|
mod.reload()
|
|||
|
```
|
|||
|
To find a symbol's virtual address:
|
|||
|
```python
|
|||
|
addr = mod.offset("symbolName")
|
|||
|
```
|
|||
|
If the symbol is not found, a `BaseException` is raised.
|
|||
|
|
|||
|
Instead of explicitly calling `offset()`, you can use attributes:
|
|||
|
```python
|
|||
|
nt = module("nt")
|
|||
|
print(hex(nt.offset("PsLoadedModuleList")))
|
|||
|
print(hex(nt.PsLoadedModuleList)) # Shortcut
|
|||
|
```
|
|||
|
To retrieve the **Relative Virtual Address (RVA)** of a symbol:
|
|||
|
```python
|
|||
|
rva = mod.rva("symbolName")
|
|||
|
```
|
|||
|
|
|||
|
#### Module Type Information
|
|||
|
|
|||
|
To retrieve type information, use:
|
|||
|
```python
|
|||
|
t = mod.type("_MDL")
|
|||
|
print(t)
|
|||
|
```
|
|||
|
Example output:
|
|||
|
```
|
|||
|
struct/class: _MDL Size: 0x1c (28)
|
|||
|
+0000 Next : _MDL*
|
|||
|
+0004 Size : Int2B
|
|||
|
+0006 MdlFlags : Int2B
|
|||
|
+0008 Process : _EPROCESS*
|
|||
|
+000c MappedSystemVa : Void*
|
|||
|
+0010 StartVa : Void*
|
|||
|
+0014 ByteCount : ULong
|
|||
|
+0018 ByteOffset : ULong
|
|||
|
```
|
|||
|
|
|||
|
#### Typed Variables
|
|||
|
|
|||
|
`pykd` provides the `typedVar` class for working with structured data. You can obtain an instance using:
|
|||
|
```python
|
|||
|
mod.typedVar(va)
|
|||
|
mod.typedVar(symbolName)
|
|||
|
mod.typedVar(typeName, va)
|
|||
|
```
|
|||
|
Example:
|
|||
|
```python
|
|||
|
nt = module("nt")
|
|||
|
print(nt.typedVar("_LIST_ENTRY", nt.PsLoadedModuleList))
|
|||
|
```
|
|||
|
Output:
|
|||
|
```
|
|||
|
struct/class: _LIST_ENTRY at 0xfffff8000369c650
|
|||
|
+0000 Flink : _LIST_ENTRY* 0xfffffa8003c64890
|
|||
|
+0008 Blink : _LIST_ENTRY* 0xfffffa80092f8f30
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 5.2 Handling Module Load and Unload Events
|
|||
|
|
|||
|
To handle module load/unload events, subclass `eventHandler`:
|
|||
|
- `onLoadModule` – Called when a module is loaded.
|
|||
|
- `onUnloadModule` – Called when a module is unloaded.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 6. Retrieving Symbolic Information
|
|||
|
|
|||
|
### 6.1 Symbol Files (PDB)
|
|||
|
|
|||
|
A **PDB (Program Database) file** contains debugging symbols. Depending on compiler settings, it may include:
|
|||
|
- Global variables and constants.
|
|||
|
- Function and method names with parameters.
|
|||
|
- User-defined types (structs, classes, enums).
|
|||
|
- Constant values.
|
|||
|
- Local variables.
|
|||
|
|
|||
|
**pykd** uses the **MS DIA library** to work with symbols and provides a custom interface for direct access.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 6.2 Type Information
|
|||
|
|
|||
|
#### `typeInfo` Class
|
|||
|
|
|||
|
The `typeInfo` class represents type information, including:
|
|||
|
- **Structures**
|
|||
|
- **Classes**
|
|||
|
- **Unions**
|
|||
|
- **Enumerations**
|
|||
|
- **Bit fields**
|
|||
|
- **Pointers**
|
|||
|
- **Basic types**
|
|||
|
|
|||
|
Methods:
|
|||
|
```python
|
|||
|
name() # Returns type name
|
|||
|
size() # Returns type size
|
|||
|
staticOffset() # Returns static field offset
|
|||
|
fieldOffset() # Returns field offset
|
|||
|
bitOffset() # Returns bit field offset
|
|||
|
bitWidth() # Returns bit field width
|
|||
|
field() # Retrieves a field
|
|||
|
asMap() # Retrieves enum name-value mapping
|
|||
|
deref() # Dereferences a pointer
|
|||
|
ptrTo() # Creates a pointer to a type
|
|||
|
arrayOf() # Creates an array of the type
|
|||
|
append() # Adds a field (for manually created structs)
|
|||
|
```
|
|||
|
|
|||
|
#### Getting a `typeInfo` Object
|
|||
|
|
|||
|
Use the constructor with a type name:
|
|||
|
```python
|
|||
|
t = typeInfo("ntdll!_UNICODE_STRING")
|
|||
|
print(t)
|
|||
|
```
|
|||
|
Example output:
|
|||
|
```
|
|||
|
class/struct : _UNICODE_STRING Size: 0x10 (16)
|
|||
|
+0000 Length : UInt2B
|
|||
|
+0002 MaximumLength : UInt2B
|
|||
|
+0008 Buffer : UInt2B*
|
|||
|
```
|
|||
|
To retrieve all types in a module:
|
|||
|
```python
|
|||
|
nt = module("ntdll")
|
|||
|
for typeName in nt.enumTypes():
|
|||
|
print(typeName)
|
|||
|
```
|
|||
|
|
|||
|
#### Creating Custom Types
|
|||
|
|
|||
|
If a required type is missing, use `typeBuilder`:
|
|||
|
```python
|
|||
|
tb = typeBuilder()
|
|||
|
us = tb.createStruct("_UNICODE_STRING")
|
|||
|
us.append("Length", tb.UInt2B)
|
|||
|
us.append("MaximumLength", tb.UInt2B)
|
|||
|
us.append("Buffer", tb.WChar.ptrTo())
|
|||
|
print(us)
|
|||
|
```
|
|||
|
Output:
|
|||
|
```
|
|||
|
class/struct : _UNICODE_STRING Size: 0x10 (16)
|
|||
|
+0000 Length : UInt2B
|
|||
|
+0002 MaximumLength : UInt2B
|
|||
|
+0008 Buffer : WChar*
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table]
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 7. Typed Variables
|
|||
|
|
|||
|
### 7.1 `typedVar` Class
|
|||
|
|
|||
|
The `typedVar` class simplifies working with complex structures:
|
|||
|
|
|||
|
```python
|
|||
|
t1 = typedVar("MyModule!MyVar")
|
|||
|
t2 = typedVar("MyModule!MyType", addr)
|
|||
|
ti = typeInfo("MyModule!MyType")
|
|||
|
t3 = typedVar(ti, addr)
|
|||
|
```
|
|||
|
If the variable or type does not exist, `SymbolException` is raised.
|
|||
|
|
|||
|
Example:
|
|||
|
```python
|
|||
|
try:
|
|||
|
typedVar("MyModule!NonExistentVar")
|
|||
|
except SymbolException:
|
|||
|
print("Variable does not exist")
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 7.2 `typedVar` Class Methods
|
|||
|
|
|||
|
- `getAddress()` – Returns variable address.
|
|||
|
- `sizeof()` – Returns variable size.
|
|||
|
- `offset()` – Returns field offset within the parent structure.
|
|||
|
- `field(fieldName)` – Retrieves a structure field.
|
|||
|
- `deref()` – Dereferences a pointer.
|
|||
|
- `type()` – Retrieves variable type.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 7.3 `typedVar` Class Methods
|
|||
|
|
|||
|
The `typedVar` class provides methods to access and manipulate structured data.
|
|||
|
|
|||
|
#### Retrieving Variable Properties
|
|||
|
- `getAddress()` – Returns the memory address of the variable.
|
|||
|
- `sizeof()` – Returns the size of the variable in bytes.
|
|||
|
- `offset()` – If the variable is a structure field, returns the **offset** within its parent.
|
|||
|
|
|||
|
#### Accessing Structure Fields
|
|||
|
- `field(fieldName)` – Returns the value of a specific field as a `typedVar` object.
|
|||
|
|
|||
|
Example:
|
|||
|
```python
|
|||
|
tv = typedVar("structVar")
|
|||
|
print(tv.field("fieldName"))
|
|||
|
```
|
|||
|
Alternatively, access fields as **attributes**:
|
|||
|
```python
|
|||
|
print(tv.fieldName)
|
|||
|
```
|
|||
|
Both methods return the same result.
|
|||
|
|
|||
|
#### Dereferencing Pointers
|
|||
|
- `deref()` – Returns the value pointed to by a `typedVar` pointer.
|
|||
|
|
|||
|
Example:
|
|||
|
```python
|
|||
|
ptr = typedVar("ptrStruct")
|
|||
|
dereferenced_value = ptr.deref()
|
|||
|
```
|
|||
|
|
|||
|
#### Retrieving Type Information
|
|||
|
- `type()` – Returns the `typeInfo` object for the variable.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 7.4 Classes and Structures
|
|||
|
|
|||
|
**Enumerating Structure Fields**
|
|||
|
|
|||
|
To loop through all fields of a structure:
|
|||
|
```python
|
|||
|
tv = typedVar("structVar")
|
|||
|
for fieldName, fieldValue in tv:
|
|||
|
print(fieldName, fieldValue)
|
|||
|
```
|
|||
|
This returns a **tuple** containing the field name and value.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 7.5 Arrays and Pointers
|
|||
|
|
|||
|
`typedVar` supports **arrays** and **multidimensional arrays**.
|
|||
|
|
|||
|
#### Accessing Array Elements
|
|||
|
Use the `[]` operator:
|
|||
|
```python
|
|||
|
arr = typedVar("intArray")
|
|||
|
print(arr[0]) # First element
|
|||
|
print(arr[2]) # Third element
|
|||
|
```
|
|||
|
|
|||
|
#### Accessing Multidimensional Arrays
|
|||
|
```python
|
|||
|
matrix = typedVar("intMatrix")
|
|||
|
print(matrix[1][2]) # Element at row 1, column 2
|
|||
|
```
|
|||
|
|
|||
|
#### Working with Pointers
|
|||
|
Use `deref()` to get the value stored at a pointer’s address:
|
|||
|
```python
|
|||
|
ptr = typedVar("ptrIntArray")
|
|||
|
print(ptr.deref()[1]) # Access second element through pointer
|
|||
|
```
|
|||
|
|
|||
|
⚠ **Important:** `typedVar` **does not follow C pointer arithmetic rules**. Pointer arithmetic treats addresses as raw numbers.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 7.6 Enumerations
|
|||
|
|
|||
|
Use `typeInfo.asMap()` to get an enumeration’s **name-value mapping**.
|
|||
|
|
|||
|
Example:
|
|||
|
```python
|
|||
|
var = typedVar("myStruct")
|
|||
|
enum_type = var.structType.type().asMap()
|
|||
|
print(enum_type[var.structType]) # Get enum name from value
|
|||
|
```
|
|||
|
|
|||
|
#### Using Enums in Conditional Statements
|
|||
|
```python
|
|||
|
if var.structType == var.structType.type().TYPE_ONE:
|
|||
|
print("TYPE_ONE detected")
|
|||
|
else:
|
|||
|
print("ANOTHER_TYPE")
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 7.7 Casting to Other Types
|
|||
|
|
|||
|
#### Converting `typedVar` to String
|
|||
|
```python
|
|||
|
print(str(typedVar("g_struct")))
|
|||
|
```
|
|||
|
Example output:
|
|||
|
```
|
|||
|
struct/class: struct3 at 0x13f4391f8
|
|||
|
+0000 m_arrayField : Int4B[2]
|
|||
|
+0008 m_noArrayField : Int4B 0x3 (3)
|
|||
|
```
|
|||
|
|
|||
|
#### Converting `typedVar` to Integer
|
|||
|
Use `int()` or `long()`. The conversion depends on the variable type:
|
|||
|
| **Type** | **Conversion Result** |
|
|||
|
|--------------|---------------------|
|
|||
|
| **Basic Types** | Direct value |
|
|||
|
| **Structures** | Pointer to the structure |
|
|||
|
| **Enums** | Numeric value |
|
|||
|
| **Pointers** | Address stored in pointer |
|
|||
|
| **Arrays** | Address of first element |
|
|||
|
|
|||
|
Example:
|
|||
|
```python
|
|||
|
var = typedVar("g_struct")
|
|||
|
print(int(var.m_noArrayField)) # Integer conversion
|
|||
|
print(hex(int(var.m_arrayField))) # Get array address
|
|||
|
```
|
|||
|
Output:
|
|||
|
```
|
|||
|
3
|
|||
|
0x13f4391f8
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 8. Processes and Threads
|
|||
|
|
|||
|
### 8.1 User-Mode Threads
|
|||
|
|
|||
|
In user mode, the debugger operates in the context of the debugged process. If the process has multiple threads, you can switch contexts.
|
|||
|
|
|||
|
**Key concepts:**
|
|||
|
- **Current thread** – The thread that will continue execution after resuming debugging.
|
|||
|
- **Implicit thread** – The thread the debugger is currently analyzing.
|
|||
|
|
|||
|
#### Switching the Debugger's Thread Context
|
|||
|
|
|||
|
To change the thread context, use:
|
|||
|
```python
|
|||
|
setImplicitThread(teb_address)
|
|||
|
```
|
|||
|
To get the current implicit thread:
|
|||
|
```python
|
|||
|
getImplicitThread()
|
|||
|
```
|
|||
|
To get a list of all process threads:
|
|||
|
```python
|
|||
|
getProcessThreads()
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 8.2 Kernel-Mode Threads
|
|||
|
|
|||
|
In kernel mode:
|
|||
|
- `setImplicitThread()` and `getImplicitThread()` use **ETHREAD** instead of TEB.
|
|||
|
- `getProcessThreads()` is **not available**.
|
|||
|
|
|||
|
To enumerate process threads, use the `_EPROCESS` structure:
|
|||
|
```python
|
|||
|
nt = module("nt")
|
|||
|
process = nt.typedVar("_EPROCESS", processAddr)
|
|||
|
threadList = nt.typedVarList(process.ThreadListHead, "_ETHREAD", "ThreadListEntry")
|
|||
|
```
|
|||
|
⚠ **Switching threads does not switch the process context.**
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 8.3 Kernel-Mode Processes
|
|||
|
|
|||
|
In kernel debugging, you can work with **system processes** and **driver execution contexts**.
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 9. Local Variables
|
|||
|
|
|||
|
### 9.1 Retrieving Local Variables
|
|||
|
|
|||
|
If debug symbols contain local variable information, you can access them without manually handling registers and stack values.
|
|||
|
|
|||
|
Use:
|
|||
|
```python
|
|||
|
locals_dict = getLocals()
|
|||
|
```
|
|||
|
Example:
|
|||
|
```python
|
|||
|
# Retrieve a specific local variable
|
|||
|
print(getLocals()["argc"])
|
|||
|
|
|||
|
# Print all local variables
|
|||
|
for varName, varValue in getLocals().items():
|
|||
|
print(varName, varValue)
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 10. Breakpoints
|
|||
|
|
|||
|
### 10.1 Setting Breakpoints
|
|||
|
|
|||
|
Use `setBp()` to set breakpoints.
|
|||
|
It returns an **ID** that can be used to remove the breakpoint with `removeBp()`.
|
|||
|
|
|||
|
**Setting a software breakpoint:**
|
|||
|
```python
|
|||
|
nt = module("nt")
|
|||
|
bpid = setBp(nt.NtCreateFile)
|
|||
|
```
|
|||
|
**Setting a hardware breakpoint:**
|
|||
|
```python
|
|||
|
bpid = setBp(nt.NtCreateFile, 1, 4)
|
|||
|
```
|
|||
|
- The second parameter is the **memory access size**.
|
|||
|
- The third parameter is the **access type**:
|
|||
|
- `1` – Read
|
|||
|
- `2` – Write
|
|||
|
- `4` – Execute
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 10.2 Conditional Breakpoints
|
|||
|
|
|||
|
Breakpoints can have **conditions** using callback functions.
|
|||
|
|
|||
|
Example:
|
|||
|
```python
|
|||
|
import fnmatch
|
|||
|
from pykd import *
|
|||
|
|
|||
|
nt = module('nt')
|
|||
|
objAttrType = nt.type("_OBJECT_ATTRIBUTES")
|
|||
|
|
|||
|
def onCreateFile(id):
|
|||
|
objattr = typedVar(objAttrType, ptrPtr(reg('esp') + 0xC))
|
|||
|
return fnmatch.fnmatch(loadUnicodeString(objattr.ObjectName), '*.exe')
|
|||
|
|
|||
|
setBp(nt.NtCreateFile, onCreateFile)
|
|||
|
```
|
|||
|
In this case, the breakpoint triggers only when an executable file (`*.exe`) is accessed.
|
|||
|
|
|||
|
You can also use **lambda functions** for conditions:
|
|||
|
```python
|
|||
|
setBp(myAddr, lambda id: reg('rax') > 0x1000)
|
|||
|
```
|
|||
|
|
|||
|
### **Handling Breakpoint Persistence**
|
|||
|
|
|||
|
If a script sets a breakpoint but then exits, the breakpoint **will be removed**.
|
|||
|
|
|||
|
To persist it, use:
|
|||
|
1. **Script-controlled execution:**
|
|||
|
```python
|
|||
|
setBp(nt.NtCreateFile, onCreateFile)
|
|||
|
go()
|
|||
|
```
|
|||
|
This waits for the breakpoint to trigger before exiting.
|
|||
|
|
|||
|
2. **Using `!pycmd`**
|
|||
|
Run the script inside `!pycmd`, so it remains active even after execution:
|
|||
|
```cmd
|
|||
|
>!pycmd
|
|||
|
>>> import setmybreak
|
|||
|
>>> quit()
|
|||
|
>g
|
|||
|
```
|
|||
|
The breakpoint stays active even after exiting the Python console.
|
|||
|
|
|||
|
⚠ **Restricted functions inside breakpoints:**
|
|||
|
Do **not** use the following inside breakpoint handlers:
|
|||
|
- `go()`, `breakin()`, `trace()`
|
|||
|
- `startProcess()`, `killProcess()`, `openDump()`
|
|||
|
- `setCurrentProcess()`, `setImplicitThread()`
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 11. Debugging Events
|
|||
|
|
|||
|
### 11.1 Handling Breakpoints (`onBreakpoint`)
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 11.2 Handling Exceptions (`onException`)
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 11.3 Handling Module Load Events (`onLoadModule`)
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### 11.4 Handling Module Unload Events (`onUnloadModule`)
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## 12. Disassembly (`disasm` Class)
|
|||
|
|
|||
|
The `disasm` class is a wrapper over the **WinDbg** disassembler. It provides equivalent results to the `u` command.
|
|||
|
|
|||
|
### 12.1 Creating a Disassembler
|
|||
|
|
|||
|
```python
|
|||
|
d = disasm() # Start from current instruction
|
|||
|
d = disasm(offset) # Start from a specific address
|
|||
|
```
|
|||
|
|
|||
|
### 12.2 Disassembling Instructions
|
|||
|
|
|||
|
```python
|
|||
|
instr = d.disasm() # Disassemble next instruction
|
|||
|
instr = d.disasm(offset) # Disassemble from a specific offset
|
|||
|
```
|
|||
|
|
|||
|
### 12.3 Assembling Instructions
|
|||
|
|
|||
|
```python
|
|||
|
d.asm("mov eax, 1") # Modify machine code
|
|||
|
```
|
|||
|
|
|||
|
### 12.4 Getting Current Instruction Info
|
|||
|
|
|||
|
```python
|
|||
|
print(d.current()) # Current address
|
|||
|
print(d.instruction()) # Instruction mnemonic
|
|||
|
print(d.length()) # Instruction size
|
|||
|
```
|
|||
|
|
|||
|
### 12.5 Getting Effective Address
|
|||
|
|
|||
|
```python
|
|||
|
print(d.ea()) # Effective address of last instruction operand
|
|||
|
```
|
|||
|
|
|||
|
### 12.6 Resetting Disassembler
|
|||
|
|
|||
|
```python
|
|||
|
d.reset() # Restart from initial address
|
|||
|
```
|
|||
|
|
|||
|
[Back to Table of Contents](#table)
|
|||
|
|
|||
|
---
|