簡體   English   中英

如何確定函數的長度?

[英]How to determine the length of a function?

考慮以下帶有函數f()的代碼,將函數本身完整地復制到緩沖區,修改其代碼並運行更改的函數。 實際上,克隆並修改返回編號22的原始函數以返回編號42。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ENOUGH 1000
#define MAGICNUMBER 22
#define OTHERMAGICNUMBER 42

int f(void)
{
    return MAGICNUMBER;
}

int main(void)
{
    int i,k;
    char buffer[ENOUGH];
    /* Pointer to original function f */
    int (*srcfptr)(void) = f;
    /* Pointer to hold the manipulated function */
    int (*dstfptr)(void) = (void*)buffer;
    char* byte;
    memcpy(dstfptr, srcfptr, ENOUGH);
    /* Replace magic number inside the function with another */
    for (i=0; i < ENOUGH; i++) {
        byte = ((char*)dstfptr)+i;
        if (*byte == MAGICNUMBER) {
            *byte = OTHERMAGICNUMBER;
        }
    }

    k = dstfptr();
    /* Prints the other magic number */
    printf("Hello %d!\n", k);
    return 0;
}

代碼現在依賴於猜測函數將適合1000字節緩沖區。 它還通過向緩沖區復制太多來違反規則,因為函數f()很可能比1000字節短很多。

這就引出了一個問題 :是否有一種方法可以計算出C中任何給定函數的大小? 一些方法包括查看中間鏈接器輸出,並根據函數中的指令進行猜測,但這還不夠。 有什么辦法可以肯定嗎?


請注意:它在我的系統上編譯和工作,但不完全符合標准,因為函數指針和void *之間的轉換不是完全允許的:

$ gcc -Wall -ansi -pedantic fptr.c -o fptr
fptr.c: In function 'main':
fptr.c:21: warning: ISO C forbids initialization between function pointer and 'void *'
fptr.c:23: warning: ISO C forbids passing argument 1 of 'memcpy' between function pointer and 'void *'
/usr/include/string.h:44: note: expected 'void * __restrict__' but argument is of type 'int (*)(void)'
fptr.c:23: warning: ISO C forbids passing argument 2 of 'memcpy' between function pointer and 'void *'
/usr/include/string.h:44: note: expected 'const void * __restrict__' but argument is of type 'int (*)(void)'
fptr.c:26: warning: ISO C forbids conversion of function pointer to object pointer type
$ ./fptr
Hello 42!
$

請注意:在一些從可寫內存執行的系統上是不可能的,這段代碼會崩潰。 它已經在運行x86_64架構的Linux上使用gcc 4.4.4進行了測試。

你不能在C中做到這一點。即使你知道長度,函數的地址也很重要,因為函數調用和對某些類型數據的訪問將使用程序計數器相對尋址。 因此,位於不同地址的函數的副本將不會與原始函數做同樣的事情。 當然還有很多其他問題。

在C標准中,沒有內省或反思的概念,因此你需要自己設計一個方法,如你所做的那樣,然而存在一些其他更安全的方法。

有兩種方法:

  1. 反匯編函數(在運行時 )直到你到達最后的 RETN / JMP /等,同時考慮開關/跳轉表。 這當然需要對你拆卸的功能進行一些繁重的分析(使用像beaEngine這樣的引擎),這當然是最可靠的,但它的速度慢而且重。
  2. 濫用編譯單元,這是非常危險的,而不是萬無一失,但如果您知道編譯器在編譯單元中按順序生成函數,您可以按照以下方式執行操作:

     void MyFunc() { //... } void MyFuncSentinel() { } //somewhere in code size_t z = (uintptr_t)MyFuncSentinel - (uintptr_t)MyFunc; uint8_t* buf = (uint8_t*)malloc(z); memcpy(buf,(char*)MyFunc,z); 

    這將有一些額外的填充,但它將是最小的(和無法訪問)。 雖然風險很高,但它的拆卸方法要快得多。

注意:這兩種方法都要求目標代碼具有讀取權限。


@R ..提出了一個非常好的觀點,你的代碼將無法重新定位,除非它的PIC或你就地重新調整它以調整地址等。

以下是符合標准的實現所需結果的方法:

int f(int magicNumber)
{
    return magicNumber;
}

int main(void)
{

    k = f(OTHERMAGICNUMBER);
    /* Prints the other magic number */
    printf("Hello %d!\n", k);
    return 0;
}

現在,你可能在沒有參數的地方有很多f()的使用,並且不想通過你的代碼改變每一個,所以你可以改為

int f()
{
    return newf(MAGICNUMBER);
}

int newf(int magicNumber)
{
    return magicNumber;
}


int main(void)
{

    k = newf(OTHERMAGICNUMBER);
    /* Prints the other magic number */
    printf("Hello %d!\n", k);
    return 0;
}

我並不是說這是對你的問題的直接回答,但你所做的是如此可怕,你需要重新考慮你的設計。

那么,您可以使用標簽在運行時獲取函數的長度:

int f()
{
    int length;
    start:
    length = &&end - &&start + 11; // 11 is the length of function prologue
                                   // and epilogue, got with gdb

    printf("Magic number: %d\n", MagicNumber);

    end:
    return length;
}

執行此函數后,我們知道它的長度,因此我們可以將malloc為正確的長度,復制和編輯代碼,然后執行它。

int main()
{
    int (*pointerToF)(), (*newFunc)(), length, i;
    char *buffer, *byte;

    length = f();

    buffer = malloc(length);
    if(!buffer) {
        printf("can't malloc\n");
        return 0;
    }

    pointerToF = f;
    newFunc = (void*)buffer;
    memcpy(newFunc, pointerToF, length);

    for (i=0; i < length; i++) {
        byte = ((char*)newFunc)+i;
        if (*byte == MagicNumber) {
            *byte = CrackedNumber;
        }
    }

    newFunc();
}

現在還有另一個更大的問題,一個是@R。 提及。 修改(正確)后使用此函數會在調用printf時導致分段錯誤,因為call指令必須指定一個錯誤的偏移量 您可以使用gdb看到這一點,使用disassemble f查看原始代碼,使用x/15i buffer查看編輯過的代碼。
順便說一句,我的代碼和你的代碼在沒有警告的情況下編譯,但在調用編輯的函數時崩潰在我的機器上( gcc 4.4.3 )。

暫無
暫無

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

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