好的,以下是一个完整的Golang代码实现ImageImportDescriptor结构中Name指针对应的字符串:
”`go package main
import (
"bytes"
"encoding/binary"
"fmt"
"io/ioutil"
"reflect"
"unsafe"
)
type IMAGE_DOS_HEADER struct {
E_magic uint16
E_cblp uint16
E_cp uint16
E_crlc uint16
E_cparhdr uint16
E_minalloc uint16
E_maxalloc uint16
E_ss uint16
E_sp uint16
E_csum uint16
E_ip uint16
E_cs uint16
E_lfarlc uint16
E_ovno uint16
}
type IMAGE_FILE_HEADER struct {
Machine uint16 // CPU类型:x86、x64等等。
NumberOfSections uint16 // 节数量。
TimeDateStamp uint32 // 文件创建时间戳。
PointerToSymbolTable uint32 // 符号表偏移量,一般为0。
NumberOfSymbols uint32 // 符号数量,一般为0。
SizeOfOptionalHeader uint16 // 可选头大小。
Characteristics uint16 // 文件属性标志,如是否可执行等等。
}
type IMAGE_DATA_DIRECTORY struct {
VirtualAddress DWORD // 虚拟地址RVA(相对虚拟地址)。
Size DWORD // 大小,单位字节。
}
type IMAGE_OPTIONAL_HEADER32 struct {
Magic WORD // 魔数:用于区分PE文件和OBJ文件。PE文件为0x10B或者0x107;OBJ文件为0xFFFF.
MajorLinkerVersion BYTE // 连接器主版本号。
MinorLinkerVersion BYTE // 连接器次版本号。
SizeOfCode DWORD // 代码段大小,单位字节。必须对齐到文件中的FileAlignment属性指定的值。
SizeOfInitializedData DWORD // 初始化数据段大小,单位字节。必须对齐到文件中的FileAlignment属性指定的值。
SizeOfUninitializedData DWORD // 未初始化数据段大小,单位字节。必须对齐到文件中的FileAlignment属性指定的值。
AddressOfEntryPoint DWORD // 程序入口点地址RVA(相对虚拟地址)。
BaseOfCode DWORD // 代码段起始地址RVA(相对虚拟地址)。
BaseOfData DWORD // 数据段起始地址RVA(相对虚拟地址),仅针对于32位PE格式有效。
ImageBase DWORD // 映像装载基址,程序在运行时被映射到内存的起始位置。一般为0x400000
SectionAlignment DWORD // 内存页边界数,要求是2^n,最小值是512,默认是4096(4KB)。通常与文件中区块偏移量划分有关系,可用作加密保护等
FileAlignment DWORD // 文件页边界数,要求是2^n,最小值是512,默认是512.通常根据磁盘扇区、硬盘缓存等因素决定
MajorOperatingSystemVersion WORD // 操作系统主版本号。
MinorOperatingSystemVersion WORD // 操作系统次版本号。
MajorImageVersion WORD // 文件主版本号。
MinorImageVersion WORD // 文件次版本号。
MajorSubsystemVersion WORD // 子系统主版本号,一般为4。
MinorSubsystemVersion WORD // 子系统次版本号,一般为0。如果是驱动程序,则应该设置为合适的子系统版本号.
Win32VersionValue DWORD // Win32 版本值(保留)。
SizeOfImage DWORD // 映像大小,单位字节。必须对齐到内存页边界数,即SectionAlignment属性指定的值。
SizeOfHeaders DWORD // 所有头大小,包括MS-DOS、PE和节表等信息。必须对齐到文件页边界数,即FileAlignment属性指定的值。
CheckSum DWORD // 校验和(保留)。可以是0或者0xFFFF表示不进行校验。(实际上也没有谁会去做这个事情)
Subsystem WORD // 程序子系统类型:GUI窗口程序、控制台程序等等
DllCharacteristics WORD // DLL文件特性标志:如是否支持ASLR、DEP等等
SizeOfStackReserve DWORD // 初始的堆栈空间大小(用于进程启动),默认是1MB。如果想改变默认大小,请使用 /STACK 参数链接器选项。
SizeOfStackCommit DWORD // 初始的堆栈页面大小(用于进程启动),默认是4KB。如果想改变默认大小,请使用 /STACK 参数链接器选项。
SizeOfHeapReserve DWORD // 初始的堆空间大小(用于进程启动),默认是1MB。如果想改变默认大小,请使用 /HEAP 参数链接器选项。
SizeOfHeapCommit DWORD // 初始的堆页面大小(用于进程启动),默认是4KB。如果想改变默认大小,请使用 /HEAP 参数链接器选项。
LoaderFlags DWORD // 加载标志,0为没有标志设置
NumberOfRvaAndSizes DWORD // 数据目录结构数量,默认为16个
DataDirectory [IMAGE_NUMBEROF_DIRECTORY] IMAGE_DATA_DIRECTORY // 存储着16个数据目录结构,从第一个元素开始:导出表、导入表、资源表、异常处理表等等...
}
type IMAGE_OPTIONAL_HEADER64 struct {
Magic WORD // 魔数:用于区分PE文件和OBJ文件。PE文件为0x10B或者0x107;OBJ文件为0xFFFF.
MajorLinkerVersion BYTE // 连接器主版本号。
MinorLinkerVersion BYTE // 连接器次版本号。
SizeOfCode DWORD // 代码段大小,单位字节。必须对齐到文件中的FileAlignment属性指定的值。
SizeOfInitializedData DWORD // 初始化数据段大小,单位字节。必须对齐到文件中的FileAlignment属性指定的值。
SizeOfUninitializedData DWORD // 未初始化数据段大小,单位字节。必须对齐到文件中的FileAlignment属性指定的值。
AddressOfEntryPoint DWORD // 程序入口点地址RVA(相对虚拟地址)。
BaseOfCode DWORD // 代码段起始地址RVA(相对虚拟地址)。
ImageBase QWORD // 映像装载基址,程序在运行时被映射到内存的起始位置。一般为0x400000
SectionAlignment DWORD // 内存页边界数,要求是2^n,最小值是512,默认是4096(4KB)。通常与文件中区块偏移量划分有关系,可用作加密保护等
FileAlignment DWORD // 文件页边界数,要求是2^n,最小值是512,默认是512.通常根据磁盘扇区、硬盘缓存等因素决定
MajorOperatingSystemVersion WORD // 操作系统主版本号。
MinorOperatingSystemVersion WORD // 操作系统次版本号。
MajorImageVersion WORD // 文件主版本号。
MinorImageVersion WORD // 文件次版本号。
MajorSubsystemVersion WORD // 子系统主版本号,一般为4。
MinorSubsystemVersion WORD // 子系统次版本号,一般为0。如果是驱动程序,则应该设置为合适的子系统版本号.
Win32VersionValue DWORD // Win32 版本值(保留)。
SizeOfImage DWORD // 映像大小,单位字节。必须对齐到内存页边界数,即SectionAlignment属性指定的值。
SizeOfHeaders DWORD // 所有头大小,包括MS-DOS、PE和节表等信息。必须对齐到文件页边界数,即FileAlignment属性指定的值。
CheckSum DWORD // 校验和(保留)。可以是0或者0xFFFF表示不进行校验。(实际上也没有谁会去做这个事情)
Subsystem WORD // 程序子系统类型:GUI窗口程序、控制台程序等等
DllCharacteristics WORD // DLL文件特性标志:如是否支持ASLR、DEP等等
SizeOfStackReserve QWORD // 初始的堆栈空间大小(用于进程启动),默认是1MB。如果想改变默认大小,请使用 /STACK 参数链接器选项。
SizeOfStackCommit QWORD // 初始的堆栈页面大小(用于进程启动),默认是4KB。如果想改变默认大小,请使用 /STACK 参数链接器选项。
SizeOfHeapReserve QWORD // 初始的堆空间大小(用于进程启动),默认是1MB。如果想改变默认大小,请使用 /HEAP 参数链接器选项。
SizeOfHeapCommit QWORD // 初始的堆页面大小(用于进程启动),默认是4KB。如果想改变默认大小,请使用 /HEAP 参数链接器选项。
LoaderFlags DWORD // 加载标志,0为没有标志设置
NumberOfRvaAndSizes DWORD // 数据目录结构数量,默认为16个
DataDirectory [IMAGE_NUMBEROF_DIRECTORY] IMAGE_DATA_DIRECTORY // 存储着16个数据目录结构,从第一个元素开始:导出表、导入表、资源表、异常处理表等等...
}
type IMAGE_NT_HEADERS32 struct {
Signature uint32 // 固定值0x00004550(PE00)。如果不是,则表示文件不是有效的PE格式文件。
FileHeader IMAGE_FILE_HEADER
OptionalHeader IMAGE_OPTIONAL_HEADER32
}
type IMAGE_NT_HEADERS64 struct {
Signature uint32 // 固定值0x00004550(PE00)。如果不是,则表示文件不是有效的PE格式文件。
FileHeader IMAGE_FILE_HEADER
OptionalHeader IMAGE_OPTIONAL_HEADER64
}
type (
DWORD uint32 // 4字节无符号整数类型。
LONG int32 // 4字节有符号整数类型。
QWORD uint64 // 8字节无符号整数类型。
SHORT int16 // 2字节有符号整数类型。
WORD uint16 // 2字节无符号整数类型。
)
const (
PIMAGE_DIRECTORY_ENTRY_EXPORT = iota // 函数名称导出表(Export Table).
PIMAGE_DIRECTORY_ENTRY_IMPORT // 导入函数地址表和名称信息(Import Address Table)
PIMAGE_DIRECTORY_ENTRY_RESOURCE // 资源表.
PIMAGE_DIRECTORY_ENTRY_EXCEPTION //
PIMAGE_DIRECTORY_ENTRY_SECURITY //
PIMAGE_DIRECTORY_ENTRY_BASERELOC //
PIMAGE_DIRECTORY_ENTRY_DEBUG //
PIMAGE_DIRECTORY_ENTRY_ARCHITECTURE //
PIMAGE_DIRECTORY_ENTRY_GLOBALPTR // 用于64位Windows系统中的TLS(Thread Local Storage).
PIMAGE_DIRECTORY_ENTRY_TLS // TLS(Thread Local Storage)表.
PIMAGE_DIRECTORY_ENTRY_LOAD_CONFIG // 加载器配置信息(不再使用).
PIMAGE_DIRECTORY_ENTRY_BOUND_IMPORT //
PIMAGE_DIRECTORY_ENTRY_IAT // 导入函数地址表,即IAT(Import Address Table)
PIMAGE_DIRECTORY_ENTRY_DELAY_IMPORT // 延迟加载函数地址表,即Delay Import Descriptor
PIMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR // COM组件信息表.
// 最后一个元素.
IMAGE_NUMBEROF_DIRECTORY
)
type IMAGE_THUNK_DATA32 struct {
AddressOfData uint32
}
type IMAGE_THUNK_DATA64 struct {
AddressOfData uint64
}
type (
PTSTR uintptr
UINT uint32
ULONG uintptr
LPCWSTR *uint16
LPCTSTR uintptr
BOOL int32
HANDLE uintptr
HMODULE uintptr
)
type IMAGE_IMPORT_BY_NAME struct {
Hint uint16 `struct:"word"`
Name byte `struct:"byte"`
}
func GetImportedFunctions(peFile []byte, moduleName string) ([]string, error) {
var moduleFound bool = false;
var importDesc IMAGE_IMPORT_DESCRIPTOR;
var importedFunctions []string;
dosHeader := (*IMAGE_DOS_HEADER)(unsafe.Pointer(&peFile[0]));
if dosHeader.E_magic != 0x5A4D { //'MZ'
return nil, fmt.Errorf("Invalid DOS header signature")
}
ntHeaderOffset := binary.Size(IMAGE_DOS_HEADER{}) + uintptr(dosHeader.E_lfarlc)
if ntHeaderOffset+binary.Size(IMAGE_NT_HEADERS32{}) > uintptr(len(peFile)) {
return nil, fmt.Errorf("Invalid NT header offset")
}
var ntHeaderSignature uint32
binary.Read(bytes.NewReader(peFile[ntHeaderOffset:]), binary.LittleEndian, &ntHeaderSignature)
if ntHeaderSignature != 0x4550 { //'PE'
return nil, fmt.Errorf("Invalid NT header signature")
}
fileHeaderOffset := ntHeaderOffset + binary.Size(ntHeaderSignature)
if fileHeaderOffset+binary.Size(IMAGE_FILE_HEADER{}) > uintptr(len(peFile)) {
return nil, fmt.Errorf("Invalid file header offset")
}
var fileHeader IMAGE_FILE_HEADER
binary.Read(bytes.NewReader(peFile[fileHeaderOffset:]), binary.LittleEndian, &fileHeader)
optHeaderOffset := fileHeaderOffset + binary.Size(fileHeader)
if optHeaderOffset >= uintptr(len(peFile)) {
return nil, fmt.Errorf("Invalid optional header offset")
}
is64Bit := false;
switch magic := (*WORD)(unsafe.Pointer(&peFile[optHeaderOffset]))[0]; magic {
case 0x10b:
optHeadersSize := binary.Size(IMAGE_OPTIONAL_HEADER32{})
importDirRVA := (*[]uint32)(unsafe.Pointer(&peFile[optHeaderOffset+optHeadersSize]))[PIMAGE_DIRECTORY_ENTRY_IMPORT]
dataSectionRVA := (*[]uint32)(unsafe.Pointer(&peFile[optHeadersSize+24]))[1];
sectionAlignment := (*DWORD)(unsafe.Pointer(&peFile[optHeadersSize+