mirror of
https://github.com/ivellioscolin/pykd.git
synced 2025-05-08 01:53:22 +08:00
150 lines
5.2 KiB
Markdown
150 lines
5.2 KiB
Markdown
## Introduction
|
||
|
||
### Step 1: Getting Started
|
||
It is best to use the automatic installer for installation. It will install pykd in the correct location and also install and register all necessary components.
|
||
|
||
If the installation completes without errors, it is time to get acquainted with pykd. To do this, start **WinDbg** and begin a debugging session (open a process, dump file, or establish a connection with a kernel debugger). Now, you can load pykd by running the following command:
|
||
|
||
```
|
||
.load pykd.pyd
|
||
```
|
||
|
||
If any errors occur during loading, WinDbg will display a message. The absence of any messages indicates that the extension has been successfully loaded.
|
||
|
||
Now, you can start working. Execute the following command:
|
||
|
||
```
|
||
!pycmd
|
||
```
|
||
After running this command, the debugger will enter user input mode. All user input will be processed by the Python interpreter.
|
||
|
||
```
|
||
0:000> !pycmd
|
||
Python 2.6.6 (r266:84297, Aug 24 2010, 18:13:38) [MSC v.1500 64 bit (AMD64)] on win32
|
||
Type "help", "copyright", "credits" or "license" for more information.
|
||
(InteractiveConsole)
|
||
>>> print "Hello world!"
|
||
Hello world!
|
||
>>>
|
||
```
|
||
|
||
Now is a good time to get familiar with Python syntax if you haven’t already. Python is very easy to learn.
|
||
|
||
Let's recall some basic syntax of Python:
|
||
|
||
```
|
||
>>> def printHello():
|
||
... i = 0
|
||
... while i < 4:
|
||
... print "Hello #%d" % i
|
||
... i += 1
|
||
...
|
||
>>> printHello()
|
||
Hello #0
|
||
Hello #1
|
||
Hello #2
|
||
Hello #3
|
||
>>>
|
||
```
|
||
|
||
Note: The indentation of blocks is determined by leading spaces, which is a "signature" feature of Python. For now, this knowledge will be sufficient. Let’s move on.
|
||
|
||
### Step 2: Accessing Registers
|
||
|
||
Any debugger should provide three basic capabilities: reading processor registers, reading memory, and controlling debugging mode. Let’s start with registers. With **pykd**, this is quite simple:
|
||
|
||
```
|
||
>>> print hex(reg("eip"))
|
||
0x778ecb60
|
||
>>> print hex(reg("esp"))
|
||
0x1ef0e0
|
||
>>> print hex(reg("esp")+4)
|
||
0x1ef0e4
|
||
```
|
||
|
||
In this case, we use the **PYKD** function `reg`. It reads processor registers by name. A curious reader might ask: how do we use functions from **PYKD** without explicitly importing the module? In reality, the module must be imported. However, **PYKD** automatically does this when constructing the Python console.
|
||
|
||
Let's write a small example to check where the current instruction pointer is pointing:
|
||
|
||
```
|
||
>>> print findSymbol(reg("eip"))
|
||
ntdll!LdrpDoDebuggerBreak+30
|
||
```
|
||
|
||
The `findSymbol` function attempts to find a debug symbol for a given address. In this case, we see that the instruction pointer is at offset `0x30` within the function `LdrpDoDebuggerBreak`, which is located in the **ntdll** module. We can determine this because we have debugging information for **ntdll.dll** (the corresponding **pdb** file). If symbols are not displaying for some reason, check your symbol path settings in **WinDbg**.
|
||
|
||
### Step 3: Accessing Memory
|
||
|
||
**PYKD** provides a large set of functions for accessing memory, which can be divided into three categories:
|
||
|
||
#### 1. Reading values from memory:
|
||
- `ptrByte`
|
||
- `ptrWord`
|
||
- `ptrDWord`
|
||
- `ptrQWord`
|
||
|
||
There are other functions as well; the full set can be found in the **PYKD 0.2 API Reference**. All functions take an address as a parameter and return the value stored at that address.
|
||
|
||
#### 2. Reading arrays
|
||
- `loadBytes`
|
||
- `loadWords`
|
||
- `loadDWords`
|
||
- `loadQWords`
|
||
|
||
All functions take a pointer to the beginning of an array and its length in elements as parameters. They return a **list** object containing the array elements.
|
||
|
||
#### 3. Reading strings
|
||
- `loadCStr`
|
||
- `loadWStr`
|
||
|
||
These functions read **null-terminated** strings from memory and return Python strings.
|
||
|
||
Let’s modify the previous example to display function arguments. We assume that the function follows the **stdcall** calling convention and that its parameters are addressed by the **ebp** register.
|
||
|
||
```
|
||
>>> def printFunc():
|
||
... print findSymbol( reg("eip") )
|
||
... params = [ ptrDWord( reg("ebp") + 4*(i+1) ) for i in range(0,3) ]
|
||
... print "var1: %x var2: %x var3: %x" % ( params[0], params[1], params[2] )
|
||
...
|
||
>>> print printFunc()
|
||
ntdll32!LdrpDoDebuggerBreak+2c
|
||
var1: 774b1383 var2: fffdd000 var3: fffde000
|
||
None
|
||
>>>
|
||
```
|
||
|
||
Pay attention to this construct:
|
||
|
||
```
|
||
params = [ ptrDWord( reg("ebp") + 4*(i+1) ) for i in range(0,3) ]
|
||
```
|
||
|
||
This is a **list comprehension**—a special Python construct that simplifies list initialization. It is equivalent to:
|
||
|
||
```
|
||
[ ptrDWord( reg("ebp") + 4) ), ptrDWord( reg("ebp") + 8) ), ptrDWord( reg("ebp") + 0xC) ) ]
|
||
```
|
||
|
||
### Step 4: Accessing Memory with Type Information
|
||
|
||
When debugging programs, we often work with **typed variables**. **PYKD** has powerful capabilities for accessing variables while preserving type information. This is one of the key features of the project: accessing struct and class fields in a way similar to source code.
|
||
|
||
For example, consider the following **C** code:
|
||
|
||
```
|
||
struct STRUCT_A {
|
||
int field1;
|
||
char field2;
|
||
};
|
||
|
||
STRUCT_A a = { 100, 2 }
|
||
```
|
||
|
||
Now, during debugging, we want to check the state of variable `a` using **PYKD**:
|
||
|
||
```
|
||
a = typedVar( "module!STRUCT_A", getOffset("module!a") )
|
||
if a.field1 != 100 or a.field2 != 2:
|
||
print "ERROR! a is not properly initialized!"
|
||
``` |