簡體   English   中英

C++ - 從結束地址獲取函數的起始地址/獲取函數的大小

[英]C++ - Get Starting Address of a function from Ending Address / Get size of a function

我正在使用 Visual Studio 的 /Gh 和 /GH 編譯器選項來分析一堆代碼。 使用的兩種方法是 _penter 和 _pexit,它們在被分析的代碼中進入或退出函數時調用。 由於我需要對特定函數進行分析/調試,因此我使用了一個已定義的數組FuncTable ,其中包含我需要使用其名稱作為字符串進行檢測的函數的地址。 因此,當輸入函數時,基本上包含寄存器內容的pStack[0]包含正在執行的代碼的當前行的地址。 類似地,當函數退出時, pStack[0]包含代碼最后一行的地址。

問題:當一個函數被輸入時(_penter 被調用),我得到了 pStack[0] 中函數第一行的地址,因此我可以通過減去一個常量(-5)來得到函數的地址並將它保存到我的稍后在 _pexit 函數中檢索的列表。 但是由於在 _pexit 中我得到了函數最后一行的地址,我需要找到函數的大小,以便從 pStack[0] 中的地址中減去該大小以到達函數的起始地址,然后進行比較該地址指向我列表中保存的地址。 下面貼上代碼。

void _stdcall EnterFunc0(unsigned * pStack)
{
    void      * pCaller;
    pCaller = (void *)(pStack[0] - 5); // pStack[0] is first line, -5 for function address
    Signature * funct = FuncTable;

    while (funct->function)
    {
        const BYTE * func = (const BYTE *)funct->function;
        if ((func == (const BYTE *)pCaller) || ((*func == 0xE9) && ((func + *(DWORD *)(func + 1) + 5) == (const BYTE *)pCaller)))
        {
            Stack_Push(funct->name, funct->returnType, true, pCaller);          
        }
        funct++;
    }

}

extern "C" __declspec(naked) void __cdecl _penter()
{
    _asm
    {
        pushad              // save all general purpose registers
        mov    eax, esp     // current stack pointer
        add    eax, 32      // stack pointer before pushad
        push   eax          // push pointer to return address as parameter to EnterFunc0

        call   EnterFunc0

        popad               // restore general purpose registers
        ret                 // start executing original function
    }
}
void _stdcall ExitFunc0(unsigned * pStack)
{
    if (startRecording) 
    {
        StackEntry * start = top;
        while (start != NULL)
        {
            //**HERE I NEED TO COMPARE THE ADDRESS OF THE FUNCTION WITH THE ONE ALREADY IN MY STACK**
                            if ((void *)(pStack[0] - sizeOfTheFunction) == start->Address)
            {
                OutputDebugString("Function Found\n");
            }
            start = start->next;
        }
    }

}
extern "C" __declspec(naked) void __cdecl _pexit()
{
    _asm
    {
        pushad              // save all general purpose registers
        mov    eax, esp     // current stack pointer
        add    eax, 32      // stack pointer before pushad
        push   eax          // push pointer to return address as parameter to EnterFunc0

        call   ExitFunc0

        popad               // restore general purpose registers
        ret                 // start executing original function
    }
}

您已經知道 _pexit() 函數中的地址,它是在 _penter() 函數中傳遞給您的。 您所要做的就是支持嵌套函數調用。 std::stack<> 對此有好處。 使用 push() 將地址保存在 _penter 中,使用 _pexit 函數中的 top() 檢索它並調用 pop()。

根本不需要知道函數體的大小。

由於編譯器確保在每個函數的開始和結束時調用_penter_pexit ,您可以確定在調用_pexit ,由_penter創建的堆棧頂部的函數指針始終指向當前函數。 沒有必要搜索它。

(這應該是正確的,除非您手動調用其中一個函數,您不應該這樣做,或者有一個多線程程序。在后一種情況下,您應該為每個線程創建一個私有堆棧。當然,您也將擁有將Stack_Pop調用添加到_pexit ,但我假設您無論如何都打算這樣做。)

事實證明,我以錯誤的方式看待問題。 我通過使用SymFromAddress方法從dbghelp.lib獲取符號信息解決了這個問題。 一旦我得到了方法的名稱,我就能夠將它與我存儲在 _penter 中的信息進行比較。

SYMBOL_INFO  * mysymbol;
HANDLE         process;
char           temp[MAX_TEMP_LENGTH] = "                                                                       ";

process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);

mysymbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
mysymbol->MaxNameLen = 255;
mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
SymFromAddr(process, (DWORD64)((void *)pStack[0]), 0, mysymbol);
OutputDebugString(mysymbol->Name);
OutputDebugString("\n");

Configuration Properties > C/C++ > Command Line

將編譯器選項添加到Additional Options

像這樣示例設置

為 _penter 鈎子添加標志 /Gh
為 _pexit 鈎子添加標志 /GH

我用於跟蹤/記錄的代碼

#include <intrin.h>

extern "C"  void __declspec(naked) __cdecl _penter(void) {
    __asm {
        push ebp;               // standard prolog
        mov ebp, esp;
        sub esp, __LOCAL_SIZE
        pushad;                 // save registers
    }
    // _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
    PBYTE addr;
    addr = (PBYTE)_ReturnAddress() - 5;

    SYMBOL_INFO* mysymbol;
    HANDLE       process;
    process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    mysymbol->MaxNameLen = 255;
    mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
    myprintf("Entered Function: %s [0x%X]\n", mysymbol->Name, addr);

    _asm {
        popad;              // restore regs
        mov esp, ebp;       // standard epilog
        pop ebp;
        ret;
    }
}

extern "C"  void __declspec(naked) __cdecl _pexit(void) {
    __asm {
        push ebp;               // standard prolog
        mov ebp, esp;
        sub esp, __LOCAL_SIZE
        pushad;                 // save registers
    }
    // _ReturnAddress always returns the address directly after the call, but that is not the start of the function!
    PBYTE addr;
    addr = (PBYTE)_ReturnAddress() - 5;

    SYMBOL_INFO* mysymbol;
    HANDLE       process;
    process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    mysymbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    mysymbol->MaxNameLen = 255;
    mysymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    SymFromAddr(process, (DWORD64)((void*)addr), 0, mysymbol);
    myprintf("Exit Function: %s [0x%X]\n", mysymbol->Name, addr);

    _asm {
        popad;              // restore regs
        mov esp, ebp;       // standard epilog
        pop ebp;
        ret;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM