如何在Go语言中执行本机指令?

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

How can I execute native instructions in GoLang?

问题

我在想如何在Go语言中执行字节码(即shellcode)。无论如何,我找到了一些帮助我入门的东西,请查看下面的代码:

package main

import (
	"fmt"
	"log"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT  = 0x1000
	MEM_RESERVE = 0x2000

	PAGE_EXECUTE_READWRITE = 0x40
)

var (
	kernel32     = syscall.MustLoadDLL("kernel32.dll")
	VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
)

func SysAlloc(n uintptr) (uintptr, error) {
	addr, _, err := VirtualAlloc.Call(0, n, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE)
	if addr == 0 {
		return 0, err
	}
	return addr, nil
}

func mkprog() error {
	const size = 64 * 1024
	addr, err := SysAlloc(size)
	if err != nil {
		return err
	}
	b := (*[size]byte)(unsafe.Pointer(addr))
	b[0] = 0xc3 // RET 
	b[1] = 0x90 // NOP
	syscall.Syscall(addr, 0, 0, 0, 0)
	return nil
}

func main() {
	err := mkprog()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("HELLO\n")
}

它可以执行NOP和RET,并成功退出程序。
问题是:如果我将b[]替换为一个shellcode(WinExec calc.exe),像这样:

b[0] = 0x33
b[1] = 0xc0                          
b[2] = 0x50                              
b[3] = 0x68
b[4] = 0x2E
b[5] = 0x65
b[6] = 0x78
b[7] = 0x65              
b[8] = 0x68
b[9] = 0x63
b[10] = 0x61
b[11] = 0x6C
b[12] = 0x63              
b[13] = 0x8B
b[14] = 0xC4                          
b[15] = 0x6A
b[16] = 0x01                          
b[17] = 0x50                              
b[18] = 0xBB
b[19] = 0xED
b[20] = 0x2A
b[21] = 0x86
b[22] = 0x7C              
b[23] = 0xFF
b[24] = 0xD3

它就无法运行了。它不应该正常运行吗?还是我漏掉了什么?

这是C/Python中的shellcode,供参考:

"\x33\xc0"                          // XOR EAX,EAX
"\x50"                              // PUSH EAX      => padding for lpCmdLine
"\x68\x2E\x65\x78\x65"              // PUSH ".exe"
"\x68\x63\x61\x6C\x63"              // PUSH "calc"
"\x8B\xC4"                          // MOV EAX,ESP
"\x6A\x01"                          // PUSH 1
"\x50"                              // PUSH EAX
"\xBB\xED\x2A\x86\x7C"              // MOV EBX,kernel32.WinExec
"\xFF\xD3"                          // CALL EBX

以及错误信息:

Exception 0xc0000005 0x8 0x7c862aed 0x7c862aed
PC=0x7c862aed
signal arrived during cgo execution

main.mkprog(0x0, 0x0)
    C:/Users/guitmz/Documents/Go_test4/test_4.go:64 +0xfe
main.main()
    C:/Users/guitmz/Documents/Go_test4/test_4.go:69 +0x2e

goroutine 2 [runnable]:
runtime.forcegchelper()
    c:/go/src/runtime/proc.go:90
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 3 [runnable]:
runtime.bgsweep()
    c:/go/src/runtime/mgc0.go:82
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 4 [runnable]:
runtime.runfinq()
    c:/go/src/runtime/malloc.go:712
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1
rax     0x7fe10
rbx     0x7c862aed
rcx     0x0
rdx     0x0
rdi     0x7ff5ffffd000
rsi     0xc082021ec0
rbp     0x569ae0
rsp     0x7fdf8
r8      0x0
r9      0x50
r10     0x8
r11     0x4d5520
r12     0x3d
r13     0x0
r14     0x0
r15     0x0
rip     0x7c862aed
rflags  0x10246
cs      0x33
fs      0x53
gs      0x2b
Error: process exited with code 2.

谢谢。

英文:

I was wondering how can I execute bytes (shellcode, basically) in GoLang. Anyway I found something that helped me getting started, check the code below:

package main

import (
	"fmt"
	"log"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT  = 0x1000
	MEM_RESERVE = 0x2000

	PAGE_EXECUTE_READWRITE = 0x40
)

var (
	kernel32     = syscall.MustLoadDLL("kernel32.dll")
	VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
)

func SysAlloc(n uintptr) (uintptr, error) {
	addr, _, err := VirtualAlloc.Call(0, n, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE)
	if addr == 0 {
		return 0, err
	}
	return addr, nil
}

func mkprog() error {
	const size = 64 * 1024
	addr, err := SysAlloc(size)
	if err != nil {
		return err
	}
	b := (*[size]byte)(unsafe.Pointer(addr))
	b[0] = 0xc3 // RET 
	b[1] = 0x90 // NOP
	syscall.Syscall(addr, 0, 0, 0, 0)
	return nil
}

func main() {
	err := mkprog()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("HELLO\n")
}

It works, it executes the NOP and the RET and the program exits sucessfully.
The thing is: if I replace b[] with a shellcode (WinExec calc.exe) like this

b[0] = 0x33
b[1] = 0xc0                          
b[2] = 0x50                              
b[3] = 0x68
b[4] = 0x2E
b[5] = 0x65
b[6] = 0x78
b[7] = 0x65              
b[8] = 0x68
b[9] = 0x63
b[10] = 0x61
b[11] = 0x6C
b[12] = 0x63              
b[13] = 0x8B
b[14] = 0xC4                          
b[15] = 0x6A
b[16] = 0x01                          
b[17] = 0x50                              
b[18] = 0xBB
b[19] = 0xED
b[20] = 0x2A
b[21] = 0x86
b[22] = 0x7C              
b[23] = 0xFF
b[24] = 0xD3

It does not run anymore. Shouldnt it run properly or am I missing something here?

This is the shellcode in C/Python, for reference:

"\x33\xc0"                          # XOR EAX,EAX
"\x50"                              # PUSH EAX      => padding for lpCmdLine
"\x68\x2E\x65\x78\x65"              # PUSH ".exe"
"\x68\x63\x61\x6C\x63"              # PUSH "calc"
"\x8B\xC4"                          # MOV EAX,ESP
"\x6A\x01"                          # PUSH 1
"\x50"                              # PUSH EAX
"\xBB\xED\x2A\x86\x7C"              # MOV EBX,kernel32.WinExec
"\xFF\xD3"                          # CALL EBX

And the error

Exception 0xc0000005 0x8 0x7c862aed 0x7c862aed
PC=0x7c862aed
signal arrived during cgo execution

main.mkprog(0x0, 0x0)
    C:/Users/guitmz/Documents/Go_test4/test_4.go:64 +0xfe
main.main()
    C:/Users/guitmz/Documents/Go_test4/test_4.go:69 +0x2e

goroutine 2 [runnable]:
runtime.forcegchelper()
    c:/go/src/runtime/proc.go:90
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 3 [runnable]:
runtime.bgsweep()
    c:/go/src/runtime/mgc0.go:82
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 4 [runnable]:
runtime.runfinq()
    c:/go/src/runtime/malloc.go:712
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1
rax     0x7fe10
rbx     0x7c862aed
rcx     0x0
rdx     0x0
rdi     0x7ff5ffffd000
rsi     0xc082021ec0
rbp     0x569ae0
rsp     0x7fdf8
r8      0x0
r9      0x50
r10     0x8
r11     0x4d5520
r12     0x3d
r13     0x0
r14     0x0
r15     0x0
rip     0x7c862aed
rflags  0x10246
cs      0x33
fs      0x53
gs      0x2b
Error: process exited with code 2.

Thanks

答案1

得分: 1

syscall.Syscall并不是你想象的那样。

它执行的是一个系统调用,即调用操作系统内核函数,而不是跳转到任意位置。

此外,shellcode期望使用C调用约定,例如栈指针指向C栈等等,而这些条件在Go运行时中并不满足。

英文:

syscall.Syscall don't do what you think.

It make a System Call, a call to OS kernel function, not an arbitrary jump to a location

Moreover the shellcode expect the C calling conventions, for example the stack pointer to be to a C stack etc. this conditions are not fulfilled in go runtime

huangapple
  • 本文由 发表于 2015年5月27日 04:44:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/30468532.html
匿名

发表评论

匿名网友

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

确定