访问线程环境块时,程序会发生混乱

NTA*_*ity 2 windows assembly go

我正在开发一个Go库来访问一些内部Windows线程结构(线程环境块),这需要编写一些汇编代码.我一直试图理解为什么它适用于Win32 C++应用程序,但它不在我的Go库上.

这个Go汇编代码片段访问fs:[0x18]以返回指向线程关联TEB的指针:

    // func ReadFsDword(offset uint32) (dword uint32)
    TEXT ·ReadFsDword(SB),$0-8
            MOVL offset+0(FP), AX
            // mov eax, dword ptr fs:[eax]
            BYTE $0x64; BYTE $0x8B; BYTE $0x00
            MOVL AX, ret+8(FP)
            RET
Run Code Online (Sandbox Code Playgroud)

这是等效的MASM代码,可以在MSVC上编译和运行:

void* readfsdword(unsigned offset_)
{
    unsigned dw;

    __asm {
        mov eax, offset_
        mov eax, fs:[eax]
        mov dw, eax
    }

    return (void*)dw;
}
Run Code Online (Sandbox Code Playgroud)

当访问返回指向TEB的指针时,Go程序会发生混乱.这是我得到的信息:

panic:运行时错误:无效的内存地址或nil指针取消引用[signal 0xc0000005 code = 0x0 addr = 0x0 pc = 0x498d5b]

Go汇编代码似乎对我而言,但我无法理解该程序如何以及为何会引起恐慌.任何帮助深表感谢!

这是重现问题的示例:

intrinsics.s

#include "textflag.h"
#include "funcdata.h"

// func ReadFsDword(offset uint32) (ret uint32)
TEXT ·ReadFsDword(SB),$0-8
        MOVL offset+0(FP), AX
        // mov eax, dword ptr fs:[eax]
        BYTE $0x64; BYTE $0x8B; BYTE $0x00
        MOVL AX, ret+8(FP)
        RET
Run Code Online (Sandbox Code Playgroud)

intrinsics.go

package nt

func ReadFsDword(offset uint32) (ret uint32)
Run Code Online (Sandbox Code Playgroud)

test.go

package main

import "nt"

func main() {
    GetProcAddress("LoadLibraryExW")
}

func GetProcAddress(proc string) unsafe.Pointer {
    teb := nt.NtGetTeb()
    fmt.Printf("%p", teb)

    // todo: implement
    return nil
}
Run Code Online (Sandbox Code Playgroud)

NTA*_*ity 5

问题得到解决.显然,Windows gs在x64上使用偏移量为0x30 的寄存器,而fs在x86/WoW64模式下使用0x18偏移量.解决方案是根据值使用fsgs使用相应的偏移量GOARCH.