1. 程式人生 > 實用技巧 >Detours學習5

Detours學習5

在程序內使用Detours來攔截使用者函式

前面寫4篇關於Detours的文章,主要是來自官方ReadMe的翻譯和少量自己的理解

C++包管理器

用於 Windows、Linux 和 macOS 的 C++ 包管理器

寫了比較長時間的C#,已經離不開Nuget這種包管理利器了,當再次使用C/C++時,那種下載原始碼、看ReadMe、找依賴包、編譯...一陣心亂如麻,在Google胡亂搜尋了一通,發現C++也有了一些包管理的方案,後面就留意到VcPkg這個由微軟以MIT協議開源的C++包管理。

下面就用VcPkg先安裝Detours並整合到VS2019中

./VcPkg.exe install detours:x86-windows
./VcPkg.exe install detours:x64-windows
./VcPkg.exe integrate install

檢視專案屬性頁可以看到vcpkg的選項

關於VcPkg的更多資訊請檢視微軟官方文件


順便提一下C/C++巨集定義

這個特性真的是讓人又愛又恨,寫得過於複雜閱讀性就差了,簡單使用提供了方便,至於那個可以省去函式入棧出棧的開銷我可沒有那麼關心

  1. 字串化 #

    將巨集定義中傳引數名轉化成用""包起來。

    #define LOGW(str) printf("W: %s\n", #str)
    

    它會忽略傳入引數前面和後面的空格;當傳入引數中間存在空格時,前處理器會自動連線各個子字串,用一個空格連線子字串。

    LOGW(   1     324    a   bc    );
    // 將預處理為
    printf("W: %s\n", "1 324 a bc");
    
  2. 符號連線 ##

    連線各個子部分,## 前後的空格可有可無,如:a##ba ## b是一樣的意義。另外,如果##後的部分本身也是一個巨集的話,##將會阻止後面這個巨集的展開。

  3. 字元化 #@

    將傳入的單字元轉化成用''包起來。

    #define MAKECHAR(c) #@c
    // 將c賦值為'A'
    char c = MAKECHAR(A);
    

有時間遇到一些費解的巨集定義時的確會比較頭大,遇到那種情況可以將巨集定義展開後再閱讀一下,vs的加上/EP選項,不使用 #line 指令預處理到 stdout。關於/EP選項和更多編譯/連線選項請檢視微軟官方文件


建立工程編寫程式碼

│  Detour.cpp
│  Detour.h
│  DetourProcessInside.cpp
│  DetourProcessInside.vcxproj
│  DetourProcessInside.vcxproj.filters
│  DetourProcessInside.vcxproj.user
  1. Detour.h

    #pragma once
    class Detour
    {
    	void* targetPtr;
    	void* detourFunc;
    public:
    	Detour(void* src, void* dest);
    	~Detour();
    };
    
    #define DETOURS(src, dest) Detour instance(src, dest)
    
  2. Detour.cpp

    #include "Detour.h"
    #include <Windows.h>
    #include <detours/detours.h>
    
    Detour::Detour(void* src, void* dest):
    	targetPtr(src), detourFunc(dest)
    {
    	DetourRestoreAfterWith();
    	DetourTransactionBegin();
    	DetourUpdateThread(GetCurrentThread());
    	DetourAttach(&src, dest);
    	DetourTransactionCommit();
    }
    
    Detour::~Detour()
    {
    	DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&targetPtr, detourFunc);
        DetourTransactionCommit();
    }
    
  3. 入口函式檔案

    #include <iostream>
    #include "Detour.h"
    
    double Add(double a, double b)
    {
        return a + b;
    }
    
    double hkAdd(double a, double b)
    {
        std::cout << "The source function has been detoured" << std::endl;
        return 9527;
    }
    
    int main()
    {
        void* targetPtr = (void*)Add;
        DETOURS(targetPtr, hkAdd);
    
        std::cout << "1 + 1 = " << Add(1, 1) << std::endl;
    
        return 0;
    }
    
  4. 輸出結果,不是1 + 1 = 2而是1 + 1 = 9527,呼叫函式Add(1, 1)時跳轉到hkAdd(1, 1)

    The source function has been detoured
    1 + 1 = 9527
    請按任意鍵繼續. . .