#include <ntifs.h> LARGE_INTEGER Cookie; /* 功能:打印到现在为止的函数调用的栈帧的信息(栈回溯),而不是最终的函数调用栈帧的信息。 记得以前有个函数,可以打印函数调用堆栈的信息,如果有符号文件还可以显示更多的信息。 当时觉得没有用,一心想解析符号文件的信息。 现在想来,即使没有符号文件及相关的信息,有这些调用堆栈的信息,对开发这和解决问题也是有用的。 不能光想着符号文件。没有符号文件分析程序更加锻炼人的能力。 此文收集并简单测试一些信息。 更多的API如下: IoGetStackLimits IoGetInitialStack IoGetRemainingStackSize IoWithinStackLimits KeExpandKernelStackAndCallout KeSetKernelStackSwapEnable http://www.cnblogs.com/welfear/archive/2010/11/16/1878503.html http://blog.csdn.net/cosmoslife/article/details/7818841 http://blogs.msdn.com/b/vcblog/archive/2014/01/23/examining-stack-traces-of-objects-using-visual-studio-2013.aspx CaptureStackBackTrace RtlCaptureStackBackTrace RtlCaptureStackContext RtlGetCallerAddress XP 已经导出。 RtlWalkFrameChain http://msdn.microsoft.com/zh-cn/office/ff563638(v=vs.100).aspx KeExpandKernelStackAndCallout made by correy made at 2014.08.05 homepage:http://correy.webs.com */ void get_open_information_from_stack_in_32() /* 相信也可以处理64位系统的,尽管它的参数的调用方式特殊,但参数也是存在栈上的。 */ { /* The RtlCaptureStackBackTrace routine captures a stack back trace by walking up the stack and recording the information for each frame. Important This is an exported function that MUST probe the ability to take page faults. 所以:IRQL: <= DISPATCH_LEVEL Requirements Versions: Available in Windows XP and later versions of the Windows operating systems. */ /* 此时栈的信息类似如下: 3: kd> k ChildEBP RetAddr efaf6964 f8870132 test!get_open_information_from_stack_in_32+0x8 [e:\code\driver\test\test.c @ 20] efaf6970 f887005d test!post_open_key+0x12 [e:\code\driver\test\test.c @ 58] efaf6980 8056bdd4 test!RegistryCallback+0x1d [e:\code\driver\test\test.c @ 74] efaf69b4 806312bd nt!CmpCallCallBacks+0x50 efaf6b88 805c0099 nt!CmpParseKey+0x7b9 efaf6c00 805bca48 nt!ObpLookupObjectName+0x119 efaf6c54 80626810 nt!ObOpenObjectByName+0xea efaf6d50 805427e8 nt!NtOpenKey+0x1c8 efaf6d50 7c92e514 nt!KiSystemServicePostCall 0168f700 7c92d5da ntdll!KiFastSystemCallRet 0168f7e8 7c93019b ntdll!ZwOpenKey+0xc 0168fa14 00000000 ntdll!RtlAllocateHeap+0x1c2 在这种情况下是一定能找到:nt!NtOpenKey函数的地址的。 */ PVOID pbacts = IoGetInitialStack();//returns the base address of the current thread's stack. /* 上面一行运行的结果如下: 3: kd> r eax=efaf7000 ebx=8055b3a0 ecx=efaf6b44 edx=e1a02d90 esi=e1a02d88 edi=e1cf5508 eip=f88700b1 esp=efaf6948 ebp=efaf6964 iopl=0 nv up ei ng nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000296 test!get_open_information_from_stack_in_32+0x11: f88700b1 33c0 xor eax,eax 3: kd> dd efaf6948 efaf6948 efaf7000 00000001 805f2501 80630424 efaf6958 00000000 e16d2518 e1d1ab98 efaf6970 efaf6968 f8870132 00000000 efaf6980 f887005d efaf6978 efaf6b44 0000000d efaf69b4 8056bdd4 efaf6988 00000000 0000000d efaf6b44 00000000 efaf6998 e16d2518 e1d1ab98 e1a02d90 e1a02d90 efaf69a8 81ed9da0 8055b214 00000000 efaf6b88 efaf69b8 806312bd 0000000d efaf6b44 e1bb5558 3: kd> dd pbacts efaf6948 efaf7000 00000001 805f2501 80630424 efaf6958 00000000 e16d2518 e1d1ab98 efaf6970 efaf6968 f8870132 00000000 efaf6980 f887005d efaf6978 efaf6b44 0000000d efaf69b4 8056bdd4 efaf6988 00000000 0000000d efaf6b44 00000000 efaf6998 e16d2518 e1d1ab98 e1a02d90 e1a02d90 efaf69a8 81ed9da0 8055b214 00000000 efaf6b88 efaf69b8 806312bd 0000000d efaf6b44 e1bb5558 可见IoGetInitialStack的内容和栈指针(这里是ESP)的内容的内容是一样的。 */ USHORT ncf = 0;//The number of captured frames USHORT i = 0; //In Windows XP and Windows Server 2003, the sum of the FramesToSkip and FramesToCapture parameters must be less than 63. ULONG FramesToSkip = 0;//The number of frames to skip from the start of the back trace. ULONG FramesToCapture = 62;//The number of frames to be captured. PVOID * BackTrace = 0;//An array of pointers captured from the current stack trace. PVOID * pBackTrace = 0; /*An optional value that can be used to organize hash tables. If this parameter is NULL, no hash value is computed. This value is calculated based on the values of the pointers returned in the BackTrace array. Two identical stack traces will generate identical hash values. */ ULONG BackTraceHash = 0; /* http://msdn.microsoft.com/zh-cn/office/ff563638(v=vs.100).aspx RtlGetCallersAddress. Use the intrinsic _ReturnAddress instead. 就是本函数的返回地址,也就是调用本函数的下一个指令的地址。 在调试器里面就是Retaddr。 */ PVOID CallersAddress = _ReturnAddress(); //http://msdn.microsoft.com/zh-cn/library/windows/hardware/Dn613940(v=vs.85).aspx int stacklength = 32 * 1024;//取X86/X64/IA64的栈的最大值。 UNICODE_STRING NtOpenKey = RTL_CONSTANT_STRING(L"NtOpenKey"); PVOID p_NtOpenKey = MmGetSystemRoutineAddress(&NtOpenKey);//此函数没有导出。获取的值恒为0. ULONG_PTR LowLimit = 0;//Pointer to a caller-supplied variable in which this routine returns the lower offset of the current thread's stack frame. ULONG_PTR HighLimit = 0;//Pointer to a caller-supplied variable in which this routine returns the higher offset of the current thread's stack frame. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //下面开始代码。 IoGetStackLimits(&LowLimit, &HighLimit); BackTrace = (PVOID *)ExAllocatePool(NonPagedPool, stacklength); if (BackTrace == NULL) { return ; } RtlZeroMemory(BackTrace, stacklength); /* BackTrace的地址一定在LowLimit和HighLimit之间,这是废话。 */ ncf = RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, BackTrace, &BackTraceHash); /* 这行代码运行后的效果类似为: 3: kd> dps esp efaf6948 efaf7000 efaf694c 00000000 efaf6950 0000003e efaf6954 ebbd2e00 efaf6958 00009000 efaf695c 82161000 efaf6960 e1d10009 efaf6964 efaf6970 efaf6968 f8870132 test!post_open_key+0x12 [e:\code\driver\test\test.c @ 58] efaf696c 00000000 efaf6970 efaf6980 efaf6974 f887005d test!RegistryCallback+0x1d [e:\code\driver\test\test.c @ 74] efaf6978 efaf6b44 efaf697c 0000000d efaf6980 efaf69b4 efaf6984 8056bdd4 nt!CmpCallCallBacks+0x50 efaf6988 00000000 efaf698c 0000000d efaf6990 efaf6b44 efaf6994 00000000 efaf6998 e16d2518 efaf699c e1d1ab98 efaf69a0 e1a02d90 efaf69a4 e1a02d90 efaf69a8 81ed9da0 efaf69ac 8055b214 nt!CmpRegistryLock+0x34 efaf69b0 00000000 efaf69b4 efaf6b88 efaf69b8 806312bd nt!CmpParseKey+0x7b9 efaf69bc 0000000d efaf69c0 efaf6b44 efaf69c4 e1bb5558 3: kd> dps BackTrace efaf695c 82161000 efaf6960 e1d10009 efaf6964 efaf6970 efaf6968 f8870132 test!post_open_key+0x12 [e:\code\driver\test\test.c @ 58] efaf696c 00000000 efaf6970 efaf6980 efaf6974 f887005d test!RegistryCallback+0x1d [e:\code\driver\test\test.c @ 74] efaf6978 efaf6b44 efaf697c 0000000d efaf6980 efaf69b4 efaf6984 8056bdd4 nt!CmpCallCallBacks+0x50 efaf6988 00000000 efaf698c 0000000d efaf6990 efaf6b44 efaf6994 00000000 efaf6998 e16d2518 efaf699c e1d1ab98 efaf69a0 e1a02d90 efaf69a4 e1a02d90 efaf69a8 81ed9da0 efaf69ac 8055b214 nt!CmpRegistryLock+0x34 efaf69b0 00000000 efaf69b4 efaf6b88 efaf69b8 806312bd nt!CmpParseKey+0x7b9 efaf69bc 0000000d efaf69c0 efaf6b44 efaf69c4 e1bb5558 efaf69c8 00000000 efaf69cc efaf6c40 efaf69d0 e63561d1 efaf69d4 81220010 efaf69d8 e1b9fb18 可见这个函数的栈顶少几个,但是不影响查找以前的函数和参数。 多几个的原因可能是又调用函数了或者局部变量的初始化。 BackTrace的这段内容就是函数的调用关系。 */ /* 这里打印的信息类似于WINDBG的K命令,但是信息不多。 */ for (pBackTrace = BackTrace;i < ncf;pBackTrace++,i++ ) { //if (*pBackTrace == p_NtOpenKey)//ZwOpenKey可以直接使用。 //{ // break; //} KdPrint(("%d: %p \n", i, *pBackTrace)); } /* 要想获取某个函数的参数信息 还得用IoGetStackLimits(&LowLimit, &HighLimit)的两个参数的值。 在那个范围内搜索。 */ /* RtlWalkFrameChain和RtlCaptureStackBackTrace功能相似。 RtlCaptureStackBackTrace封装了RtlWalkFrameChain(见WDK)。 只不过: 1.RtlWalkFrameChain导出(且头文件有函数申明)但是没有公开。 2.RtlWalkFrameChain还可以获取包括(加上)用户态栈帧的数目。 3.#pragma optimize( "y", off ) // disable FPO */ //ncf = (USHORT)RtlWalkFrameChain (BackTrace, FramesToCapture, 0); //ncf = (USHORT)RtlWalkFrameChain (BackTrace, FramesToCapture, 1); ExFreePool(BackTrace); } NTSTATUS post_open_key(__in_opt PVOID Argument2) { unsigned int status = STATUS_SUCCESS; get_open_information_from_stack_in_32(); return status; } EX_CALLBACK_FUNCTION RegistryCallback; NTSTATUS RegistryCallback(__in PVOID CallbackContext, __in_opt PVOID Argument1, __in_opt PVOID Argument2) { switch((int)Argument1) { case RegNtPostOpenKey:// == 13 仅仅XP收到此消息. post_open_key(Argument2); break; default: break; } return STATUS_SUCCESS; } DRIVER_UNLOAD Unload; VOID Unload(__in PDRIVER_OBJECT DriverObject) { CmUnRegisterCallback(Cookie); } #pragma INITCODE DRIVER_INITIALIZE DriverEntry; NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath) { KdBreakPoint(); DriverObject->DriverUnload = Unload; return CmRegisterCallback(RegistryCallback, NULL, &Cookie); }
2014年8月5日星期二
打印栈帧的一点信息
订阅:
博文评论 (Atom)
dds addr即可,或者kv也行
回复删除