Tuesday, March 15, 2005

 

Some notes on Dump

I.
如何获取windows的调试符号文件

By Hercules
From http://blog.vckbase.com/zyq654321/archive/2004/11/01/1188.html
http://blog.vckbase.com/zyq654321/archive/2004/11/05/1289.html

Program Database是目前最新的Debug信息格式。目前为此已经出现了两个版本,一个是用于Visual studio6.0的PDB20,另一个是用于Visual studio.Net(2002 or 2003)的PDB70。然后后者并不会兼容前者,所以visual studio6.0将不能使用最新的PDB70。

下面指出PDB中通常会保存一些什么类型的信息:全局函数和变量;成员函数、成员变量及局部变量;符号对应的源文件中的行号;变量类型信息;FPO信息;编辑后继续运行信息。一般来说对于每一种符号都会有相应的保存属性,符号类型、地址、大小和符号名。

Program Database信息是与文件执行体分离的,通常它会保存在.pdb后缀的文件中。PDB文件的建立是与编译器和链接器紧密相连的。通过设定编译器选项(/ZI, /Z7, /Zi, /Zd),编译器可以在编译源文件时对应每一个源文件产生一个相应的调试符号文件;然后设定相应的链接器选项(/DEBUGTYPE, /DEBUG, /PDB等)可以将单个的调试符号文件合并成PDB文件。

如果你要利用PDB为你的工作服务,你可以使用最新的Debugging Tools for Window来对PDB文件(当然也可以是EXE和DLL文件)进行对符号信息的操作。

下载地址:Debugging Tools for Windows http://www.microsoft.com/whdc/ddk/debugging/default.mspx

如果你要使用它的SDK,就应该确保在安装时使用的是Custom安装。这样在你安装后的你会得到最新的dbghelp.h和dbghelp.lib等文件。

PDB(Program Database) file是一种最新的用于存贮debug信息的格式文件。与它的两个“前辈”(COFF和CODEVIEW)不同的是,PDB可以保存所有种类的debug信息,并支持增量链接(incremental link)。此外,PDB文件是以单独文件的形式出现,而COFF和COEDEVIEW两种格式的debug信息是存在执行文件内的。(注:CODEVIEW格式的debug信息也可存于执行文件名,其后缀名为.dbg).

通常在调试时,我们无法获得一些系统DLL的调试符号,这样会让我们对程序运行的细节的了解可能会有一些问题,当然我们可以通过查看反汇编窗口来解决一些问题。但是如果你能够得到这个调试符号,是不是觉得会很爽。呵呵,爽是次要的,重要的是这些调试符号会强化你所需要的线索。要实现这一点,就须要有相应的windows调试符号文件,你可以通过下面的方法获得它:

symsrv*symsrv.dll*f:\localsymbols*http://msdl.microsoft.com/download/symbols

将此句加在项目属性中的[符号路径处](VC++.NET)即可。 其中f:\localsymbols是你的本地盘路径,请先确保它的存在。完成后,F5进行debug。第一次是时间比较长,这是因为在下载符号库文件。你可以在output窗口中看到一个接一个的符号库被加载……

提示,设定了此属性后可能会导致debug的速度下降。当然这是正常现象,东东多了么。

现在就去下它们吧,Enjoy!

另外也可以通过设定环境变量来实现:

环境变量法:
Set _NT_SYMBOL_PATH = symsrv*symsrv.dll*f:\localsymbols*http://msdl.microsoft.com/download/symbols

具体设定位置:My Computer--->Properties---->Advanced--->Environment Varibles

不过偶建议最好用时再设它,不用时就不要设它,免得影响速度。

注: 关于获取SymSrv.dll ,请安装 Debugging Tools for Windows package

II.
源码:演示如何dump指定文件的符号信息

By Hercules
From http://blog.vckbase.com/zyq654321/archive/2004/11/08/1364.html

前面讲了一些关于符号信息的小常识。今天正好有点空,整理了一段代码,贴上来供大家作例子。代码是完整的,建个新工程加入它即可。但是要注意也许你的环境不支持这些代码,没关系,看看偶的上一篇文章,获得最新的debugging tools for windows即可。闲话少说,例子代码如下。

//============================================================================

// SymDump: A small tool used to dump symbol informaton from EXE, DLL or PDB files

// Author : zyq654321 --- Oct, 2004

// Comment: This is only a sample code to show you how to dump symbol information,

// you can improve it and your advise is appreciated.

#include

#include

#define _NO_CVCONST_H // We should define the constant in order to ...

#include

#include



#pragma comment( lib, "dbghelp.lib" )



BOOL CALLBACK SymEnumSymbolsProc(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext )

{

if( pSymInfo != NULL )

{

// Show the symbol

_tprintf(_T("TYPE INDEX:%d "), pSymInfo->TypeIndex);

_tprintf(_T("SIZE:%d "), pSymInfo->Size);

_tprintf(_T("VALUE: %d "), pSymInfo->Value);

_tprintf(_T("NAME: %s\n"), pSymInfo->Name);

_tprintf(_T(" SYMBOL ADDRESS:0x%X "), pSymInfo->Address);

_tprintf(_T("PDB CLASSIFICATION: %d\n"), pSymInfo->Tag);





}

return TRUE;

}



int _tmain( int argc, const TCHAR* argv[] )

{

if(argc < 2)

{

goto FAILED_PARAM;

}



// Initilaize the symbol handle for the current process

if(!SymInitialize( GetCurrentProcess(),

NULL,

FALSE ))

{

_tprintf(_T("Failed when SymInitialize():%d\n"), GetLastError());

return 0;

}



if( argv[1] == NULL )

{

goto FAILED_PARAM;

}



//------------------------------------------------------------------------

// Set initial parameters

TCHAR pszExt[MAX_PATH];

_tsplitpath( argv[1], NULL, NULL, NULL, pszExt );

DWORD64 dw64Base = 0; // if the image is a .pdb file, dw64Base cannot be zero.

// if the value is zero, the library obtains the load address

// from the symbol file.

DWORD dwFileSize = 0; // if the image is a .pdb file, dwFileSize cannot be zero.

// if the value is zero, the library obtains the size

// from the symbol file.



_tcslwr(pszExt);

if(_tcsicmp(pszExt, _T(".pdb")) == 0)

{

// this is a .pdb file, and so we should set the load address and file size;

dw64Base = 0x10000000;

// get the file size

HANDLE hFile = CreateFile( argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );

if( INVALID_HANDLE_VALUE == hFile )

{

_tprintf(_T("Failed when open %s: %d"), argv[1], GetLastError());

goto FAILED_PARAM;

}

if( INVALID_FILE_SIZE == ( dwFileSize = GetFileSize(hFile, NULL)) )

{

_tprintf(_T("Failed when read the size of %s: %d"), argv[1], GetLastError());

goto FAILED_PARAM;

}

CloseHandle(hFile);

}



//------------------------------------------------------------------------

// Load symbol table

_tprintf(_T("Loading... %s\n"), argv[1]);

DWORD64 dw64ModAddress = SymLoadModule64( GetCurrentProcess(),

NULL,

argv[1],

NULL,

dw64Base,

dwFileSize);



if( dw64ModAddress == 0 )

{

_tprintf(_T("Failed when SymLoadModule64(): %d \n"), GetLastError());

return 0;

}

_tprintf(_T("Load Address: %I64x \n"), dw64ModAddress);



//------------------------------------------------------------------------

// Show symbol information supported by the loaded file

IMAGEHLP_MODULE64 ImageModule;



memset(&ImageModule, 0, sizeof(ImageModule) );

ImageModule.SizeOfStruct = sizeof(ImageModule);

if(!SymGetModuleInfo64( GetCurrentProcess(),

dw64ModAddress,

&ImageModule ))

{

_tprintf(_T("Failed when load image info:%d"), GetLastError());

return 0;

}



// Show informations included in the IMAGEHLP_MODULE64

_tprintf(_T("CheckSum : %d\n"), ImageModule.CheckSum);

_tprintf(_T("Number of Loaded Symbols: %d\n"), ImageModule.NumSyms);

switch(ImageModule.SymType)

{

case SymCoff:

_tprintf(_T("Symbol Type: COFF symbols\n") );

break;

case SymCv:

_tprintf(_T("Symbol Type: CodeView symbols\n") );

break;

case SymDeferred:

_tprintf(_T("Symbol Type: Symbol loading deferred\n") );

break;

case SymDia:

_tprintf(_T("Symbol Type: DIA symbols\n") );

break;

case SymExport:

_tprintf(_T("Symbol Type: Symbols generated from a DLL export table\n") );

break;

case SymNone:

_tprintf(_T("Symbol Type: No symbols are loaded\n") );

break;

case SymPdb:

_tprintf(_T("Symbol Type: PDB symbols\n") );

break;

case SymSym:

_tprintf(_T("Symbol Type: .sym file\n") );

break;

case SymVirtual:

_tprintf(_T("Symbol Type: Virtual module created by SymLoadModuleEx with SLMFLAG_VIRTUAL\n") );

break;

}

_tprintf(_T("Module Name : %s\n"), ImageModule.ModuleName);

_tprintf(_T("Image Name : %s\n"), ImageModule.ImageName);

_tprintf(_T("Loaded Image Name: %s\n"), ImageModule.LoadedImageName);

_tprintf(_T("Loaded PDB Name : %s\n"), ImageModule.LoadedPdbName);

_tprintf(_T("CV Signature : %d\n"), ImageModule.CVSig);

_tprintf(_T("CV Data : %s\n"), ImageModule.CVData);

_tprintf(_T("PDB Signature : %d\n"), ImageModule.PdbSig);

if( ImageModule.PdbSig70 != GUID_NULL )

{

WCHAR wcsBuffer[MAX_PATH];

if(0 == StringFromGUID2( ImageModule.PdbSig70,

wcsBuffer,

MAX_PATH))

{

_tprintf(_T("PDB Signature 7.0: Unknown GUID\n"));

}

else

{

_tprintf(_T("PDB Signature 7.0: %S\n"), wcsBuffer);

}

}

else

{

_tprintf(_T("PDB Signature 7.0: GUID_NULL \n"));

}

_tprintf(_T("DBI Age of PDB : %d\n"), ImageModule.PdbAge);

_tprintf(_T("Loaded PDB is unmatched : %s\n"), ImageModule.PdbUnmatched ? _T("Yes"):_T("No"));

_tprintf(_T("Loaded DBG is unmatched : %s\n"), ImageModule.DbgUnmatched ? _T("Yes"):_T("No"));

_tprintf(_T("Line number information is available: %s\n"), ImageModule.LineNumbers ? _T("Yes"):_T("No"));

_tprintf(_T("Symbol information is available : %s\n"), ImageModule.GlobalSymbols ? _T("Yes"):_T("No"));

_tprintf(_T("Type information is available : %s\n"), ImageModule.TypeInfo ? _T("Yes"):_T("No"));



//------------------------------------------------------------------------

// Enumerate symbols in the module

_tprintf(_T("Symbols List:\n"));

if(!SymEnumSymbols( GetCurrentProcess(),

dw64ModAddress,

NULL, // Null point out that list all symbols

SymEnumSymbolsProc,

NULL))

{

_tprintf( _T("Failed when SymEnumSymbols(): %d \n"), GetLastError() );

}



//------------------------------------------------------------------------

// Unload symbols table

if(!SymUnloadModule64( GetCurrentProcess(), dw64ModAddress ))

{

_tprintf( _T("Failed when SymUnloadModule64() : %d \n"), GetLastError() );

}



if(!SymCleanup(GetCurrentProcess()))

{

_tprintf(_T("Failed when SymCleanup(): %d \n"), GetLastError());

return 0;

}





return 0;



FAILED_PARAM:

_tprintf(_T("Failed Parameter!\n"));

_tprintf(_T("Usage: SymDump -filename\n"));

_tprintf(_T("filename: a EXE, DLL or PDB file\n"));

return 0;
}

III.
源码例子:显示EXE,DLL或PDB文件中指定函数的Parameter和local variables信息

By Hercules
From http://blog.vckbase.com/zyq654321/archive/2004/11/16/1550.html

上次写了列出EXE,DLL或PDB文件中的符号信息,这次再贴一段代码。举例说明如何列举一指定函数的局部信息,即函数的参数和局部变量信息。呵呵,本人即不爱说废话,又怕打字太累。^_^,开门见山,来看下面代码吧。Enjoy!

//============================================================================

// LFVars : A small tool used to display function symbol informaton from EXE, DLL or PDB files

// L(ist)F(untion)Vars: list function parameters and local variabls.

// Author : zyq654321 --- Oct, 2004

// Comment: This is only a sample code to show you how to dump symbol information,

// you can improve it and your advise is appreciated.

#include

#include

#define _NO_CVCONST_H // We should define the constant in order to ...

#include

#include



#pragma comment( lib, "dbghelp.lib" )



BOOL CALLBACK LFVarsCallback( SYMBOL_INFO* pSymInfo, ULONG SymbolSize, PVOID UserContext )

{

if( pSymInfo != 0 )

{

// Increase the counter of found local variables and parameters

if( UserContext != 0 )

*(int*)UserContext += 1;

// list parameters or variables

_tprintf(_T("Name: %s \n"),pSymInfo->Name);

_tprintf(_T(" Type: %s "),

(pSymInfo->Flags & SYMFLAG_PARAMETER) ? "Function Parameter" :

( (pSymInfo->Flags & SYMFLAG_LOCAL)? "Local Variable": "Unknown"));

TCHAR tcsReg[10];

switch(pSymInfo->Register)

{

case 17:

_tcscpy(tcsReg,_T("[EAX]"));

break;

case 18:

_tcscpy(tcsReg,_T("[ECX]"));

break;

case 19:

_tcscpy(tcsReg,_T("[EDX]"));

break;

case 20:

_tcscpy(tcsReg,_T("[EBX]"));

break;

case 21:

_tcscpy(tcsReg,_T("[ESP]"));

break;

case 22:

_tcscpy(tcsReg,_T("[EBP]"));

break;

case 23:

_tcscpy(tcsReg,_T("[ESI]"));

break;

case 24:

_tcscpy(tcsReg,_T("[EDI]"));

break;

default:

_tcscpy(tcsReg,_T("Unknown"));

break;

}

_tprintf( _T("Register: %s "), tcsReg );

UINT uMax = 0xFFFFFFFF;

_tprintf( _T("Address(Offset): %c0x%X "),

(LONG)pSymInfo->Address >= 0? ' ' : '-',

(LONG)pSymInfo->Address >= 0? pSymInfo->Address : (uMax - pSymInfo->Address + 1));

_tprintf( _T("Size: %d \n"), pSymInfo->Size);



//ShowSymbolDetails( *pSymInfo );

}



return TRUE; // Continue enumeration

}



int main( int argc, const TCHAR* argv[] )

{

if(argc < 3)

{

goto FAILED_PARAM;

}



// Set debug options

DWORD dwOpn = SymGetOptions();

dwOpn |= SYMOPT_DEBUG;

SymSetOptions(dwOpn);



// Initilaize the symbol handle for the current process

if(!SymInitialize( GetCurrentProcess(),

NULL,

FALSE ))

{

_tprintf(_T("Failed when SymInitialize():%d\n"), GetLastError());

return 0;

}



if( argv[1] == NULL || argv[2] == NULL)

{

goto FAILED_PARAM;

}



//------------------------------------------------------------------------

// Set initial parameters

TCHAR pszExt[MAX_PATH];

_tsplitpath( argv[1], NULL, NULL, NULL, pszExt );

DWORD64 dw64Base = 0; // if the image is a .pdb file, dw64Base cannot be zero.

// if the value is zero, the library obtains the load address

// from the symbol file.

DWORD dwFileSize = 0; // if the image is a .pdb file, dwFileSize cannot be zero.

// if the value is zero, the library obtains the size

// from the symbol file.



_tcslwr(pszExt);

if(_tcsicmp(pszExt, _T(".pdb")) == 0)

{

// this is a .pdb file, and so we should set the load address and file size;

dw64Base = 0x10000000;

// get the file size

HANDLE hFile = CreateFile( argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );

if( INVALID_HANDLE_VALUE == hFile )

{

_tprintf(_T("Failed when open %s: %d"), argv[1], GetLastError());

goto FAILED_PARAM;

}

if( INVALID_FILE_SIZE == ( dwFileSize = GetFileSize(hFile, NULL) ))

{

_tprintf(_T("Failed when read the size of %s: %d"), argv[1], GetLastError());

goto FAILED_PARAM;

}

CloseHandle(hFile);

}



//------------------------------------------------------------------------

// Load symbol table

_tprintf(_T("Load %s.....\n"), argv[1]);

DWORD64 dw64ModAddress = SymLoadModule64( GetCurrentProcess(),

NULL,

argv[1],

NULL,

dw64Base,

dwFileSize);



if( dw64ModAddress == 0 )

{

_tprintf(_T("Failed when SymLoadModule64(): %d \n"), GetLastError());

return 0;

}

_tprintf(_T("Load Address: %I64x \n"), dw64ModAddress);



//------------------------------------------------------------------------

// Display the function parameters and local variables according to the

// specified function name.

SYMBOL_INFO symInfo;

symInfo.SizeOfStruct = sizeof(SYMBOL_INFO);

if(! SymFromName( GetCurrentProcess(),

argv[2],

&symInfo) )

{

_tprintf( _T("Failed When SymFromName(): %d \n"), GetLastError() );

goto FAILED_AFTER_LOAD;

}



// We only need functon symbol

if( symInfo.Tag != SymTagFunction )

{

_tprintf( _T("Invalid Function Name or Not Found.\n") );

goto FAILED_AFTER_LOAD;

}



// List funtion's parameters and its local variables

IMAGEHLP_STACK_FRAME stackFrm;

stackFrm.InstructionOffset = symInfo.Address;

if(!SymSetContext( GetCurrentProcess(),

&stackFrm,

0 ))

{

_tprintf( _T("Failed when SymSetContext: %d \n"), GetLastError() );

goto FAILED_AFTER_LOAD;

}



int nVarsCnt = 0;

if(!SymEnumSymbols( GetCurrentProcess(),

0,

0,

LFVarsCallback,

&nVarsCnt ) )

{

_tprintf( _T("Failed when SymEnumSymbols(): %d \n"), GetLastError() );

goto FAILED_AFTER_LOAD;

}



_tprintf( _T("%d function parmeters and local variables had been listed.\n"), nVarsCnt );





FAILED_AFTER_LOAD:

//------------------------------------------------------------------------

// Unload symbols table

if(!SymUnloadModule64( GetCurrentProcess(), dw64ModAddress ))

{

_tprintf( _T("Failed when SymUnloadModule64() : %d \n"), GetLastError() );

}



if(!SymCleanup(GetCurrentProcess()))

{

_tprintf(_T("Failed when SymCleanup(): %d \n"), GetLastError());

return 0;

}





return 0;



FAILED_PARAM:

_tprintf(_T("Failed Parameter!\n"));

_tprintf(_T("Usage: LFVars -filename -function_name\n"));

_tprintf(_T("filename: a EXE, DLL or PDB file\n"));

return 0;
}

IV.
写个自己的调试器

By Hercules
From http://blog.vckbase.com/zyq654321/archive/2005/07/02/9082.html

对于写了一段时间的程序员来说,了解一些debugger的实质无疑对于技术的提高是有很大帮助的。而debugging自身也是一门非常细节化,比较复杂的技术。好的Debug工具如SoftICE,也是技术稍深一些的程序员必备的技术之一。这篇随笔并不会去讨论Debug技术的实质,而只是利用Platform SDK和最新的DbgHelp.dll提供的API作为引擎写一个自己的debugger,也即是写一个实用的debugger的Host端。呵呵,本人不擅于写文章,所以下面就指一个例子来说,即如何给一个进程设下断点并捕获。其实看过IA32的人都知道,CPU为Debug提供了强力的支持,比如设单步跟踪的标志;再比如BIOS代码的调试,对于ROM的BIOS代码是无法进行插CC(int 3)指令进行断点设置的,这时CPU提供了地址中断,执行至指定地址时,CPU自行中断。在这里并不会提及这些内容,当然也许有时间,或大家感兴趣,我可以写点或讨论一二。下面单就如何给进行设断点来说说,即然是随笔,所以可能会有错别字,欢迎大家来找别字 ^_^,

调试目标程序时,你得有相应的权限。用下面这个函数得到它:

//--------------------------------------------------------------------------

// EnableDebugAccessCtl

// Description: Enable or disable debug access control

// Return value: 0 means succeed

DWORD EnableDebugAccessCtl(BOOL bEnable)

{

HANDLE hTokenHandle = NULL;

if( !OpenProcessToken(GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES,

&hTokenHandle ) )

{

return GetLastError();

}

// Lookup the privilege value

TOKEN_PRIVILEGES tp;

tp.PrivilegeCount = 1;

if( !LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid ) )

{

CloseHandle(hTokenHandle);

return -1;

}

// Enable/disable the privilege

tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;

if( !AdjustTokenPrivileges( hTokenHandle, FALSE, &tp, sizeof(tp), NULL, NULL ) )

{

CloseHandle(hTokenHandle);

return -1;

}



CloseHandle(hTokenHandle);

return 0;

}

我们可以attach一个正在运行的进行或重新启动一个进程进行调试。DebugActiveProcess可以实现第一种情况;第二种情况可以用CreateProcess,下面是代码,注意创建标志。

// Create Process

STARTUPINFO si;

PROCESS_INFORMATION pi;

ZeroMemory( &si, sizeof(si) );

si.cb = sizeof(si);

ZeroMemory( &pi, sizeof(pi) );

if( !CreateProcess(NULL, csCmdParms.GetBuffer(),

NULL, NULL, FALSE,

CREATE_NEW_CONSOLE | DEBUG_ONLY_THIS_PROCESS,

NULL, NULL,

&si, &pi ) )

{

return GetLastError();

}



这下初调试的程序就跑起来了,接下来一般来用一个循环来获取目标进行的Debug事件。主循环看起来应该是这样的,嘿,MSDN给了你一个模板,那就用吧:

DEBUG_EVENT DebugEv; // debugging event information DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
for(;;)
{
// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.
WaitForDebugEvent(&DebugEv, INFINITE);
// Process the debugging event code.
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.
switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_SINGLE_STEP:
// First chance: Update the display of the
// current instruction and register values.
break;
case DBG_CONTROL_C:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
default;
// Handle other exceptions.
break;
}
case CREATE_THREAD_DEBUG_EVENT:
// As needed, examine or change the thread's registers
// with the GetThreadContext and SetThreadContext functions;
// and suspend and resume thread execution with the
// SuspendThread and ResumeThread functions.
break;
case CREATE_PROCESS_DEBUG_EVENT:
// As needed, examine or change the registers of the
// process's initial thread with the GetThreadContext and
// SetThreadContext functions; read from and write to the
// process's virtual memory with the ReadProcessMemory and
// WriteProcessMemory functions; and suspend and resume
// thread execution with the SuspendThread and ResumeThread
// functions. Be sure to close the handle to the process image
// file with CloseHandle.
break;
case EXIT_THREAD_DEBUG_EVENT:
// Display the thread's exit code.
break;
case EXIT_PROCESS_DEBUG_EVENT:
// Display the process's exit code.
break;
case LOAD_DLL_DEBUG_EVENT:
// Read the debugging information included in the newly
// loaded DLL. Be sure to close the handle to the loaded DLL
// with CloseHandle.
break;
case UNLOAD_DLL_DEBUG_EVENT:
// Display a message that the DLL has been unloaded.
break;
case OUTPUT_DEBUG_STRING_EVENT:
// Display the output debugging string.
break;
}

// Resume executing the thread that reported the debugging event.
ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus);
}
可以从CASE语句中看到可以获取什么样的事件。注意CREATE_PROCESS_DEBUG_EVENT事件,我们设置断点是在这里,也就是进行被创建的时候。

说到这里就要讲讲断点是怎么设置的了。通常我们设置的断点称为软件断点。通俗点说这个断点其实并不是在原代码中写的,它的基本工作是:调试器跟据用户的设置对代码进行了修改,也即是在指地址处更换一个字节的值为CC,当程序运行到用户指定地址时,CC被处理器装入执行,因为CC对应用int 3指令,这是处理器的一个断点异常,于是处理暂停当前程序,并发出相应异常,通过异常处理程序和debug驱动程序,调试器Host端得到这个事件,此时用户就可以对被debugging的程序进行一些访问了,比如寄存器,局部变量等各种信息,可以对目标程序的指定代码块进行反汇编或进行源码级的查看。

注意:这里指出的是当程序被断点所中断时,调试器获得此断点事件后第一件是要将有被改写为CC的内存单元重新改会为原来程序的代码,以保证程序的完整性。另一点提示,被捕获的第一个断点异常应该被丢掉,呵呵,那不关你的事,它是规定的一个步骤,当然,你是可以用它做点什么事的。

当然继续调试的时候,目标程序会根据EIP来执行下一条指令,而这时EIP实际指向的是CC后的位置,因此必须令EIP指针值减1,这样程序就对用户实现了透明,用户是不知道调试器作了什么手脚的,当然别忘,程序继续跑时,其实的断点还要改成CC的。

写目标进程可以用WriteProcessMemory进行,写完后别忘了用FlushInstructionCache指令flush一下目标进行的指令的cache。如果页为只读时,可以使用VirtualProtectEx改其为读写属性然后再写。

修改目标线程EIP的值可以用SetThreadContext,当然EIP的值请先用GetThreadContext来获得。啊,这样就可以了。好热,写了40多分钟......要吃冰棒去了。



<< Home

This page is powered by Blogger. Isn't yours?