加载通过CGO生成的DLL文件

huangapple go评论79阅读模式
英文:

Loading reflectively a CGO generated DLL

问题

只是尝试使用反射式DLL加载,所以我写了一个简单的消息框:

package main
import "C"
import (
    "unsafe"
    "syscall"
)

//export OnProcessAttach
func OnProcessAttach() {
    const (
        NULL  = 0
        MB_OK = 0
    )
    caption := "Hola"
    title := "desdegoo"
    ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
        uintptr(NULL),
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))),
        uintptr(MB_OK))

    if ret != 1 {
        return
    }
    return
}

func main() {}

我使用以下命令生成了一个dll(只是一个带有cgo/golang的消息框):
go build --buildmode=c-shared main.go

当使用LoadLibrary()加载dll并运行导出的函数OnProcessAttach时,它可以正常工作(弹出消息框),但是当尝试实现DLL反射加载时,通过解析重定位和解析IAT时,它就无法工作了。
似乎执行基本重定位和IAT设置为初始化go运行时的.rdata上的null部分(在从NT头部初始化的入口点处初始化)。
这是我用于解析导入的代码片段:

// resolve base relocations
IMAGE_DATA_DIRECTORY relocations = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
DWORD_PTR relocationTable = relocations.VirtualAddress + (DWORD_PTR)dllBase;
DWORD relocationsProcessed = 0;

while (relocationsProcessed < relocations.Size)
{
    PBASE_RELOCATION_BLOCK relocationBlock = (PBASE_RELOCATION_BLOCK)(relocationTable + relocationsProcessed);
    relocationsProcessed += sizeof(BASE_RELOCATION_BLOCK);
    DWORD relocationsCount = (relocationBlock->BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY);
    PBASE_RELOCATION_ENTRY relocationEntries = (PBASE_RELOCATION_ENTRY)(relocationTable + relocationsProcessed);
    for (DWORD i = 0; i < relocationsCount; i++)
    {
        relocationsProcessed += sizeof(BASE_RELOCATION_ENTRY);
        if (relocationEntries[i].Type == 0)
        {
            continue;
        }

        DWORD_PTR relocationRVA = relocationBlock->PageAddress + relocationEntries[i].Offset;
        DWORD_PTR addressToPatch = 0;
        ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase, relocationRVA), &addressToPatch, sizeof(DWORD_PTR), NULL);
        addressToPatch += deltaImageBase;
        memcpy((PVOID)((DWORD_PTR)dllBase + relocationRVA), &addressToPatch, sizeof(DWORD_PTR));
    }
}

// resolve IAT
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL;
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)dllBase);
LPCSTR libraryName = "";
HMODULE library = NULL;

while (importDescriptor->Name != NULL)
{
    libraryName = (LPCSTR)importDescriptor->Name + (DWORD_PTR)dllBase;
    library = LoadLibraryA(libraryName);
    if (library)
    {
        PIMAGE_THUNK_DATA thunk = NULL;
        thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)dllBase + importDescriptor->FirstThunk);
        while (thunk->u1.AddressOfData != NULL)
        {
            if (IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal))
            {
                LPCSTR functionOrdinal = (LPCSTR)IMAGE_ORDINAL(thunk->u1.Ordinal);
                thunk->u1.Function = (DWORD_PTR)GetProcAddress(library, functionOrdinal);
            }
            else {
                PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)dllBase + thunk->u1.AddressOfData);
                DWORD_PTR functionAddress = (DWORD_PTR)GetProcAddress(library, functionName->Name);
                thunk->u1.Function = functionAddress;
            }
            ++thunk;
        }
    }
    importDescriptor++;
}

在这样做之后,我解析EAT以查找OnProcessAttach函数,直接运行它显然不起作用,因为go运行时没有初始化,但是尝试初始化它会导致程序崩溃,因为上述原因。它会引发EXCEPTION_ACCESS_VIOLATION,因为它尝试读取一块被置空的字节。

入口点的反汇编:
mov rax, qdword ptr ds:[address]
mov dword ptr ds:[rax]

在转储中跟随地址,它看起来是null
00 00 00 00 00 00 00 00 00 [..]

而原始的dll确实有值
90 2B C5 EA 01 [...]

我知道我正在将.rdata上的那些字节设置为null,但是无法弄清楚为什么在执行重定位时会发生这种情况,也许Go运行时不适合我所尝试的操作?还是其他原因?

英文:

Just trying to experiment with reflective DLL loading, so I wrote a simple messagebox:

package main
import &quot;C&quot;
import (
&quot;unsafe&quot;
&quot;syscall&quot;
)
//export OnProcessAttach
func OnProcessAttach() {
const (
NULL  = 0
MB_OK = 0
)
caption := &quot;Hola&quot;
title := &quot;desdegoo&quot;
ret, _, _ := syscall.NewLazyDLL(&quot;user32.dll&quot;).NewProc(&quot;MessageBoxW&quot;).Call(
uintptr(NULL),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))),
uintptr(MB_OK))
if ret != 1 {
return
}
return
}
func main() {}

I generated a dll (just a messagebox with cgo/golang), using the following command
go build --buildmode=c-shared main.go

When loading the dll with LoadLibrary() and running the exported function OnProcessAttach it works (pops the message box), but when trying to achieve DLL reflective loading, by resolving the relocations and resolving the IAT it just don't work.
Seems like performing the base relocations and the IAT sets to null sections on .rdata that are used for initializing the go runtime (which initializes in the entrypoint from NT headers)
This is the piece of code I'm using for resolving the imports:

// resolve base relocations
IMAGE_DATA_DIRECTORY relocations = ntHeaders-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
DWORD_PTR relocationTable = relocations.VirtualAddress + (DWORD_PTR)dllBase;
DWORD relocationsProcessed = 0;
while (relocationsProcessed &lt; relocations.Size)
{
PBASE_RELOCATION_BLOCK relocationBlock = (PBASE_RELOCATION_BLOCK)(relocationTable + relocationsProcessed);
relocationsProcessed += sizeof(BASE_RELOCATION_BLOCK);
DWORD relocationsCount = (relocationBlock-&gt;BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY);
PBASE_RELOCATION_ENTRY relocationEntries = (PBASE_RELOCATION_ENTRY)(relocationTable + relocationsProcessed);
for (DWORD i = 0; i &lt; relocationsCount; i++)
{
relocationsProcessed += sizeof(BASE_RELOCATION_ENTRY);
if (relocationEntries[i].Type == 0)
{
continue;
}
DWORD_PTR relocationRVA = relocationBlock-&gt;PageAddress + relocationEntries[i].Offset;
DWORD_PTR addressToPatch = 0;
ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase, relocationRVA), &amp;addressToPatch, sizeof(DWORD_PTR), NULL);
addressToPatch += deltaImageBase;
memcpy((PVOID)((DWORD_PTR)dllBase + relocationRVA), &amp;addressToPatch, sizeof(DWORD_PTR));
}
}
// resolve IAT
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL;
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)dllBase);
LPCSTR libraryName = &quot;&quot;;
HMODULE library = NULL;
while (importDescriptor-&gt;Name != NULL)
{
libraryName = (LPCSTR)importDescriptor-&gt;Name + (DWORD_PTR)dllBase;
library = LoadLibraryA(libraryName);
if (library)
{
PIMAGE_THUNK_DATA thunk = NULL;
thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)dllBase + importDescriptor-&gt;FirstThunk);
while (thunk-&gt;u1.AddressOfData != NULL)
{
if (IMAGE_SNAP_BY_ORDINAL(thunk-&gt;u1.Ordinal))
{
LPCSTR functionOrdinal = (LPCSTR)IMAGE_ORDINAL(thunk-&gt;u1.Ordinal);
thunk-&gt;u1.Function = (DWORD_PTR)GetProcAddress(library, functionOrdinal);
}
else {
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)dllBase + thunk-&gt;u1.AddressOfData);
DWORD_PTR functionAddress = (DWORD_PTR)GetProcAddress(library, functionName-&gt;Name);
thunk-&gt;u1.Function = functionAddress;
}
++thunk;
}
}
importDescriptor++;
}

After doing so I resolve the EAT looking for the OnProcessAttach function, running it directly doesn't work obviously because the go runtime isn't initialized, but trying to initialize it crashes the program because the above stated. It gives a EXCEPTION_ACCESS_VIOLATION because tries to read a chunk of bytes that gets nullified.

Dissasembly of the entrypoint:
mov rax, qdword ptr ds:[address]
mov dword ptr ds:[rax]

Following the address in dump it looks null
00 00 00 00 00 00 00 00 00 [..]

While the original dll has indeed values
90 2B C5 EA 01 [...]

I know I'm setting to null those bytes on .rdata, but can't figure out why that's happening when I perform the relocations, maybe the Go runtime doesn't fit for what I'm trying to do? Or is something else?

答案1

得分: 1

在解决问题后,我忘记在这里发布解决方案了。
错误出现在以下代码行中:

    ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase, relocationRVA), &amp;addressToPatch, sizeof(DWORD_PTR), NULL);

这只是一个输入错误,应该是加号而不是逗号,这就是为什么地址被设置为null的原因。

英文:

After solving I just forgot posting the solution here
Error was in the following line

    ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase, relocationRVA), &amp;addressToPatch, sizeof(DWORD_PTR), NULL);

That was just a finger error, should be the add symbol and not the comma, that's why it was nullyfing the address.

huangapple
  • 本文由 发表于 2023年2月4日 02:42:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/75339825.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定