在Go中执行字节数组

时间:2015-05-15 15:38:33

标签: go function-pointers shellcode

我试图在Go程序中执行shellcode,类似于使用其他语言执行它的方式。

示例1 - Shellcode in C program

示例2 - http://www.debasish.in/2012/04/execute-shellcode-using-python.html

所有方法都有类似的技术 - 通过操作系统特定的分配(mmap,virtualalloc等)将shellcode分配给可执行内存,然后通过在执行之前创建指向该位置的函数指针来执行代码。

这是我在Go中执行相同操作的可怕hacky示例。 shellcode在传递给函数之前对其执行了操作,因此它的格式为[]字节是固定的。说mmap期望传递文件描述符,这就是为什么可怕的"写入tmp文件"部分存在。

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频道,但有人认为这可能是不可能的。

非常感谢任何帮助。

干杯。

3 个答案:

答案 0 :(得分:5)

首先,您根本不需要(当前)使用mmap,因为内存是可执行的。如果确实需要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])))

答案 1 :(得分:4)

有多种方法可以做到,但我自己没有测试过。

您可能需要查看https://github.com/nelhage/gojit/blob/master/jit.go 它实现了基于cgo的解决方案(更安全,但速度更慢),以及基于直接呼叫的解决方案。查看所有与构建相关的函数。

答案 2 :(得分:0)

我试图了解 mmap PROT_EXEC 如何在 Go 中工作,但遇到了同样的问题。

我很难将 []byte 正确地转换为函数。这并不像这里的一些答案所说的那么明显。

我想出了一个工作示例,说明如何使用名为 mmap-go 的更高级别的 mmap 库来执行此操作。在这个例子中,我们有一个函数的可执行代码,它为传递的参数加一。

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])