Frida是一款基于Python+javascript的Hook框架, 可運行在Windows、Android、iOS、Linux、MacOS全平臺系統(tǒng)中,主要使用了動態(tài)二進制插樁技術。插樁技術是指將額外的代碼注入到目標程序中, 以實現(xiàn)收集目標運行時信息, 插樁技術主要分為兩種:源代碼插樁和二進制插樁, 源代碼插樁是將額外代碼注入到程序源代碼中,二進制插樁是將額外代碼注入到二進制可執(zhí)行文件中。
使用Frida可以訪問目標進程的內(nèi)存空間,在目標程序運行時可以覆蓋一些功能,從導入的類中調(diào)用函數(shù),在堆上可以查找對象實例并使用這些對象實例,并可以Hook、跟蹤和攔截函數(shù)等等。
Frida是一個非常強大的動態(tài)instrumentation框架,其主要能力包括:
Frida在實戰(zhàn)使用過程中,經(jīng)常使用的功能語法主要包括以下這些:
導入frida模塊:
const frida = require('frida');
附加/注入進程:
// 附加const session = await frida.attach(pid);// 注入const session = await frida.spawn([path], options);
創(chuàng)建/加載/卸載腳本實例:
# 創(chuàng)建const script = await session.createScript(source);# 加載await script.load();# 卸載await script.unload();
導出函數(shù):
rpc.exports = { func1: (args) => { // ... }}
Hook函數(shù):
Interceptor.attach(target, { onEnter: function(args) { }, onLeave: function(retval) { }});
讀寫內(nèi)存:
let buf = Memory.readByteArray(addr, len);Memory.writeByteArray(addr, [1, 2, 3]);
枚舉/搜索模塊:
// 枚舉Process.enumerateModules()// 搜索Process.findModuleByName()
枚舉/搜索導出函數(shù):
// 枚舉Module.enumerateExports()// 搜索Module.findExportByName()
調(diào)用函數(shù):
let retval = Module.getExportByName()(args);
這里以Windows10環(huán)境為基礎進行實驗, 首先在Windows搜索框中搜索:PowerShell, 以管理員權限打開, 并執(zhí)行以下命令:
pip install frida-tools
注意:這里一定要以管理員權限打開PowerShell,否則可能會安裝失敗。
安裝成功后如圖所示:
圖片
輸入命令frida --version 查看Frida的版本號,如果正常顯示版本號,則說明安裝成功, 如圖:
圖片
編寫測試程序的目的是要驗證Frida能否成功Hook測試程序中的指定函數(shù), 并將函數(shù)的每個參數(shù)內(nèi)容進行打印。
我在這里的測試程序使用C++編寫, 主要完成2個函數(shù), AES加密和解密算法, 設想的步驟是將AES加密算法和解密算法的兩個函數(shù)編譯成Dll,并將兩個函數(shù)導出, 然后再寫一個客戶端程序加載Dll并調(diào)用導出函數(shù)。
AES加密算法的C++代碼如下:
extern "C" __declspec(dllexport) void AesEncrypt(unsigned char* plaintext, int plaintext_len, unsigned char* key, unsigned char* iv, unsigned char* ciphertext) { EVP_CIPHER_CTX* ctx; int len; int ciphertext_len; printf("AesEncrypt is at %p/n", AesEncrypt); ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len); ciphertext_len = len; EVP_EncryptFinal_ex(ctx, ciphertext + len, &len); ciphertext_len += len; EVP_CIPHER_CTX_free(ctx);}
AES加密算法函數(shù)AesEncrypt包含了四個參數(shù),分別為: 明文字符串、明文字符串長度、Key、iv向量。
AES解密算法C++代碼如下:
// AES解密extern "C" __declspec(dllexport) void AesDecrypt(unsigned char* ciphertext, int ciphertext_len, unsigned char* key, unsigned char* iv, unsigned char* plaintext) { EVP_CIPHER_CTX* ctx; int len; int plaintext_len; printf("AesDecrypt is at %p/n", AesDecrypt); ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); plaintext_len = len; EVP_DecryptFinal_ex(ctx, plaintext + len, &len); plaintext_len += len; EVP_CIPHER_CTX_free(ctx);}
同樣,AES解密算法函數(shù)AesDecrypt也提供了四個參數(shù),分別為:密文文本、密文文本長度、Key、iv向量。
重新建立一個新的C++工程, 這里模擬了真實程序的業(yè)務場景,對明文字符串使用AES算法加密,為了防止程序運行太快退出,這里將主要程序邏輯放到一個while循環(huán)中,并使用暫停功能進行控制,方便后面的函數(shù)Hook實驗。為了方便操作, 我在中間插入了pause暫停, 方便后面手動控制函數(shù)的調(diào)用時機。
應用代碼如下:
typedef void(__stdcall* AES_ENCRYPT_TYPE)(unsigned char*, int, unsigned char*, unsigned char*, unsigned char*);typedef void(__stdcall* AES_DECRYPT_TYPE)(unsigned char*, int, unsigned char*, unsigned char*, unsigned char*);int main(){ while (1) { //std::cout << "Hello World!/n"; //原文 unsigned char plaintext[] = "This is a plaintext message"; // 密鑰 unsigned char key[32] = "suntiger20232021sdvdiuyt657uhjg"; // 初始化向量 unsigned char iv[16] = "9876kvdfdkkdfdf"; // 密文緩沖區(qū) unsigned char ciphertext[128]; memset(ciphertext, 0, 128); // 加密 AesEncrypt(plaintext, strlen((char*)plaintext), key, iv, ciphertext); system("pause"); unsigned char decryptedtext[128]; memset(decryptedtext, 0, 128); AesDecrypt(ciphertext, strlen((char*)ciphertext), key, iv, decryptedtext); printf("%s/n", decryptedtext); system("pause"); Sleep(1); }}
將以上代碼編譯執(zhí)行后,每按一次空格鍵, 程序便會依次向下執(zhí)行, 如圖:
圖片
插樁程序代碼由Python和Javascript語言組合而成,功能非常強大, 以下代碼是我自己實現(xiàn)的對動態(tài)鏈接庫中的導出函數(shù)AesEncrypt進行Hook,建立一個Python工程,代碼如下:
import fridaimport sysdef on_message(message, data): print(message)# Press the green button in the gutter to run the script.if __name__ == '__main__': local = frida.get_local_device() // 附加進程 session = local.attach("test.exe") script = session.create_script(""" var baseAddr = Module.findBaseAddress('aes.dll'); // 查找函數(shù)模塊 var aesEncryptAddr = Module.findExportByName("aes.dll", "AesEncrypt"); Interceptor.attach(aesEncryptAddr, { onEnter: function(args) { send(args[0]); console.log(''); console.log('[+]plaintext: ' + Memory.readUtf8String(args[0])); console.log('[+]plaintext len: ' + args[1].toInt32()); console.log('[+]key:' + Memory.readUtf8String(args[2])); console.log('[+]iv:' + Memory.readUtf8String(args[3])); } }); """) script.on('message', on_message) script.load() sys.stdin.read() session.detach()
在上面的Hook代碼中,我主要完成了以下操作:
以下是我執(zhí)行插樁程序后,獲取到的參數(shù)內(nèi)容,如圖:
圖片
可以看到,已經(jīng)成功把解密函數(shù)的四個參數(shù)全部打印出來, C++函數(shù)Hook成功。
本文鏈接:http://www.tebozhan.com/showinfo-26-15861-0.html使用Frida在Windows中攔截C++函數(shù)
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: JS 常見的 六種繼承方式,你知道幾種?
下一篇: 四種常見線程池的原理,你學會了嗎?