最近突发兴趣想写一个简单的win32 程序 调试器。
看了相关资料,把程序写出来了,但是郁闷的是:导入被调试程序后,当被调试程序发生异常时,调试器却拦截不下来?
捣鼓了一整天实在想不出来是什么原因,遂发帖向csdn众高手求教。
好,现在附上源代码:
OS:windows xp sp3
开发工具:VC 2008
/*
    Crash - Process Instrumentor
    Copyright (C) 2005 Pedram Amini <[email protected],[email protected]>    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the Free
    Software Foundation; either version 2 of the License, or (at your option)
    any later version.    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    more details.    You should have received a copy of the GNU General Public License along with
    this program; if not, write to the Free Software Foundation, Inc., 59 Temple
    Place, Suite 330, Boston, MA 02111-1307 USA    Return Codes:
        -1   - An error occured during the process instrumentation.
         0   - Process exited normally.
         1   - Process generated exception.*/#include <windows.h>
#include <stdio.h>
#include <stdlib.h>#include "libdasm.h"int main (int argc, char **argv)
{
    PROCESS_INFORMATION pi;
    INSTRUCTION         inst;
    STARTUPINFO         si;
    DEBUG_EVENT         dbg;
    CONTEXT             context;
    HANDLE              thread;
    HANDLE              process;
    DWORD               wait_time;
    DWORD               start_time;
    BOOL                ret;
BOOL exception;
// BOOL continueDebug;
    u_char              inst_buf[32];
    char                inst_string[256];
    char                command_line[32768];
    int                 i; DWORD   ProcessID;
    //
    // variable initialization.
    //    memset(&pi, 0, sizeof(pi));
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);    memset(command_line, 0, sizeof(command_line));
    memset(inst_buf,     0, sizeof(inst_buf));    start_time = GetTickCount();
exception  = FALSE;    //
    // command line processing.
    //    // minimum arg check.
/*
    if (argc < 4)
    {
        fprintf(stderr, "[!] Usage: crash <path to app> <milliseconds> <arg1> [arg2 arg3 ... argn]\n\n");
        return -1;
    } */    // convert wait time from string to integer.
/*
    if ((wait_time = atoi(argv[2])) == 0)
    {
        fprintf(stderr, "[!] Milliseconds argument unrecognized: %s\n\n", argv[2]);
        return -1;
    }
*/    // create the command line string for the call to CreateProcess().
    strcpy(command_line, argv[1]);    for (i = 2; i < argc; i++)
    {
        strcat(command_line, " ");
        strcat(command_line, argv[i]);
    }    //
    // launch the target process.
    // ret = CreateProcess(NULL,       // target file name.
command_line,               // command line options.
        NULL,                       // process attributes.
        NULL,                       // thread attributes.
        FALSE,                      // handles are not inherited.
        DEBUG_PROCESS,              // debug the target process and all spawned children.        NULL,                       // use our current environment.
        NULL,                       // use our current working directory.
        &si,                        // pointer to STARTUPINFO structure.
        &pi);                       // pointer to PROCESS_INFORMATION structure. ProcessID=pi.dwProcessId;   
WaitForSingleObject(ProcessID,3000);
//ret=WinExec(command_line,SW_SHOW);
    printf("[*] %s\n", GetCommandLine());  //Print the command line
if (!ret)
    {
        fprintf(stderr, "[!] CreateProcess() failed: %d\n\n", GetLastError());
        return -1;
    }    //
    // watch for an exception.
    //

DebugActiveProcess(ProcessID);
    while (0< 2000)
    {
        if (WaitForDebugEvent(&dbg, 1000))
        {
            // we are only interested in debug events.
            if (dbg.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
            {
                ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, DBG_CONTINUE);
                continue;
            }            // get a handle to the offending thread.
            if ((thread = OpenThread(THREAD_GET_CONTEXT, FALSE, dbg.dwThreadId)) == NULL)
            {
                fprintf(stderr, "[!] OpenThread() failed: %d\n\n", GetLastError());
                return -1;
            }
context.ContextFlags=CONTEXT_FULL;
            // get the context of the offending thread.
                   if (GetThreadContext(thread, &context) == 0)
            {
                fprintf(stderr, "[!] GetThreadContext() failed: %d\n\n", GetLastError());
                return -1;
            }           // examine the exception code.
            switch (dbg.u.Exception.ExceptionRecord.ExceptionCode)
            {
                case EXCEPTION_ACCESS_VIOLATION:
exception = TRUE;
printf("[*] Access Violation\n");
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
exception = TRUE;
printf("[*] Divide by Zero\n");
break;
                case EXCEPTION_STACK_OVERFLOW:
                    exception = TRUE;
printf("[*] Stack Overflow\n");
break;
                default:
//printf("[*] Unknown Exception (%08x):\n", dbg.u.Exception.ExceptionRecord.ExceptionCode);
                    ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, DBG_CONTINUE);
            } SuspendThread(pi.dwProcessId); // if an exception occured, print more information.
if (1)
{
// open a handle to the target process.
if ((process = OpenProcess(PROCESS_VM_READ, FALSE, dbg.dwProcessId)) == NULL)
{
fprintf(stderr, "[!] OpenProcess() failed: %d\n\n", GetLastError());
return -1;
} // grab some memory at EIP for disassembly.
ReadProcessMemory(process, (void *)context.Eip, &inst_buf, 32, NULL); // decode the instruction into a string.
get_instruction(&inst, inst_buf, MODE_32);
get_instruction_string(&inst, FORMAT_INTEL, 0, inst_string, sizeof(inst_string)); // print the exception to screen.
printf("[*] Exception caught at %08x %s\n", context.Eip, inst_string);
printf("[*] EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", context.Eax, context.Ebx, context.Ecx, context.Edx);
printf("[*] ESI:%08x EDI:%08x ESP:%08x EBP:%08x\n\n", context.Esi, context.Edi, context.Esp, context.Ebp);

return 1;
}        }
}
    //
    // done.
    //    printf("[*] Process terminated normally.\n\n");
    return 0;
}

解决方案 »

  1.   

    WaitForSingleObject(ProcessID,3000); 
    DebugActiveProcess(ProcessID); 
    这两行代码是多余的,去掉再看看。
      

  2.   

    不对吧?看LZ的意思是想创建个进程,然后进行调试,怎么能吧DebugActiveProcess去掉呢?感觉楼主写的死循环很奇怪。出什么错?
      

  3.   

    对,哥们,是死循环,主要是为了调试方便。问题的关键是:被调试的程序本来有问题,要异常的(单独运行)。但是如果在此程序调试,却安全返回。我在怀疑,进程是建立了,是不是没有启动,运行。是不是?
    还有,如果我把createprocess的参数改为:DEBUG_PROCESS改为:CREATE_NO_WINDOW,被调试程序会异常(弹出windows 默认异常处理对话框)。但是crash.exe,还是不能捕捉到异常。这到底是怎么回事呢?
      

  4.   

    现在的问题是:如果用这个crash.exe启动一个被调试程序,根本捕捉不到异常?
      

  5.   

    另外我把libdasm.h和libdasm.c放上来,和crash.c一起,在VC2008下建立一个工程,有兴趣的朋友可以试试。
    只有链接,还烦请大家自己下载;
    libdasm.h:
    http://libemu.carnivore.it/doxygen/html/libdasm_8h-source.html
    libdasm.c:
    http://libemu.carnivore.it/doxygen/html/libdasm_8c-source.html
      

  6.   

    CreateProcess指定了DEBUG_PROCESS标志就表示对该进程进行调试,再DebugActiveProcess就重复了。
      

  7.   

    确认一下是否走到switch (dbg.u.Exception.ExceptionRecord.ExceptionCode) 这步。
    管理员权限下,debug特权默认是disable的,需要你自己在代码里提权。
      

  8.   

    调试部分没有问题,我写了一个触发 EXCEPTION_INT_DIVIDE_BY_ZERO 的程序, 用这段代码调试,成功捕获住了。
      

  9.   

    提权后,我还是没有测试成功,难道是我提权代码有问题:
    int EnableDebugPriv(const char * name)
    {
        HANDLE hToken;
        TOKEN_PRIVILEGES tp;
        LUID luid;    //打开进程令牌环
        OpenProcessToken(GetCurrentProcess(),
                        TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
                        &hToken);
        //获得进程本地唯一ID
        LookupPrivilegeValue(NULL,name,&luid);
         
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        tp.Privileges[0].Luid = luid;
        //调整权限
        AdjustTokenPrivileges(hToken,0,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL); CloseHandle( hToken ); 
        return 0;
    }
      

  10.   

    提权后,我还是没有测试成功,难道是我提权代码有问题:
    int EnableDebugPriv(const char * name)
    {
        HANDLE hToken;
        TOKEN_PRIVILEGES tp;
        LUID luid;    //打开进程令牌环
        OpenProcessToken(GetCurrentProcess(),
                        TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
                        &hToken);
        //获得进程本地唯一ID
        LookupPrivilegeValue(NULL,name,&luid);
         
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        tp.Privileges[0].Luid = luid;
        //调整权限
        AdjustTokenPrivileges(hToken,0,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL); CloseHandle( hToken ); 
        return 0;
    }
      

  11.   

    HANDLE hToken; 
    TOKEN_PRIVILEGES tkp; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid); tkp.PrivilegeCount = 1;  
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
    AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0); 
    CloseHandle( hToken ); 这个提权代码仅仅是你调试运行在其他帐号下的程序才需要。
      

  12.   


    // Debuger.cpp : Defines the entry point for the console application.
    //#include "stdafx.h"//#include "libdasm.h" int main (int argc, char **argv) 

        PROCESS_INFORMATION pi; 
        //INSTRUCTION inst; 
        STARTUPINFO si; 
        DEBUG_EVENT dbg; 
        CONTEXT context; 
        HANDLE              thread; 
        HANDLE              process; 
        DWORD wait_time; 
        DWORD start_time; 
        BOOL                ret; 
    BOOL exception; 
    // BOOL continueDebug; 
        u_char              inst_buf[32]; 
        char                inst_string[256]; 
        char                command_line[32768]; 
        int i;  DWORD  ProcessID; 
        // 
        // variable initialization. 
        //     memset(&pi, 0, sizeof(pi)); 
        memset(&si, 0, sizeof(si)); 
        si.cb = sizeof(si);     memset(command_line, 0, sizeof(command_line)); 
        memset(inst_buf,    0, sizeof(inst_buf));     start_time = GetTickCount(); 
    exception  = FALSE;     // 
        // command line processing. 
        //     // minimum arg check. 
    /* 
        if (argc < 4) 
        { 
            fprintf(stderr, "[!] Usage: crash <path to app> <milliseconds> <arg1> [arg2 arg3 ... argn]\n\n"); 
            return -1; 
        } */     // convert wait time from string to integer. 
    /* 
        if ((wait_time = atoi(argv[2])) == 0) 
        { 
            fprintf(stderr, "[!] Milliseconds argument unrecognized: %s\n\n", argv[2]); 
            return -1; 
        } 
    */     // create the command line string for the call to CreateProcess(). 
        strcpy(command_line, argv[1]);     for (i = 2; i < argc; i++) 
        { 
            strcat(command_line, " "); 
            strcat(command_line, argv[i]); 
        }     // 
        // launch the target process. 
        //  ret = CreateProcess(NULL, // target file name. 
    command_line, // command line options. 
    NULL, // process attributes. 
    NULL, // thread attributes. 
    FALSE, // handles are not inherited. 
    DEBUG_PROCESS, // debug the target process and all spawned children. 
    NULL, // use our current environment. 
    NULL, // use our current working directory. 
    &si, // pointer to STARTUPINFO structure. 
    &pi); // pointer to PROCESS_INFORMATION structure. 
    //ProcessID=pi.dwProcessId;
    //WaitForSingleObject(ProcessID,3000); 
    WaitForSingleObject(pi.hProcess,3000); 
    //ret=WinExec(command_line,SW_SHOW); 
        printf("[*] %s\n", GetCommandLine());  //Print the command line 
    if (!ret) 
        { 
            fprintf(stderr, "[!] CreateProcess() failed: %d\n\n", GetLastError()); 
            return -1; 
        }     // 
        // watch for an exception. 
        //  //DebugActiveProcess(ProcessID); 
    DebugActiveProcess(pi.dwProcessId); 
        while (0 < 2000) 
        { 
            if (WaitForDebugEvent(&dbg, 1000)) 
            { 
                // we are only interested in debug events. 
                if (dbg.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) 
                { 
                    ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, DBG_CONTINUE); 
                    continue; 
                }             // get a handle to the offending thread. 
                if ((thread = OpenThread(THREAD_GET_CONTEXT, FALSE, dbg.dwThreadId)) == NULL) 
                { 
                    fprintf(stderr, "[!] OpenThread() failed: %d\n\n", GetLastError()); 
                    return -1; 
                } 
    context.ContextFlags=CONTEXT_FULL; 
                // get the context of the offending thread. 
                      if (GetThreadContext(thread, &context) == 0) 
                { 
                    fprintf(stderr, "[!] GetThreadContext() failed: %d\n\n", GetLastError()); 
                    return -1; 
                }           // examine the exception code. 
                switch (dbg.u.Exception.ExceptionRecord.ExceptionCode) 
                { 
                    case EXCEPTION_ACCESS_VIOLATION: 
    exception = TRUE; 
    printf("[*] Access Violation\n"); 
    break; 
    case EXCEPTION_INT_DIVIDE_BY_ZERO: 
    exception = TRUE; 
    printf("[*] Divide by Zero\n"); 
    break; 
    case EXCEPTION_STACK_OVERFLOW: 
    exception = TRUE; 
    printf("[*] Stack Overflow\n"); 
    break; 
                    default: 
    //printf("[*] Unknown Exception (%08x):\n", dbg.u.Exception.ExceptionRecord.ExceptionCode); 
                        ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, DBG_CONTINUE); 
                }  //SuspendThread(pi.dwProcessId); 
    //SuspendThread(pi.hThread);  // if an exception occured, print more information. 
    if (1) 

    // open a handle to the target process. 
    if ((process = OpenProcess(PROCESS_VM_READ, FALSE, dbg.dwProcessId)) == NULL) 

    fprintf(stderr, "[!] OpenProcess() failed: %d\n\n", GetLastError()); 
    return -1; 
    }  // grab some memory at EIP for disassembly. 
    ReadProcessMemory(process, (void *)context.Eip, &inst_buf, 32, NULL);  // decode the instruction into a string. 
    //get_instruction(&inst, inst_buf, MODE_32); 
    //get_instruction_string(&inst, FORMAT_INTEL, 0, inst_string, sizeof(inst_string));  // print the exception to screen. 
    //printf("[*] Exception caught at %08x %s\n", context.Eip, inst_string); 
    //printf("[*] EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", context.Eax, context.Ebx, context.Ecx, context.Edx); 
    //printf("[*] ESI:%08x EDI:%08x ESP:%08x EBP:%08x\n\n", context.Esi, context.Edi, context.Esp, context.Ebp);  //return 1; 

            } 

        // 
        // done. 
        //     printf("[*] Process terminated normally.\n\n"); 
        return 0; 
    }这个是我在你的代码基础上做出修改的,可以正常捕获到异常的代码。
      

  13.   

    WaitForDebugEvent 这个是正常返回么?
    单步走走看看。
      

  14.   

    是不是忘了 ContinueDebugEvent?