在Go中执行字节数组

huangapple go评论87阅读模式
英文:

Executing Byte Array in Go

问题

我正在尝试在Go程序中执行shellcode,类似于其他语言的做法。

示例1 - https://stackoverflow.com/questions/16626857/shellcode-in-c-program

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

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

以下是我在Go中进行相同操作的一个例子。在将shellcode传递给函数之前,对其进行了一些操作,因此它的格式作为[]byte是固定的。需要注意的是,mmap期望传入一个文件描述符,这就是为什么有一个可怕的“写入临时文件”的部分存在。

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频道上提问,但有人建议可能不可能实现。

非常感谢任何帮助。

谢谢。

英文:

I'm trying to execute shellcode within a Go program, similar to how you can do it with other languages.

Example 1 - https://stackoverflow.com/questions/16626857/shellcode-in-c-program

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

All methods have broadly similar techniques - assign the shellcode to executable memory via the OS specific allocation (mmap, virtualalloc, etc) and then execute the code by creating a function pointer pointing to that location before executing.

Here is my horrible hacky example at performing the same thing in Go. The shellcode has manipulations performed on it before being passed to the function, so it's format as a []byte is fixed. Saying that mmap expects a file descriptor passed in which is why the horrible "writing to a tmp file" section exists.

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

At the end of the code I've got a pointer (slice?) to the code in what I presume is executable memory - but I've got no idea how to cast this address into a function pointer for execution. I asked on some IRC channels but it was suggested that it may not be possible.

Any help is greatly appreciated.

Cheers.

答案1

得分: 6

首先,你目前不需要使用mmap,因为Go语言的内存是可执行的。如果你确实需要使用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堆栈调用它,那么可以直接在C语言中使用cgo更容易实现。

/*
call(char *code) {
    int (*ret)() = (int(*)())code;
    ret();
}
*/
import "C"

func main() {
    ...
    // 在调用点,你可以通过将shellcode转换为正确类型的指针,直接将其发送给C函数。
    C.call((*C.char)(unsafe.Pointer(&shellcode[0])))
}

以上是翻译好的内容,请确认是否满意。

英文:

First, you don't need to (currently) use mmap at all, since go memory is executable. If you do need mmap, you can use anonymous memory and forgo the temp file:

b, e := syscall.Mmap(0, 0, len(shellcode), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_ANON)
copy(b, shellcode)

Otherwise, you can try to use shellcode directly, since it's already backed by a contiguous array.

As for converting the bytes in shellcode to a function, the analog from C would look like:

f := *(*func() int)(unsafe.Pointer(&d[0]))

which creates a function value named f which can then be called like a normal function.

If the shellcode isn't specifically crafted fro Go, and you need to call it from a C stack, it would be easier to do it directly in C using cgo.

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

答案2

得分: 4

有多种方法可以做到这一点,尽管我自己没有测试过。

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

英文:

There are multiple ways to do it, though I have not tested them myself.

You may want to have a look at https://github.com/nelhage/gojit/blob/master/jit.go
It implements a solution based on cgo (which is safer, but slower), and a solution based on a direct call. Look at all the build related functions.

答案3

得分: 1

我正在尝试学习如何在Go语言中使用mmapPROT_EXEC,并遇到了相同的问题。

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

我找到了一个使用更高级的mmapmmap-go的工作示例。在这个示例中,我们有一个可执行函数的代码,它将传递的参数加一。

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)) // 输出 11

我以为unsafe.Pointer(&memory[0])会解决我的问题,就像其中一个答案建议的那样。但是通过这样做,你无法将地址转换为正确的可调用函数。诀窍是再进一步进行以下操作:

memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)

而不是

f := *(*func(int) int)(&memory[0])
英文:

I was trying to learn how mmap PROT_EXEC would work in Go, and came up with the same problem.

I had a hard time to get the []byte to be casted to a function correctly. It is not so obvious as some of the answers here state.

I've came up with an working example of how to do it using a higher level mmap library called mmap-go. In this example we have an executable code of a function that increments one to the passed argument.

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

I thought unsafe.Pointer(&memory[0]) would solve my problem like one of the answers suggests. But by doing that you cannot cast the address to a callable function in the correct address. The trick was to go one step further and do this

memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)

instead of

f := *(*func(int) int)(&memory[0])

huangapple
  • 本文由 发表于 2015年5月15日 23:38:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/30263520.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定