Home  >  Article  >  Backend Development  >  Reflective loading of CGO-generated DLLs

Reflective loading of CGO-generated DLLs

王林
王林forward
2024-02-08 21:00:13450browse

反射加载 CGO 生成的 DLL

Question content

Just wanted to try reflective dll loading, so I wrote a simple message box:

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() {}

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

When loading the dll using loadlibrary() and running the exported function onprocessattach, it works (message box pops up), but when trying to implement reflective loading of the dll, by resolving relocations and resolving iat, it doesn't work. It seems that a basic relocation is performed and iat is set to an empty section on .rdata which is used to initialize the go runtime (initialized in the entry point of the nt header) Here is the code snippet I used to resolve the import issue:

// 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++;
    }

After doing this, I solved the problem of eat looking for the onprocessattach function, running it directly obviously doesn't work because the go runtime is not initialized, but trying to initialize it causes the program to crash, as mentioned above. It gives exception_access_violation because an invalid block of bytes is tried to be read.

Disassembly of entry point: mov rax, qdword ptr ds:[address] mov dword ptr ds:[rax]

As per the address in the dump, it looks empty 00 00 00 00 00 00 00 00 00 [..]

Although the original dll does have value 90 2b c5 ea 01 [...]

I know I'm setting these bytes on the .rdata to null, but can't figure out why this happens when performing a relocation, maybe the go runtime isn't suitable for what I'm trying to do? Or something else?


Correct Answer


Solution I just forgot to post the solution here The error occurs at the following line

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

It was just a finger error, the sign should have been added instead of the comma, that's why it invalidated the address.

The above is the detailed content of Reflective loading of CGO-generated DLLs. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete