Python構(gòu)建Windows調(diào)試器

一、Windows API

1誓焦、進(jìn)程

創(chuàng)建進(jìn)程
參考文檔:CreateProcess

注意:本章內(nèi)容所有使用的函數(shù)都是Windows的內(nèi)核函數(shù)對(duì)編碼要求比較嚴(yán)格,因?yàn)閜ython使用的是kernel32.dll動(dòng)態(tài)鏈接庫里導(dǎo)出的函數(shù)涯雅,在kernel32.dll里沒有CreateProcess()這個(gè)函數(shù)外构,只有CreateProcessA()跟CreateProcessW()。

/***ANSI版本***/
BOOL WINAPI CreateProcessA(
     LPCSTR lpApplicationName,
     LPSTR lpCommandLine,
     LPSECURITY_ATTRIBUTES lpProcessAttributes,
     LPSECURITY_ATTRIBUTES lpThreadAttributes,
     BOOL blnheritHandles,
     DWORD dwCreationFlags,
     LPVOID lpEnvironment,
     LPCSTR lpCurrentDirectory,
     LPSTARTUPINFOA lpStartupInfo,    //ANSI版本
     LPPROCESS_INFORMATION lpProcessInformation
);
/***Unicode版本***/
BOOL WINAPI CreateProcessW(
     LPCWSTR lpApplicationName,
     LPWSTR lpCommandLine,
     LPSECURITY_ATTRIBUTES lpProcessAttributes
     LPSECURITY_ATTRIBUTES lpThreadAttributes,
     BOOL bInheritHandles,
     DWORD dwCreationFlags,
     LPVOID lpEvironment,
     LPCWSTR lpCurrentDirectory
     LPSTARTUPINFOW lpStartupInfo,   //Unicode版本
     LPPROCESS_INFORMATION lpProcessInformation
);

窗口狀態(tài)
參考文檔:STARTUPINFO

typedef struct _STARTUPINFO {
//ANSI版本:STARTUPINFOA
//Unicode版本:STARTUPINFOW
     DWORD  cb;
     LPTSTR lpReserved; //ANSI版本嗜闻;Unicode版本 LPTWSTR lpReserved
     LPTSTR lpDesktop;  //ANSI版本蜕依;Unicode版本 LPTWSTR lpDesktop
     LPTSTR lpTitle;  //ANSI版本;Unicode版本 LPTWSTR lpTitle
     DWORD  dwX;
     DWORD  dwY;
     DWORD  dwXSize;
     DWORD  dwYSize;
     DWORD  dwXCountChars;
     DWORD  dwYCountChars;
     DWORD  dwFillAttribute;
     DWORD  dwFlags;
     WORD   wShowWindow;
     WORD   cbReserved2;
     LPBYTE lpReserved2;
     HANDLE hStdInput;
     HANDLE hStdOutput;
     HANDLE hStdError;
}STARTUPINFO, *LPSTARTUPINFO;

進(jìn)程信息
參考文檔:PROCESS_INFORMATION

typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

獲取進(jìn)程句柄
參考文檔:OpenProcess

HANDLE WINAPI OpenProcess(
     DWORD dwDesiredAccess,  //該參數(shù)用于表示向目標(biāo)進(jìn)程對(duì)象索要的訪問權(quán)限
     BOOL bInheritHadnle,
     DWORD dwProcessId,
);

附加進(jìn)程
參考文檔:DebugActiveProcess

BOOL WINAPI DebugActiveProcess(
     DWORD dwProcessId  //進(jìn)程的PID
);

調(diào)試事件
參考文檔:WaitForDebugEvent

BOOL WINAPI WaitForDebugEvent(
     LPDEBUG_EVENT lpDebugEvent,
     DWORD         dwMilliseconds
);

恢復(fù)進(jìn)程
參考文檔:ContinueDebugEvent

BOOL WINAPI ContinueDebugEvent(
    DWORD dwProcessId,
    DWORD dwThreadId,
    DWORD dwContinueStatus
);

分離進(jìn)程
參考文檔:DebugActiveProcessStop

BOOL WINAPI DebugActiveProcessStop(
     DWORD dwProcessId
);

調(diào)試事件的信息
參考文檔:DEBUG_EVENT

typedef struct _DEBUG_EVENT {
     DWORD dwDebugEventCode;  //事件碼
     DWORD dwProcessId;
     DWORD dwThreadId;
     union {
        EXCEPTION_DEBUG_INFO      Exception;
        CREATE_THREAD_DEBUG_INFO  CreateThread;
        CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
        EXIT_THREAD_DEBUG_INFO    ExitThread;
        EXIT_PROCESS_DEBUG_INFO   ExitProcess;
        LOAD_DLL_DEBUG_INFO       LoadDll;
        UNLOAD_DLL_DEBUG_INFO     UnloadDll;
        OUTPUT_DEBUG_STRING_INFO  DebugString;
        RIP_INFO                  RipInfo;
  } u;
} DEBUG_EVENT, *LPDEBUG_EVENT;

2琉雳、異常
參考文檔:EXCEPTION_DEBUG_INFO

typedef struct _EXCEPTION_DEBUG_INFO {
       EXCEPTION_RECORD ExceptionRecord;
       DWORD            dwFirstChance;
} EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO;

參考文檔:EXCEPTION_RECORD

typedef struct _EXCEPTION_RECORD {
      DWORD                    ExceptionCode;   //異常狀態(tài)碼样眠,參考GetExceptionCode函數(shù)
      DWORD                    ExceptionFlags;
      struct _EXCEPTION_RECORD  *ExceptionRecord;
      PVOID                    ExceptionAddress;
      DWORD                    NumberParameters;
      ULONG_PTR                ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD, *PEXCEPTION_RECORD;

3、線程
獲取線程
參考文檔:OpenThread

HANDLE WINAPI OpenThread(
     DWORD dwDesiredAccess,  //設(shè)置線程訪問權(quán)限
     BOOL  bInheritHandle,
     DWORD dwThreadId
);

枚舉線程
參考文檔:CreateToolhelp32Snapshot

HANDLE WINAPI CreateToolhelp32Snapshot(
     DWORD dwFlags,  //獲取信息的確切類型(進(jìn)程類型翠肘,線程類型檐束,模塊類型,堆類型)
     DWORD th32ProcessID
);

線程篩選
參考文檔:Thread32First

BOOL WINAPI Thread32First(
     HANDLE          hSnapshot,
     LPTHREADENTRY32 lpte  //THREADENTRY32結(jié)構(gòu)體
);

參考文檔:Thread32Next

BOOL WINAPI Thread32Next(
    HANDLE          hSnapshot,
    LPTHREADENTRY32 lpte  //THREADENTRY32結(jié)構(gòu)體
);

參考文檔:THREADENTRY32

typedef struct tagTHREADENTRY32 {
    DWORD dwSize;
    DWORD cntUsage;
    DWORD th32ThreadID;
    DWORD th32OwnerProcessID;
    LONG  tpBasePri;
    LONG  tpDeltaPri;
    DWORD dwFlags;
} THREADENTRY32, *PTHREADENTRY32;

4束倍、CONTEXT
參考文檔:CONTEXT

//CONTEXT
//由于不同處理器的CONTEXT是不同的被丧,MSDN官方?jīng)]有給出具體的結(jié)構(gòu)盟戏。
//有安裝VC,或者VisualStudio的可以轉(zhuǎn)到定義甥桂。查看具體結(jié)構(gòu)柿究。
//x86處理器的CONTEXT結(jié)構(gòu)體
typedef struct _CONTEXT {
    DWORD ContextFlags;
    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;
    FLOATING_SAVE_AREA FloatSave;
    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;
    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;
    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;              
    DWORD   EFlags;          
    DWORD   Esp;
    DWORD   SegSs;
    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; //MAXIMUM_SUPPORTED_EXTENSION = 512
} CONTEXT;
//ContextFlags
CONTEXT_CONTROL = 0x00010001  //SS:SP, CS:IP, FLAGS, BP
CONTEXT_INTEGER = 0x00010002  //AX, BX, CX, DX, SI, DI
CONTEXT_SEGMENTS = 0x00010004  //DS, ES, FS, GS
CONTEXT_FLOATING_POINT = 0x00010008
CONTEXT_DEBUG_REGISTERS = 0x00010010  //DB 0-3,6,7
CONTEXT_EXTENDED_REGISTERS = 0x00010020
CONTEXT_FULL = 0x00010007 //CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)

獲取線程的CONTEXT
參考文檔:GetThreadContext

BOOL WINAPI GetThreadContext(
    HANDLE    hThread,
    LPCONTEXT lpContext
);

設(shè)置線程的CONTEXT
參考文檔:SetThreadContext

BOOL WINAPI SetThreadContext(
    HANDLE  hThread,
    const CONTEXT *lpContext
);

5、內(nèi)存操作
參考文檔:ReadProcessMemory

BOOL WINAPI ReadProcessMemory(
    HANDLE  hProcess,
    LPCVOID lpBaseAddress,
    LPVOID  lpBuffer,
    SIZE_T  nSize,
    SIZE_T  *lpNumberOfBytesRead
);

參考文檔:WriteProcessMemory

BOOL WINAPI WriteProcessMemory(
    HANDLE  hProcess,
    LPVOID  lpBaseAddress,
    LPCVOID lpBuffer,
    SIZE_T  nSize,
    SIZE_T  *lpNumberOfBytesWritten
);

獲取函數(shù)虛擬內(nèi)存地址
參考文檔:GetProcAddress

FARPROC WINAPI GetProcAddress(
    HMODULE hModule,
    LPCSTR  lpProcName
);

獲取函數(shù)所在模塊(.dll或.exe文件)
參考文檔:[GetModuleHandle][1] [CloseHandle][2]
[1]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms683199(v=vs.85).aspx
[2]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms724211(v=vs.85).aspx

/***ANSI版本***/
HMODULE WINAPI GetModuleHandleA(
    LPCSTR lpModuleName
);
/***Unicode版本***/
HMODULE WINAPI GetModuleHandleW(
    LPCWSTR lpModuleName
);
//使用完該函數(shù)后記得調(diào)用CloseHandle()關(guān)閉句柄黄选,避免內(nèi)核對(duì)象泄漏(句柄泄漏)
BOOL WINAPI CloseHandle(
    HANDLE hObject
);

獲取系統(tǒng)信息
參考文檔:[GetSystemInfo][3] [SYSTEM_INFO][4]
[3]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms724381(v=vs.85).aspx
[4]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms724958(v=vs.85).aspx

void WINAPI GetSystemInfo(
   LPSYSTEM_INFO lpSystemInfo  //SYSTEM_INFO結(jié)構(gòu)體
);
typedef struct _SYSTEM_INFO {
  union {
    DWORD  dwOemId;
    struct {
      WORD wProcessorArchitecture;
      WORD wReserved;
    };
  };
  DWORD     dwPageSize;  //系統(tǒng)默認(rèn)內(nèi)存頁大小
  LPVOID    lpMinimumApplicationAddress;
  LPVOID    lpMaximumApplicationAddress;
  DWORD_PTR dwActiveProcessorMask;
  DWORD     dwNumberOfProcessors;
  DWORD     dwProcessorType;
  DWORD     dwAllocationGranularity;
  WORD      wProcessorLevel;
  WORD      wProcessorRevision;
} SYSTEM_INFO;

獲取指定進(jìn)程內(nèi)存頁信息
參考文檔:[VirtualQueryEx][5] [MEMORY_BASIC_INFORMATION][6] [VirtualProtectEx][7]
[5]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366907(v=vs.85).aspx
[6]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366775(v=vs.85).aspx
[7]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366899(v=vs.85).aspx

SIZE_T WINAPI VirtualQueryEx(
  HANDLE                    hProcess,
  LPCVOID                   lpAddress,
  PMEMORY_BASIC_INFORMATION lpBuffer,
  SIZE_T                    dwLength
);
typedef struct _MEMORY_BASIC_INFORMATION {
  PVOID  BaseAddress;
  PVOID  AllocationBase;
  DWORD  AllocationProtect;
  SIZE_T RegionSize;
  DWORD  State;
  DWORD  Protect; 
  DWORD  Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
BOOL WINAPI VirtualProtectEx(
  HANDLE hProcess,
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flNewProtect,  //Memory Protection Constants
  PDWORD lpflOldProtect
);

二蝇摸、各種系統(tǒng)宏定義

1、進(jìn)程權(quán)限
參考文檔:Process Access Rights

//Process Access Rights
DELETE = 0x00010000
READ_CONTROL = 0x00020000
WRITE_OWNER = 0x00080000
SYNCHRONIZE = 0x00100000

PROCESS_CREATE_PROCESS = 0x0080
PROCESS_CREATE_THREAD = 0x0002
PROCESS_DUP_HANDLE = 0x0040
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
PROCESS_SET_INFORMATION = 0x0200
PROCESS_SET_QUOTA = 0x0100
PROCESS_SUSPEND_RESUME = 0x0800
PROCESS_TERMINATE = 0x0001
PROCESS_VM_OPERATION = 0x0008
PROCESS_VM_READ = 0x0010
PROCESS_VM_WRITE = 0x0020

PROCESS_ALL_ACCESS = 0x001F0FFF //該值表示目標(biāo)進(jìn)程的所有訪問權(quán)限办陷,其值為以上權(quán)限的其他值取位或運(yùn)算得來貌夕。

2、線程權(quán)限
參考文檔:Thread Access Rights

//Thread Access Rights
DELETE = 0x00010000
READ_CONTROL = 0x00020000
WRITE_DAC = 0x00040000
WRITE_OWNER = 0x00080000
SYNCHRONIZE = 0x00100000

THREAD_DIRECT_IMPERSONATION = 0x0200
THREAD_GET_CONTEXT = 0x0008
THREAD_IMPERSONATE = 0x0100
THREAD_QUERY_INFORMATION = 0x0040
THREAD_QUERY_LIMITED_INFORMATION = 0x0800
THREAD_SET_CONTEXT = 0x0010
THREAD_SET_INFORMATION = 0x0020
THREAD_SET_LIMITED_INFORMATION = 0x0400
THREAD_SET_THREAD_TOKEN = 0x0080
THREAD_SUSPEND_RESUME = 0x0002
THREAD_TERMINATE = 0x0001

THREAD_ALL_ACCESS = 0X001F03FF //包括以上所有訪問權(quán)限民镜。

3啡专、調(diào)試狀態(tài)

//ContinueStatus
DBG_CONTINUE = 0x00010002
DBG_EXCEPTION_NOT_HANDLE = 0x80010001

4、DEBUG_EVENT事件碼

//參考DEBUG_EVENT結(jié)構(gòu)體
EXCEPTION_DEBUG_EVENT = 0x1
CREATE_THREAD_DEBUG_EVENT = 0x2
CREATE_PROCESS_DEBUG_EVENT = 0x3
EXIT_THREAD_DEBUG_EVENT = 0x4
EXIT_PROCESS_DEBUG_EVENT = 0x5
LOAD_DLL_DEBUG_EVENT = 0x6
UNLOAD_DLL_DEBUG_EVENT = 0x7
OUTPUT_DEBUG_STRING_EVENT = 0x8
RIP_EVENT = 0x9

5制圈、信息類型

//參考API函數(shù)CreateToolhelp32Snapshot
TH32CS_INHERIT = 0x80000000
TH32CS_SNAPHEAPLIST = 0x00000001
TH32CS_SNAPMODULE = 0x00000008
TH32CS_SNAPMODULE32 = 0x00000010 
TH32CS_SNAPPROCESS = 0x00000002
TH32CS_SNAPTHREAD = 0x00000004

TH32CS_SNAPALL = 0x0000000F //包含進(jìn)程類型们童、線程類型、模塊類型离唐、堆類型病附。

6、內(nèi)存保護(hù)頁狀態(tài)
參考文檔: Momery Protection Constants

三亥鬓、異常事件處理

四完沪、斷點(diǎn)設(shè)置

1、軟斷點(diǎn)

當(dāng)調(diào)試器被告知需要在某一個(gè)內(nèi)存地址上設(shè)置一個(gè)斷點(diǎn)時(shí)嵌戈,調(diào)試器將首先讀取位于這個(gè)內(nèi)存地址上的第一個(gè)操作碼字節(jié)并將器存儲(chǔ)在斷點(diǎn)列表中覆积,接著調(diào)試器將字節(jié)0xCC寫入那個(gè)內(nèi)存地址取代值。當(dāng)cpu試圖執(zhí)行操作碼0xCC時(shí)熟呛,將觸發(fā)一個(gè)斷點(diǎn)事件(INT3事件)宽档,二調(diào)試器將最終捕獲這個(gè)事件。接著調(diào)試器將檢查指令指針是否正指向一個(gè)此前被我們設(shè)置了斷點(diǎn)的內(nèi)存地址庵朝,如果這個(gè)地址在調(diào)試器內(nèi)部的斷點(diǎn)列表中被查找到吗冤,那么調(diào)試器會(huì)將之前存儲(chǔ)的字節(jié)數(shù)據(jù)寫回此內(nèi)存地址中,這樣當(dāng)次進(jìn)程恢復(fù)執(zhí)行后九府,正確的指令操作碼將被執(zhí)行椎瘟。

//本文使用Python3.5版本
//myDebugger.py
//讀取內(nèi)存
  def read_process_memory(self, address, length):
        data = b""  //初始化字節(jié)碼
        read_buf = create_string_buffer(length)
        count = c_ulong(0)
        if not kernel32.ReadProcessMemory(self.h_process, address, read_buf, length, byref(count)):
            print("read_process_memory fail.")
            return False
        else:
            data += read_buf.raw
            return data
//寫入內(nèi)存
  def write_process_memory(self, address, data):
        count = c_ulong(0)
        length = len(data)
        c_data = c_char_p((data[count.value:]))
        if not kernel32.WriteProcessMemory(self.h_process, address, c_data, length  , byref(count)):
            return False
        else:
            return True
//設(shè)置軟斷點(diǎn),讀取目標(biāo)進(jìn)程內(nèi)存地址第一個(gè)操作碼字節(jié)侄旬,修改為“\xCC”
  def bp_set_sw(self, address):
        if not address in self.software_breakpoints.keys():
            try:
                original_byte = self.read_process_memory(str(address), 2) //為了方便比較這里設(shè)置讀取2個(gè)字節(jié)
                print("read process memory %s" %original_byte)
                //寫入一個(gè)INT3中斷指令肺蔚,操作碼值為"\xCC"
                res = self.write_process_memory(str(address), b"\xCC")  //第二個(gè)參數(shù)傳遞的是字節(jié)類型
                if res:
                    print("Write success.")
                    print("New memory %s"%self.read_process_memory(str(address),2))  //為了方便比較這里設(shè)置讀取2個(gè)字節(jié)
                else:
                    print(kernel32.GetLastError())
                    print("Write fail.")
                //將設(shè)置的斷點(diǎn)記錄進(jìn)斷點(diǎn)列表中
                self.software_breakpoints[address] = (str(address), original_byte)
            except:
                print("bp_set fail.")
                print(kernel32.GetLastError())  //返回異常代碼值
                return False
        return True

2、硬件斷點(diǎn)

通過使用位于cpu上的調(diào)試寄存器來實(shí)現(xiàn)儡羔。一般cpu上有8個(gè)調(diào)試寄存器(DR0到DR7)宣羊。DR0到DR3用于存儲(chǔ)所設(shè)硬件斷點(diǎn)的內(nèi)存地址璧诵。任何時(shí)刻最多只能使用4個(gè)硬件斷點(diǎn)。DR4和DR5保留使用仇冯。DR6為調(diào)試狀態(tài)寄存器之宿,記錄上一次斷點(diǎn)觸發(fā)所產(chǎn)生的調(diào)試事件類型信息。DR7是硬件斷點(diǎn)的激活開關(guān)赞枕,同時(shí)存儲(chǔ)各個(gè)斷點(diǎn)觸發(fā)條件信息澈缺。通過設(shè)置DR7特定標(biāo)記位,可以設(shè)置斷點(diǎn)的觸發(fā)條件:

  • 當(dāng)位于一個(gè)特定內(nèi)存地址上的指令被執(zhí)行時(shí)觸發(fā)斷點(diǎn)(00 ——執(zhí)行斷點(diǎn)炕婶,斷點(diǎn)長度標(biāo)記1字節(jié))
  • 當(dāng)數(shù)據(jù)被寫入一個(gè)特定內(nèi)存地址是觸發(fā)斷點(diǎn)(01——數(shù)據(jù)寫入斷點(diǎn),斷點(diǎn)長度標(biāo)志2字節(jié) WORD)
  • 當(dāng)數(shù)據(jù)被讀出或?qū)懭耄ú话▓?zhí)行)一個(gè)特定非可執(zhí)行內(nèi)存地址是觸發(fā)斷點(diǎn)莱预。(11——數(shù)據(jù)讀寫(但非執(zhí)行)斷點(diǎn)柠掂,斷點(diǎn)長度標(biāo)記4字節(jié) DWORD)

硬件斷點(diǎn)使用1號(hào)中斷(INT1),INT1事件被用于硬件斷點(diǎn)和單步事件依沮。
在cpu試圖執(zhí)行一條指令前涯贞,首先檢查當(dāng)前指令所在的地址是否被設(shè)置了有效的硬件斷點(diǎn),除此之外危喉,cpu還會(huì)檢查當(dāng)前指令包含的操作數(shù)是否位于設(shè)置了硬件斷點(diǎn)的內(nèi)存地址上宋渔。如果以上的內(nèi)存地址被存儲(chǔ)在DR0-DR3中任意的一個(gè)調(diào)試寄存器中,并且滿足之前所設(shè)定的讀辜限、寫或者執(zhí)行條件皇拣,那么cpu暫停并觸發(fā)一個(gè)INT1事件。如果相關(guān)的內(nèi)存地址并沒有存儲(chǔ)在任何一個(gè)調(diào)試寄存器中薄嫡,cpu在執(zhí)行完當(dāng)前的指令后將繼續(xù)執(zhí)行下一條指令并且同樣進(jìn)行斷點(diǎn)檢測氧急,依次往復(fù)。

//myDebugger.py
//設(shè)置硬件斷點(diǎn)
  def bp_set_hw(self, address, length, condition):
        //檢查硬件斷點(diǎn)的長度是否有效
        if length not in (1, 2, 4):
            return False
        else:
            length -= 1
        //檢查硬件斷點(diǎn)的觸發(fā)條件是否有效
        if condition not in (HW_ACCESS, HW_EXECUTE, HW_WRITE):
            //這里的condition其實(shí)就是調(diào)試寄存器DR7的標(biāo)志位毫深,
            //HW_ACCESS = 0x00000003, HW_EXECUTE = 0x00000000, HW_WRITE = 0x00000001
            return False
        //檢查是否存在空置的調(diào)試寄存器
        if not 0 in self.hardware_breakpoints.keys():  
           //Python3.x 的版本沒有has_keys()吩坝,本文所有用到該函數(shù)的都改用in代替
           //Python2.x 的寫法:if not self.hardware_breakpoints.has_keys(0): 
            available = 0
        elif not 1 in self.hardware_breakpoints.keys():
            available =1
        elif not 2 in self.hardware_breakpoints.keys():
            available = 2
        elif not 3 in self.hardware_breakpoints.keys():
            available = 3
        else:
            return  False
        //在每個(gè)線程環(huán)境下設(shè)置調(diào)試寄存器
        for thread_id in self.enumerate_threads():
            context = self.get_thread_context(thread_id=thread_id)
            context.Dr7 |= 1 << (available * 2)  //設(shè)置DR7中相應(yīng)的標(biāo)志位,來激活斷點(diǎn)
            //在空置的寄存器下寫入斷點(diǎn)地址
            if available == 0:
                context.Dr0 = address
            elif available == 1:
                context.Dr1 = address
            elif available == 2:
                context.Dr2 = address
            elif available == 3:
                context.Dr3 = address
            //設(shè)置硬件斷點(diǎn)觸發(fā)條件
            context.Dr7 |= condition << ((available * 4) + 16)
            //設(shè)置硬件斷點(diǎn)長度
            context.Dr7 |= length << ((available * 4) + 18)
            //提交改動(dòng)后線程上下文環(huán)境信息
            h_thread = self.open_thread(thread_id)
            kernel32.SetThreadContext(h_thread, byref(context))
        //更新內(nèi)部硬件斷點(diǎn)列表
        self.hardware_breakpoints[available] = (str(address), length, condition)
        return  True
//執(zhí)行單步事件處理
  def exception_handler_single_step(self):
        //摘自PyDbg源碼中的一段注釋:
        //判斷該單步事件是否有一個(gè)硬件斷點(diǎn)所觸發(fā)哑蔫,若是則捕獲這個(gè)斷點(diǎn)事件钉寝,根據(jù)Intel給出的文檔
        //應(yīng)當(dāng)能夠通過檢測調(diào)試寄存器Dr6上的BS標(biāo)志位來判斷出該單步事件的觸發(fā)原因,然而Windows
        //系統(tǒng)似乎并沒有正確地將這個(gè)標(biāo)志位傳遞過來闸迷。
        if self.context.Dr6 &0x01 and 0 in self.hardware_breakpoints.keys():
            slot = 0
        elif self.context.Dr6 &0x02 and 1 in self.hardware_breakpoints.keys():
            slot = 1
        elif self.context.Dr6 &0x04 and 2 in self.hardware_breakpoints.keys():
            slot = 2
        elif self.context.Dr6 &0x08 and 3 in self.hardware_breakpoints.keys():
            slot = 3
        else:
            //此次INT1中斷不是由硬件斷點(diǎn)觸發(fā)
            continue_status = DBG_EXCEPTION_NOT_HANDLED
            return continue_status
        //從列表中移除斷點(diǎn)
        if self.bp_del_hw(slot):
            continue_status = DBG_CONTINUE
            print("[*] Hardware breakpoint removed.")
            return continue_status
//刪除硬件斷點(diǎn)
  def bp_del_hw(self, slot):
        //移除所有線程的硬件斷點(diǎn)
        for thread_id in self.enumerate_threads():
            context = self.get_thread_context(thread_id=thread_id)
            //重新設(shè)置DR7調(diào)試標(biāo)志位
            context.Dr7 &= ~(1 << (slot * 2))
            //清零斷點(diǎn)地址
            if slot == 0:
                context.Dr0 = 0x00000000
            elif slot == 1:
                context.Dr1 = 0x00000000
            elif slot == 2:
                context.Dr2 = 0x00000000
            elif slot == 3:
                context.Dr3 = 0x00000000
            else:
                return False
            //清空斷點(diǎn)觸發(fā)條件標(biāo)志位
            context.Dr7 &= ~(3 << ((slot * 4) + 16))
            //清空斷點(diǎn)長度標(biāo)志位
            context.Dr7 &= ~(3 << ((slot * 4) + 18))
            //提交移除斷點(diǎn)后的線程上下文環(huán)境信息
            h_thread = self.open_thread(thread_id)
            kernel32.SetThreadContext(h_thread, byref(context))
        //從內(nèi)部斷點(diǎn)列表中移除硬件斷點(diǎn)
        del self.hardware_breakpoints[slot]
        return True

3嵌纲、內(nèi)存斷點(diǎn)

當(dāng)一個(gè)調(diào)試器設(shè)置一個(gè)內(nèi)存斷點(diǎn)時(shí),調(diào)試器實(shí)質(zhì)上所做的是改變一個(gè)內(nèi)存區(qū)域或一個(gè)內(nèi)存頁的訪問權(quán)限稿黍。當(dāng)cpu視圖訪問者一內(nèi)存區(qū)域時(shí)疹瘦,便會(huì)觸發(fā)GUARD_PAGE_EXCEPTION異常事件。

//myDebugger.py
//設(shè)置內(nèi)存斷點(diǎn)
  def bp_set_mem(self, address, size):
        mbi = MEMORY_BASIC_INFORMATION()
        //判斷是否能獲取一個(gè)完整的MEMORY_BASIC_INFORMATION結(jié)構(gòu)體巡球,否則返回false
        if kernel32.VirtualQueryEx(self.h_process, address, byref(mbi), sizeof(mbi)) < sizeof(mbi):
            return False
        current_page = mbi.BaseAddress
       //對(duì)整個(gè)內(nèi)存斷點(diǎn)區(qū)域所覆蓋的所有內(nèi)存頁進(jìn)行設(shè)置訪問權(quán)限
        while current_page <= address + size:
            //將這個(gè)內(nèi)存頁記錄在列表中言沐,以便于將這些保護(hù)頁與操作系統(tǒng)或debuge進(jìn)程自設(shè)的保護(hù)頁區(qū)別開來
            self.guarded_pages.append(current_page)
            old_protection = c_ulong(0)
            if not kernel32.VirtualProtectEx(self.h_process, current_page, size,
                                             mbi.Protect | PAGE_GUARD, byref(old_protection)):
                return False
            //以系統(tǒng)所設(shè)置的內(nèi)存頁大小作為增長單位邓嘹,遞增內(nèi)存斷點(diǎn)區(qū)域
            current_page += self.page_size
        //將該內(nèi)存斷點(diǎn)記錄進(jìn)全局列表中
        self.memory_breakpoints[address] = (address, size, mbi)
        return True

五、功能整合

//myDebugDefines.py
from ctypes import *

BYTE = c_ubyte
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
PVOID = c_void_p
LPVOID = c_void_p
UINT_PTR = c_ulong
SIZE_T = c_ulong

DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010
PROCESS_ALL_ACCESS = 0x001F0FFF
INFINITE = 0xFFFFFFFF
DBG_CONTINUE = 0x00010002
DBG_EXCEPTION_NOT_HANDLED = 0x80010001

EXCEPTION_DEBUG_EVENT = 0x01
CREATE_THREAD_DEBUG_EVENT = 0x02
CREATE_PROCESS_DEBUG_EVENT = 0x03
EXIT_THREAD_DEBUG_EVENT = 0x04
EXIT_PROCESS_DEBUG_EVENT = 0x05
LOAD_DLL_DEBUG_EVENT = 0x06
UNLOAD_DLL_DEBUG_EVENT = 0x07
OUTPUT_DEBUG_STRING_EVENT = 0x08
RIP_EVENT = 0x09

EXCEPTION_ACCESS_VIOLATION = 0xC0000005
EXCEPTION_BREAKPOINT = 0x80000003
EXCEPTION_GUARD_PAGE = 0x80000001
EXCEPTION_SINGLE_STEP = 0x80000004

TH32CS_SNAPTHREAD = 0x00000004

THREAD_ALL_ACCESS = 0x001F03FF

CONTEXT_FULL = 0x00010007
CONTEXT_DEBUG_REGISTERS = 0x00010010

HW_ACCESS = 0x00000003
HW_EXECUTE = 0x00000000
HW_WRITE = 0x00000001

PAGE_GUARD = 0x00000100

class STARTUPINFO(Structure):
    _fields_ = [
        ("cb", DWORD),
        ("lpReserved", LPTSTR),
        ("lpDesktop", LPTSTR),
        ("lpTitle", LPTSTR),
        ("dwX", DWORD),
        ("dwY", DWORD),
        ("dwXSize", DWORD),
        ("dwYSize", DWORD),
        ("dwXCountChars", DWORD),
        ("dwYCountChars", DWORD),
        ("dwFillAttribute", DWORD),
        ("dwFlags", DWORD),
        ("wShowWindow", WORD),
        ("cbReserved2",WORD),
        ("lpReserved2", LPTSTR),
        ("hStdInput", HANDLE),
        ("hStdOutput", HANDLE),
        ("hStdError", HANDLE),
    ]
class PROCESS_INFORMATION(Structure):
    _fields_ = [
        ("hProcess", HANDLE),
        ("hThread", HANDLE),
        ("dwProcessId", DWORD),
        ("dwThreadId", DWORD),
    ]
class EXCEPTION_RECORD(Structure):
    pass
EXCEPTION_RECORD._fields_ = [
    ("ExceptionCode", DWORD),
    ("ExceptionFlags", DWORD),
    ("ExceptionRecord", POINTER(EXCEPTION_RECORD)),
    ("ExceptionAddress", PVOID),
    ("NumberParameters", DWORD),
    ("ExceptionInformation", UINT_PTR*15),
]
class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
        ("ExceptionRecord", EXCEPTION_RECORD),
        ("dwFirstChance", DWORD),
    ]
class DEBUG_EVENT_UNION(Union):
    _fields_ = [
        ("Exception", EXCEPTION_DEBUG_INFO),
    ##  ("CreateThread", CREATE_THREAD_DEBUG_INFO),
    ]
class DEBUG_EVENT(Structure):
    _fields_ = [
        ("dwDebugEventCode", DWORD),
        ("dwProcessId", DWORD),
        ("dwThreadId", DWORD),
        ("u", DEBUG_EVENT_UNION),
    ]
class THREADENTRY32(Structure):
    _fields_ = [
        ("dwSize", DWORD),
        ("cntUsage", DWORD),
        ("th32ThreadID", DWORD),
        ("th32OwnerProcessID", DWORD),
        ("tpBasePri", DWORD),
        ("tpDeltaPri", DWORD),
        ("dwFlags", DWORD)
    ]
class FLOATING_SAVE_AREA(Structure):
    _fields_ = [
        ("ControlWord", DWORD),
        ("StatusWord", DWORD),
        ("TagWord", DWORD),
        ("ErrorOffset", DWORD),
        ("ErrorSelector", DWORD),
        ("DataOffset", DWORD),
        ("DataSelector", DWORD),
        ("RegisterArea", BYTE * 80),
        ("Cr0NpxState", DWORD),
    ]
class CONTEXT(Structure):
    _fields_ = [
        ("ContextFlags", DWORD),
        ("Dr0", DWORD),
        ("Dr1", DWORD),
        ("Dr2", DWORD),
        ("Dr3", DWORD),
        ("Dr6", DWORD),
        ("Dr7", DWORD),
        ("FloatSave", FLOATING_SAVE_AREA),
        ("SegGs", DWORD),
        ("SegFs", DWORD),
        ("SegEs", DWORD),
        ("SegDs", DWORD),
        ("Edi", DWORD),
        ("Esi", DWORD),
        ("Ebx", DWORD),
        ("Edx", DWORD),
        ("Ecx", DWORD),
        ("Eax", DWORD),
        ("Ebp", DWORD),
        ("Eip", DWORD),
        ("SegCs", DWORD),
        ("EFlags", DWORD),
        ("Esp", DWORD),
        ("SegSs", DWORD),
        ("ExtendedRegisters", BYTE * 512),
    ]
class PROC_STRUCT(Structure):
    _fields_ = [
        ("wProcessorArchitecture", WORD),
        ("wReserved", WORD),
    ]
class SYSTEM_INFO_UNION(Union):
    _fields_ = [
        ("dsOemId", DWORD),
        ("sProcStruc", PROC_STRUCT),
    ]
class SYSTEM_INFO(Structure):
    _fields_ = [
        ("uSysInfo", SYSTEM_INFO_UNION),
        ("dwPageSize", DWORD),
        ("lpMinimumApplicationAddress", LPVOID),
        ("lpMaximumApplicationAddress", LPVOID),
        ("dwActiveProcessMask", DWORD),
        ("dwNumberOfProcessors", DWORD),
        ("dwProcessorType", DWORD),
        ("dwAllocationGranularity", DWORD),
        ("wProcessorLevel", WORD),
        ("wProcessorRevision", WORD),
    ]
class MEMORY_BASIC_INFORMATION(Structure):
    _fields_ = [
        ("BaseAddress", PVOID),
        ("AllocationBase", PVOID),
        ("AllocationProtect", DWORD),
        ("RegionSize",SIZE_T),
        ("State", DWORD),
        ("Protect", DWORD),
        ("Type", DWORD),
    ]
//myDebugger.py
//完整代碼
//定義debugger類
class debugger():
    def __init__(self):
        self.h_process = None
        self.h_thread = None
        self.pid = None
        self.debugger_active = False
        self.context = None
        self.exception = None
        self.exception_address = None
        self.software_breakpoints = {}
        self.hardware_breakpoints = {}
        self.memory_breakpoints = {}
        self.first_breakpoints = True
        system_info = SYSTEM_INFO()
        kernel32.GetSystemInfo(byref(system_info))
        self.page_size = system_info.dwPageSize
        self.guarded_pages = []

    def load(self, path_to_exe):
        //參數(shù)dwCreationFlags中的標(biāo)志位控制著進(jìn)程的創(chuàng)建方式险胰。
        //以下參數(shù)初始化可以參考CreateProcess函數(shù)
        creation_flags = DEBUG_PROCESS
        //實(shí)例化之前定義的結(jié)構(gòu)體
        startupinfo = STARTUPINFO()
        process_information = PROCESS_INFORMATION()
        //以下兩個(gè)成員變量的共同作用下汹押,新建進(jìn)程將在一個(gè)單獨(dú)的窗體中被顯示,也可以通過改變
        //結(jié)構(gòu)體STARTUPINFO中的各成員變量的值來控制debugee進(jìn)程的行為起便。
        startupinfo.dwFlags = 0x1
        startupinfo.wShowWindow = 0x0
        //設(shè)置結(jié)構(gòu)體STARTUPINFO中的成員變量cb的值棚贾,用以表示結(jié)構(gòu)體本身的大小
        startupinfo.cb = sizeof(startupinfo)

        if  windll.kernel32.CreateProcessW(path_to_exe,
                                    None, None, None, None,
                                    creation_flags,
                                    None, None,
                                    byref(startupinfo),
                                    byref(process_information)):
            //注意print的規(guī)范,python3.x是帶有小括號(hào)的榆综,python2.x沒有小括號(hào)妙痹。
            print("[*] We have successfully launched the process!")  
            print("[*] PID : %d" %process_information.dwProcessId)
            //保存一個(gè)指向新建進(jìn)程的有效句柄,以供后續(xù)的進(jìn)程訪問所使用鼻疮。
            self.h_process = self.open_process(process_information.dwProcessId)
        else:
            print("[*] Error : 0x%08x." %kernel32.GetLastError())
    //打開指定pid的進(jìn)程
    def open_process(self, pid):
        h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
        print(type(h_process))
        return h_process
    //附加進(jìn)程
    def attach(self, pid):
        self.h_process = self.open_process(pid)
        //試圖附加到目標(biāo)進(jìn)程怯伊,若附加操作失敗,則輸出提示信息后返回
        print("attach after h_process: 0x%08x." %self.h_process)
        if kernel32.DebugActiveProcess(pid):
            self.debugger_active = True
            self.pid = int(pid)
            ##self.run()
        else:
            print("[*] Unable to attach to the process.")
    def run(self):
        //等待發(fā)生在debugee進(jìn)程中的調(diào)試事件
        while self.debugger_active == True:
            self.get_debug_event()
    //獲取調(diào)試事件
    def get_debug_event(self):
        debug_event = DEBUG_EVENT()
        continue_status = DBG_CONTINUE
        if kernel32.WaitForDebugEvent(byref(debug_event),INFINITE):
            ##input("press a key to continue...")
            ##self.debugger_active = False
            //獲取相關(guān)線程的句柄并提取其CONTEXT
            self.h_thread = self.open_thread(debug_event.dwThreadId)
            self.context = self.get_thread_context(debug_event.dwThreadId)
            ##self.context = self.get_thread_context(self.h_thread)
            print("Event code: %d Thread ID: %d" %(debug_event.dwDebugEventCode, debug_event.dwThreadId))
            //如果事件碼為異常事件判沟,則檢測該事件碼的類型
            if debug_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT:
                //獲得異常代碼
                self.exception = debug_event.u.Exception.ExceptionRecord.ExceptionCode
                self.exception_address = debug_event.u.Exception.ExceptionRecord.ExceptionAddress
            if self.exception == EXCEPTION_ACCESS_VIOLATION:
                print("[*] Access Violation Detected")
            //若檢測到斷點(diǎn)耿芹,則調(diào)用相應(yīng)的處理程序
            elif self.exception == EXCEPTION_BREAKPOINT:
                continue_status = self.exception_handler_breakpoint()
            elif self.exception == EXCEPTION_GUARD_PAGE:
                print("Guard Page Access Detected.")
            elif self.exception == EXCEPTION_SINGLE_STEP:
                self.exception_handler_single_step()
                print("Single Stepping.")
            kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
    //退出附加進(jìn)程
    def detach(self):
        if kernel32.DebugActiveProcessStop(self.pid):
            print("[*] Finished debugging Exiting...")
            return True
        else:
            print("[*] Detach fail.")
            return False
    //打開線程
    def open_thread(self,thread_id):
        h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)
        if h_thread is not None:
            return h_thread
        else:
            print("[*] Could not obtain a valid thread handle.")
            return False
    //枚舉線程
    def enumerate_threads(self):
        thread_entry = THREADENTRY32()
        thread_list = []
        snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid)
        if snapshot is not None:
            thread_entry.dwSize = sizeof(thread_entry)
            success = kernel32.Thread32First(snapshot, byref(thread_entry))
            while success:
                if thread_entry.th32OwnerProcessID == self.pid:
                    thread_list.append(thread_entry.th32ThreadID)
                success = kernel32.Thread32Next(snapshot, byref(thread_entry))

            kernel32.CloseHandle(snapshot)
            return thread_list
        else:
            print("enumerate_thread fail.")
            return False
    //獲取線程的CONTEXT
    def get_thread_context(self, thread_id):
        context = CONTEXT()
        context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS
        h_thread = self.open_thread(thread_id)
        if kernel32.GetThreadContext(h_thread, byref(context)):
            kernel32.CloseHandle(h_thread)
            return context
        else:
            print("get thread context fail.")
            return False
    //斷點(diǎn)處理
    def exception_handler_breakpoint(self):
        print("[*] Inside the breakpoint handler.")
        print("Exception address: 0x%08x" %self.exception_address)
        print(kernel32.GetLastError())
        if not self.exception_address in self.hardware_breakpoints.keys():
            if self.first_breakpoints == True:
                self.first_breakpoints = False
                print("[*] Hit the first breakpoint.")
        else:
            print("[*] Hit user defined breakpoint.")
        return DBG_CONTINUE
    //從內(nèi)核模塊提取目標(biāo)函數(shù)虛擬內(nèi)存地址
    def func_resolve(self, dll, function):
        handle = kernel32.GetModuleHandleW(dll)
        print("func_resolve handle 0x%08x" %handle)
        address = kernel32.GetProcAddress(handle, function)
        print(kernel32.GetLastError())
        print("func_resolve address 0x%08x" %address)
        kernel32.CloseHandle(handle)
        return address
    //完整代碼,參考軟斷點(diǎn)
    def read_process_memory(self, address, length):   
    def write_process_memory(self, address, data):
    def bp_set_sw(self, address):
    //完整代碼挪哄,參考硬件斷點(diǎn)
    def bp_set_hw(self, address, length, condition):    
    def exception_handler_single_step(self):    
    def bp_del_hw(self, slot):
    //完整代碼吧秕,參考內(nèi)存斷點(diǎn)
    def bp_set_mem(self, address, size):
//test.py
import myDebugger
from myDebuggerDefines import *
debugger = myDebugger.debugger()
##debugger.load("C:\\windows\\system32\\calc.exe")
##print("h_process:0x%08x"%debugger.h_process)

pid = input("Enter the PID of the process to attach to : ")
debugger.attach(int(pid))

'''
//枚舉線程存入列表中
list = debugger.enumerate_threads()
//從列表中讀取每個(gè)線程的CONTEXT信息
for thread in list:
    thread_context = debugger.get_thread_context(thread)
    print("[*] Dumping registers for thread ID : 0x%08x" %thread)
    print("[**]EIP: 0x%08x"%thread_context.Eip)
    print("[**]ESP: 0x%08x"%thread_context.Esp)
    print("[**]EBP: 0x%08x"%thread_context.Ebp)
    print("[**]EAX: 0x%08x"%thread_context.Eax)
    print("[**]EBX: 0x%08x"%thread_context.Ebx)
    print("[**]ECX: 0x%08x"%thread_context.Ecx)
    print("[**]EDX: 0x%08x"%thread_context.Edx)
    print("[*]END DUMP")
'''
//獲取msvcrt.dll模塊里的printf函數(shù)的虛擬地址
printf_address = debugger.func_resolve("msvcrt.dll", b"wprintf")  //第二個(gè)參數(shù)需要轉(zhuǎn)化成字節(jié)編碼
print("[*] Address of printf: 0x%08x" %printf_address)
##print("pid:%d"%debugger.pid)
//測試軟斷點(diǎn)
##debugger.bp_set_sw(str(printf_address))
//測試硬件斷點(diǎn)
##debugger.bp_set_hw(printf_address, 1, HW_EXECUTE)
//測試內(nèi)存斷點(diǎn)
debugger.bp_set_mem(printf_address, 0x2000)
debugger.run()
##debugger.detach()
//用于測試
//測試步驟:先運(yùn)行printf_loop.py,然后到系統(tǒng)任務(wù)管理器找到該進(jìn)程的PID迹炼,再運(yùn)行test.py砸彬,提示輸入PID。
//printf_loop.py
from ctype import *
import time

msvcrt = cdll.msvcrt
counter = 0
while 1:
     //這里使用printf打印疗涉,測試時(shí)就獲取printf函數(shù)的虛擬內(nèi)存地址
     //如果使用wprintf打印拿霉,測試時(shí)就獲取wprintf函數(shù)的虛擬內(nèi)存地址
     msvcrt.printf("Loop interation %d!\n" %counter) 
     time.sleep(1)
     counter += 1

六、兼容問題

1咱扣、python2.x跟python3.x部分規(guī)范問題绽淘。
2、ANSI跟Unicode編碼問題闹伪。python3.x用的是Unicode編碼沪铭,如果需要使用到ANSI編碼,只需在字符串前加b偏瓤,例:b“string”

結(jié)束語

本文只是根據(jù)調(diào)試器的基本原理構(gòu)建一個(gè)簡單的調(diào)試器杀怠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市厅克,隨后出現(xiàn)的幾起案子赔退,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硕旗,死亡現(xiàn)場離奇詭異窗骑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)漆枚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門创译,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人墙基,你說我怎么就攤上這事软族。” “怎么了残制?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵立砸,是天一觀的道長。 經(jīng)常有香客問我初茶,道長仰禽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任纺蛆,我火速辦了婚禮,結(jié)果婚禮上规揪,老公的妹妹穿的比我還像新娘桥氏。我一直安慰自己,他們只是感情好猛铅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布字支。 她就那樣靜靜地躺著,像睡著了一般奸忽。 火紅的嫁衣襯著肌膚如雪堕伪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天栗菜,我揣著相機(jī)與錄音欠雌,去河邊找鬼。 笑死疙筹,一個(gè)胖子當(dāng)著我的面吹牛富俄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播而咆,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼霍比,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暴备?” 一聲冷哼從身側(cè)響起悠瞬,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后浅妆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體望迎,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年狂打,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了擂煞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡趴乡,死狀恐怖对省,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晾捏,我是刑警寧澤蒿涎,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站惦辛,受9級(jí)特大地震影響劳秋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胖齐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一玻淑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呀伙,春花似錦补履、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雨女,卻和暖如春谚攒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氛堕。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工馏臭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岔擂。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓位喂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乱灵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子塑崖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容