在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 產(chǎn)品經(jīng)理/ 運行時的掛鉤 C 函數(shù)
5 個提示助你設(shè)計出精妙的 Apple Watch 應(yīng)用軟件
如何使用安卓密鑰庫存儲密碼和其他敏感信息
從 HDFS 中使用分布式的 MAP REDUCE JOB 寫入 CASSANDRA
現(xiàn)代 Javascript 工具漫游指南
理解 Cassandra 壓縮儲存的作用
不懂 JavaScript?那你就不是一個 Web 開發(fā)者
如何開發(fā)一個簡單的 Android Wear 應(yīng)用程序
Angular 與 React 的比拼
谷歌加入 OpenStack 基金會的 4 個理由
15個很有用的面向設(shè)計師的 UI 和 UX 設(shè)計工具及資源
DevTools 摘要: 處理條帶化數(shù)據(jù)時給條帶化數(shù)據(jù)的一個新家
為什么是 Node.js ? 什么時候使用 Node.js ?
玩轉(zhuǎn) Dcoker:Hello World, 開發(fā)環(huán)境和你的應(yīng)用
運行時的掛鉤 C 函數(shù)
在游戲開發(fā)中獲得成功
在 iOS 開發(fā)中使用 TWITTERKIT & DIGITS
使用 ionic 將數(shù)據(jù)保存到本地存儲中
20 個有用的 Angular.js 工具
如何成為一個超級軟件開發(fā)者
用 Go 語言來看 Android! 出發(fā), Android, 出發(fā)!

運行時的掛鉤 C 函數(shù)

文章翻譯:邵凱陽
發(fā)表時間:2015 年 7 月 27 日
原文作者:Thomas Finch
文章分類:移動應(yīng)用開發(fā)

關(guān)于文本

鉤子(Hook)是 Windows 消息處理機制的一個平臺,該技術(shù)可以實現(xiàn)對消息的監(jiān)視,具有很強大的功能。本文就是基于鉤子的主要功能實現(xiàn)鉤子在 C 中的應(yīng)用,主要介紹了運行時掛鉤 C 函數(shù)的基本步驟、相關(guān)代碼和一些局限性。

文章內(nèi)容

這是一份我最近嘗試的關(guān)于在 C 中運行時函數(shù)掛鉤的快速記錄。對于鉤入一個函數(shù)最基本的思想是用您自己的代碼替換函數(shù)的代碼,所以在調(diào)用該函數(shù)時,您的代碼將被執(zhí)行。運行時掛鉤允許您在被執(zhí)行的程序沒有自己的代碼或者沒有以任何方式對其文件進行實際修改的時候更改程序的運行方式。運行時函數(shù)掛鉤并不少見,并且用于 iOS 越獄調(diào)整(通過 Cydia SubstrateSubstitute 平臺提供技術(shù)支持)以及 Xposed 框架 在 Android 程序中的使用。

如果您想在您自己的計算機上了解這篇文章,您需要使用 Xcode 和 Xcode 命令行工具安裝的 Mac。這些代碼能夠在 Github 上找到。

示例程序

//testProgram.c
#include <stdio.h>

int hookTargetFunction() {
    printf("Calling original function!\n");
    return 5;
}

int main() {
    printf("The number is: %d\n", hookTargetFunction());
    return 0;
} 

編譯和運行該程序可以得到下面的輸出:

Calling original function!
The number is: 5

我們的目標是鉤入 hookTargetFunction 這個函數(shù)并且更改該函數(shù)的返回值,使返回值不再是 5。

掛鉤目標函數(shù)

我們掛鉤目標函數(shù)的方法是通過創(chuàng)建一個動態(tài)庫,當程序運行時加載它。這個動態(tài)庫的構(gòu)造函數(shù)會在 main 函數(shù)的目標可執(zhí)行文件前執(zhí)行,所以我們就可以在目標可執(zhí)行文件運行之前在內(nèi)存中修改它。若要運行我們替換的代碼,我們需要在掛鉤函數(shù)的開頭插入跳轉(zhuǎn)指令的機器代碼。換句話說,當計算機嘗試運行目標函數(shù)時,他將跳轉(zhuǎn)到我們替換函數(shù)所在的位置并運行我們的代碼。

這個過程的第一步就是創(chuàng)建包含一個構(gòu)造函數(shù)和一個替換函數(shù)的動態(tài)庫。

//inject.c
#include <stdio.h>

int hookReplacementFunction() {
    printf("Calling replacement function!\n");
    return 3;
}

__attribute__((constructor))
static void ctor(void) {
    printf("Dylib constructor called!\n");
}

當他使用了 DYLD_INSERT_LIBRARIES 環(huán)境變量的目標程序被編譯和加載后,我們能夠看到他的構(gòu)造函數(shù)在主程序之前被執(zhí)行。

$ ls
inject.c    testProgram testProgram.c
$ clang -dynamiclib inject.c -o inject.dylib
$ DYLD_INSERT_LIBRARIES=inject.dylib ./testProgram
Dylib constructor called!
Calling original function!
The number is: 5

為了鉤入目標函數(shù),現(xiàn)在我們可以開始向構(gòu)造函數(shù)中添加代碼。由于 x86 跳轉(zhuǎn)指令使用相對尋址,所以我們不能簡單的在內(nèi)存中給計算機一個地址讓其跳轉(zhuǎn)。首先,我們需要從目標函數(shù)中找到替換函數(shù)的抵消函數(shù),這些可以通過獲得進入每個函數(shù)的指針,然后從另一個指針中減去一個函數(shù)的指針。

void *mainProgramHandle = dlopen(NULL, RTLD_NOW);
int64_t *origFunc = dlsym(mainProgramHandle , "hookTargetFunction");
int64_t *newFunc = (int64_t*)&hookReplacementFunction;
int32_t offset = (int64_t)newFunc - ((int64_t)origFunc + 5 * sizeof(char));

在這個示例代碼中有一些值得關(guān)注的事情。首先是使用 dlopen 來獲得進入目標可執(zhí)行文件的指針。dlopen 通常被用來加載共享庫,但是 根據(jù)其文檔,如果傳遞 NULL 作為文件名,它也可以用于訪問主可執(zhí)行文件。其次應(yīng)該注意的是,跳轉(zhuǎn)的偏移量實際采取的是下一條指令的地址,在這種情況下目標函數(shù)將增加 5 bytes,因為插入跳轉(zhuǎn)指令的大小為 5 bytes。

在這篇文章中我省略了一小步,那就是使目標函數(shù)所在的內(nèi)存是可寫的,因為處于安全的考慮,在默認情況下內(nèi)存僅僅是可讀的和可執(zhí)行的。一旦這些被完成,最后一步就是創(chuàng)建和插入跳轉(zhuǎn)指令。x86 操作碼是 E9,他與立即數(shù)偏移尋址一起是無條件跳轉(zhuǎn),因此我們將這作為指令的第一個字節(jié),緊跟的是偏移。

int64_t instruction = 0xE9 | offset << 8;
*origFunc = instruction;

這里是完成的 inject.c 文件:

#include <stdio.h>
#include <dlfcn.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>

int hookReplacementFunction() {
    printf("Calling replacement function!\n");
    return 3;
}

__attribute__((constructor))
static void ctor(void) {
    //Get pointers to the original and new functions and calculate the jump offset
    void *mainProgramHandle = dlopen(NULL, RTLD_NOW);
    int64_t *origFunc = dlsym(mainProgramHandle , "hookTargetFunction");
    int64_t *newFunc = (int64_t*)&hookReplacementFunction;
    int32_t offset = (int64_t)newFunc - ((int64_t)origFunc + 5 * sizeof(char));

    //Make the memory containing the original funcion writable
    //Code from http://stackoverflow.com/questions/20381812/mprotect-always-returns-invalid-arguments
    size_t pageSize = sysconf(_SC_PAGESIZE);
    uintptr_t start = (uintptr_t)origFunc;
    uintptr_t end = start + 1;
    uintptr_t pageStart = start & -pageSize;
    mprotect((void *)pageStart, end - pageStart, PROT_READ | PROT_WRITE | PROT_EXEC);

    //Insert the jump instruction at the beginning of the original function
    int64_t instruction = 0xe9 | offset << 8;
    *origFunc = instruction;
}

當他編譯和執(zhí)行完后,他確實改變了主程序的輸出!

$ ls
inject.c    testProgram testProgram.c
$ ./testProgram 
Calling original function!
The number is: 5
$ clang -dynamiclib inject.c -o inject.dylib
$ DYLD_INSERT_LIBRARIES=inject.dylib ./testProgram
Calling replacement function!
The number is: 3

這里是另外一個執(zhí)行過程和一些調(diào)試輸出,顯示了跳轉(zhuǎn)指令插入目標函數(shù)的開始:

$ DYLD_INSERT_LIBRARIES=inject.dylib ./testProgram
Original function address: 0x1078abee0
Replacement function address: 0x1078b4c40
Offset: 0x8d5b
Before replacement: 
*(origFunc+0):  554889e5
*(origFunc+4):  488d3d73
*(origFunc+8):  00e84c00
*(origFunc+12): 00000089
After replacement: 
*(origFunc+0):  e95b8d00
*(origFunc+4):  488d3d73
*(origFunc+8):  00e84c00
*(origFunc+12): 00000089
Calling replacement function!
The number is: 3

局限性

掛鉤這種方法的一個局限性是他要求目標函數(shù)至少是 5 bytes,用于插入跳轉(zhuǎn)指令。這看起來似乎是一個愚蠢的限制,但創(chuàng)建這樣小的函數(shù)也肯定是可能的(例如,只有單字節(jié)大小的 ret 指令)。我想不出解決這一問題的方式,畢竟對單字節(jié)進行操作時很艱難的。最直截了當?shù)慕鉀Q方法就是不掛鉤小于 5 bytes的指令。

我遇到的另外一個問題是讓這些代碼運行在 Linux 上。出于某些原因,Linux 始終在一個高地址加載動態(tài)庫,地址如此之高以至于偏移量溢出了可用的 32 bits。我不認為這是可以修復(fù)的,盡管也使用了跳轉(zhuǎn)指令,因為偏移量的最大尺寸是 32 bits。但是,這個函數(shù)能夠被掛鉤通過另外一種方法——例如,將替換函數(shù)的地址壓入堆棧,然后通過 ret 指令跳轉(zhuǎn)到改地址。這種方法將會比簡單的跳轉(zhuǎn)花費更多的空間,但是這是我現(xiàn)在僅能想到的方法。

我希望您能夠喜歡這篇文章!再一次,自由下載并且在您自己的計算機上測試這些代碼。當您自己動手嘗試時,您會體會到更多的樂趣!

更多IT技術(shù)干貨: wiki.jikexueyuan.com
加入極客星球翻譯團隊: http://wiki.jikexueyuan.com/project/wiki-editors-guidelines/translators.html

版權(quán)聲明:
本譯文僅用于學(xué)習和交流目的。非商業(yè)轉(zhuǎn)載請注明譯者、出處,并保留文章在極客學(xué)院的完整鏈接
商業(yè)合作請聯(lián)系 wiki@jikexueyuan.com
原文地址:[http://thomasfinch.me/blog/2015/07/24/Hooking-C-Functions-At-Runtime.html ](http://thomasfinch.me/blog/2015/07/24/Hooking-C-Functions-At-Runtime.html )