在 Go 中执行字节数组
Executing Byte Array in Go
我正在尝试在 Go 程序中执行 shellcode,类似于使用其他语言执行此操作的方式。
示例 1 - Shellcode in C program
示例 2 - http://www.debasish.in/2012/04/execute-shellcode-using-python.html
所有方法都有大致相似的技术 - 通过 OS 特定分配(mmap、virtualalloc 等)将 shellcode 分配给可执行内存,然后通过在执行前创建指向该位置的函数指针来执行代码.
这是我在 Go 中执行相同操作的可怕的 hacky 示例。 shellcode 在传递给函数之前对其进行了操作,因此它的格式为 []byte 是固定的。说 mmap 需要传入一个文件描述符,这就是可怕的 "writing to a tmp file" 部分存在的原因。
func osxExec(shellcode []byte) {
f, err := os.Create("data/shellcode.tmp")
if err != nil {
fmt.Println(err)
}
defer f.Close()
_,_ = f.Write(shellcode)
f.Sync()
b, err := syscall.Mmap(int(f.Fd()), 0, len(shellcode), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_SHARED)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%p", b)
}
在代码的末尾,我有一个指针(切片?)指向我认为是可执行内存中的代码 - 但我不知道如何将此地址转换为函数指针以供执行.我在一些 IRC 频道上询问过,但有人建议这可能是不可能的。
非常感谢任何帮助。
干杯。
虽然我没有亲自测试过,但有多种方法可以做到。
你可能想看看https://github.com/nelhage/gojit/blob/master/jit.go
它实现了基于cgo的解决方案(更安全,但速度较慢),以及基于直接调用的解决方案。查看所有与构建相关的功能。
首先,您根本不需要(当前)使用 mmap
,因为 go memory 是可执行的。如果确实需要 mmap
,可以使用匿名内存并放弃临时文件:
b, e := syscall.Mmap(0, 0, len(shellcode), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_ANON)
copy(b, shellcode)
否则,您可以尝试直接使用 shellcode
,因为它已经由连续数组支持。
至于将 shellcode
中的字节转换为函数,C 中的模拟如下所示:
f := *(*func() int)(unsafe.Pointer(&d[0]))
创建一个名为 f
的函数值,然后可以像普通函数一样调用它。
如果 shellcode 不是专门为 Go 编写的,并且您需要从 C 堆栈调用它,那么使用 cgo 直接在 C 中调用它会更容易。
/*
call(char *code) {
int (*ret)() = (int(*)())code;
ret();
}
*/
import "C"
func main() {
...
// at your call site, you can send the shellcode directly to the C
// function by converting it to a pointer of the correct type.
C.call((*C.char)(unsafe.Pointer(&shellcode[0])))
我试图了解 mmap
PROT_EXEC
如何在 Go
中工作,但遇到了同样的问题。
我很难将 []byte
正确转换为函数。它并不像这里的一些答案所说的那么明显。
我想出了一个工作示例,说明如何使用名为 mmap-go 的更高级别 mmap
库来完成此操作。在此示例中,我们有一个函数的可执行代码,该函数将传递的参数递增 1。
code := []byte{
0x48, 0xc7, 0x44, 0x24, 0x10, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8b, 0x44, 0x24, 0x08,
0x48, 0xff, 0xc0,
0x48, 0x89, 0x44, 0x24, 0x10,
0xc3,
}
memory, err := mmap.MapRegion(nil, len(code), mmap.EXEC|mmap.RDWR, mmap.ANON, 0)
if err != nil {
panic(err)
}
copy(memory, code)
memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)
f := *(*func(int) int)(ptr)
fmt.Println(f(10)) // Prints 11
我认为 unsafe.Pointer(&memory[0])
会像其中一个答案所暗示的那样解决我的问题。但是通过这样做,您不能将地址转换为正确地址中的可调用函数。诀窍是更进一步并执行此操作
memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)
而不是
f := *(*func(int) int)(&memory[0])
我正在尝试在 Go 程序中执行 shellcode,类似于使用其他语言执行此操作的方式。
示例 1 - Shellcode in C program
示例 2 - http://www.debasish.in/2012/04/execute-shellcode-using-python.html
所有方法都有大致相似的技术 - 通过 OS 特定分配(mmap、virtualalloc 等)将 shellcode 分配给可执行内存,然后通过在执行前创建指向该位置的函数指针来执行代码.
这是我在 Go 中执行相同操作的可怕的 hacky 示例。 shellcode 在传递给函数之前对其进行了操作,因此它的格式为 []byte 是固定的。说 mmap 需要传入一个文件描述符,这就是可怕的 "writing to a tmp file" 部分存在的原因。
func osxExec(shellcode []byte) {
f, err := os.Create("data/shellcode.tmp")
if err != nil {
fmt.Println(err)
}
defer f.Close()
_,_ = f.Write(shellcode)
f.Sync()
b, err := syscall.Mmap(int(f.Fd()), 0, len(shellcode), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_SHARED)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%p", b)
}
在代码的末尾,我有一个指针(切片?)指向我认为是可执行内存中的代码 - 但我不知道如何将此地址转换为函数指针以供执行.我在一些 IRC 频道上询问过,但有人建议这可能是不可能的。
非常感谢任何帮助。
干杯。
虽然我没有亲自测试过,但有多种方法可以做到。
你可能想看看https://github.com/nelhage/gojit/blob/master/jit.go 它实现了基于cgo的解决方案(更安全,但速度较慢),以及基于直接调用的解决方案。查看所有与构建相关的功能。
首先,您根本不需要(当前)使用 mmap
,因为 go memory 是可执行的。如果确实需要 mmap
,可以使用匿名内存并放弃临时文件:
b, e := syscall.Mmap(0, 0, len(shellcode), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_ANON)
copy(b, shellcode)
否则,您可以尝试直接使用 shellcode
,因为它已经由连续数组支持。
至于将 shellcode
中的字节转换为函数,C 中的模拟如下所示:
f := *(*func() int)(unsafe.Pointer(&d[0]))
创建一个名为 f
的函数值,然后可以像普通函数一样调用它。
如果 shellcode 不是专门为 Go 编写的,并且您需要从 C 堆栈调用它,那么使用 cgo 直接在 C 中调用它会更容易。
/*
call(char *code) {
int (*ret)() = (int(*)())code;
ret();
}
*/
import "C"
func main() {
...
// at your call site, you can send the shellcode directly to the C
// function by converting it to a pointer of the correct type.
C.call((*C.char)(unsafe.Pointer(&shellcode[0])))
我试图了解 mmap
PROT_EXEC
如何在 Go
中工作,但遇到了同样的问题。
我很难将 []byte
正确转换为函数。它并不像这里的一些答案所说的那么明显。
我想出了一个工作示例,说明如何使用名为 mmap-go 的更高级别 mmap
库来完成此操作。在此示例中,我们有一个函数的可执行代码,该函数将传递的参数递增 1。
code := []byte{
0x48, 0xc7, 0x44, 0x24, 0x10, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8b, 0x44, 0x24, 0x08,
0x48, 0xff, 0xc0,
0x48, 0x89, 0x44, 0x24, 0x10,
0xc3,
}
memory, err := mmap.MapRegion(nil, len(code), mmap.EXEC|mmap.RDWR, mmap.ANON, 0)
if err != nil {
panic(err)
}
copy(memory, code)
memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)
f := *(*func(int) int)(ptr)
fmt.Println(f(10)) // Prints 11
我认为 unsafe.Pointer(&memory[0])
会像其中一个答案所暗示的那样解决我的问题。但是通过这样做,您不能将地址转换为正确地址中的可调用函数。诀窍是更进一步并执行此操作
memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)
而不是
f := *(*func(int) int)(&memory[0])