0

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

  • I'm not sure it's doable from native code. Not easily, anyway. It's straightforward from managed code though. – Paul Sanders Jun 21 '22 at 08:08
  • I want to capture both native and managed code callstack, not only managed code. Do you mean getting mixed code (native and managed) is straightforward using managed code? can you tell me the specific method? – GuangJun Liu Jun 21 '22 at 08:50
  • Sorry, no, I meant that getting a managed code stack trace is straightforward from managed code. How you're going to do it from your hook is not at all clear to me. – Paul Sanders Jun 21 '22 at 08:52
  • the produce hooked by detours is the mixed code. So, I need to capture both of them. anyway, thanks for your help. – GuangJun Liu Jun 21 '22 at 09:04
  • OK, well, you're going have to call back into C# via some kind of Interop sourcery (which is an area I'm not familiar with, sorry) and then grab your managed stack trace from there. Whether that gets you a _full_ stack trace, or whether it stops at the native / managed boundary, I wouldn't know, you'd have to try it. – Paul Sanders Jun 21 '22 at 09:07

1 Answers1

1

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.

Shane Powell
  • 13,698
  • 2
  • 49
  • 61