How to obtain both managed(dotnet) and native(C++) callstack of my project by dbghelp?

85 views Asked by At

My project contains managed part(dotnet) and native(C++) part. I want to hook Winapi CreateFiles in my project by detours and log callstack. Now I have obtained a native callstack by invoking CaptureStackBackTrace and SymFromAddr api in dbghelp. But I meet some troubles when those functions are used in dotnet. as shown in the following picture, it can obtain correct callstack of native code(22, 21, 1, 0) but not work for managed code(20-2). may I have some methods to obtain both managed(dotnet) and un-managed(C++) callstack correctly? or maybe you have the best method to get callstack, could you help me?

dotnet produce:

public class CreateFileOrFolder
{
    public void writeToFile(string pathString) {
        System.IO.File.Create(pathString);
    }

    static void Main()
    {
        System.Console.WriteLine("Press any key to start.\n");
        System.Console.ReadKey();
    
        string fileName = "c.txt";
        string pathString = System.IO.Path.Combine(@"D:\JunFiles\testDotNetProceduce", fileName);

        // Verify the path that you have constructed.
        Console.WriteLine("Path to my file: {0}\n", pathString);
        CreateFileOrFolder creater = new CreateFileOrFolder();
        if (!System.IO.File.Exists(pathString)) {
            creater.writeToFile(pathString);
        }
        else
        {
            System.IO.File.Delete(pathString);
            Console.WriteLine("File \"{0}\" already exists. but now it is deleted\n", fileName);
            creater.writeToFile(pathString);
        }
    }

}

detours hook function:

HANDLE(*__stdcall oldCreateFile)(LPCWSTR,
    DWORD,
    DWORD,
    LPSECURITY_ATTRIBUTES,
    DWORD,
    DWORD,
    HANDLE) = CreateFileW;

HANDLE WINAPI newCreateFile(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
) {

    static BOOL state = FALSE;
    printStack();
    symbolSolve(process);
    printf("HOOK sucess!\n");
    std::cout << current_working_directory() << endl;
    
    wchar_t newFileName[] = L".\\newFile.txt";
    return oldCreateFile(
        newFileName, // L".\\NewFile.txt",     // Filename
        //lpFileName,
        dwDesiredAccess,          // Desired access
        dwShareMode,        // Share mode
        lpSecurityAttributes,                   // Security attributes
        dwCreationDisposition,             // Creates a new file, only if it doesn't already exist
        dwFlagsAndAttributes,  // Flags and attributes
        NULL);
}

get callstack:

#define STACK_INFO_LEN  200
static HANDLE process;
unsigned int   i;
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
std::queue<std::pair<void*, unsigned short>> myQueue;

PDWORD hashValue = (PDWORD)malloc(sizeof(DWORD));

void printStack(void)
{
    frames = CaptureStackBackTrace(0, 100, stack, hashValue);
    void* stackTmp = stack;
}

DWORD WINAPI symbolSolve(LPVOID p) {
    int myqueue_size = myQueue.size();
    char* szBriefInfo = NULL;
    static const int MAX_STACK_FRAMES = 12;
    void* pStack[MAX_STACK_FRAMES];
    static char szStackInfo[STACK_INFO_LEN * MAX_STACK_FRAMES];
    static char szFrameInfo[STACK_INFO_LEN];
    if (szBriefInfo == NULL) {
        strcpy_s(szStackInfo, "stack traceback:\n");
    }
    else {
        strcpy_s(szStackInfo, szBriefInfo);
    }

    symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    symbol->MaxNameLen = 255;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    IMAGEHLP_LINE line;
    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    DWORD displacementLine = 0;
    std::string strs = "";
    strs += std::to_string(*hashValue);
    for (i = 0; i < frames; i++) {
        SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
        BOOL ret = SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &displacementLine, &line);
        _snprintf_s(szFrameInfo, sizeof(szFrameInfo), "%i: %s - 0x%0X ", frames - i - 1, symbol->Name, symbol->Address);
        strcat_s(szStackInfo, szFrameInfo);

        if (ret) {
            _snprintf_s(szFrameInfo, sizeof(szFrameInfo), " | %s at %o \n", line.FileName, line.LineNumber);
            strcat_s(szStackInfo, szFrameInfo);
        }
        else {
            _snprintf_s(szFrameInfo, sizeof(szFrameInfo), "|error %d \n", GetLastError());
            strcat_s(szStackInfo, szFrameInfo);
        }
    }
    printf("%s \n", szStackInfo);
    free(symbol);
    return 0;
}

result:

enter image description here

1

There are 1 answers

0
Shane Powell On

The short answer is that dbhlp can't help with the managed parts of the stack walk.

It can be done, as windbg can do mixed mode stacks but it's specific to the version of .net and it also requires access to the full process memory to do it. I don't think the results from CaptureStackBackTrace will be enough, although since you are doing this in-process you may get away with it.

See the Mixed Mode Stackwalk article, which should give you pointers in the right direction.