diff --git a/.gitignore b/.gitignore index c6127b3..1ff1803 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ modules.order Module.symvers Mkfile.old dkms.conf +# Ignore build files +libgore* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c9d95c0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Want to contribute? + +We welcome tickets, pull requests, feature suggestions. + +When developing, please try to comply to the general code style that we try to +maintain across the project. When introducing new features or fixing +significant bugs, please also include some concise information. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2ee6f14 --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +# Copyright 2019 The GoRE.tk Authors. All rights reserved. +# Use of this source code is governed by the license that +# can be found in the LICENSE file. + +APP = libgore + +SHELL = /bin/bash +DIR = $(shell pwd) +GO = go +UID=$(shell id -u) +GID=$(shell id -g) +DOCKER_FOLDER=docker +CONTAINER_NAME=gorebuild +VERSION=$(shell git describe --tags 2> /dev/null || git log --pretty=format:'%h' -n 1) + +NO_COLOR=\033[0m +OK_COLOR=\033[32;01m +ERROR_COLOR=\033[31;01m +WARN_COLOR=\033[33;01m +MAKE_COLOR=\033[33;01m%-20s\033[0m + +APP_FILES=$(APP).so $(APP).dll $(APP).h +PACKAGE=$(APP)-$(VERSION) +LINUX_BUILD_FOLDER=build/linux +LINUX_ARCHIVE=$(PACKAGE)-linux-amd64.tar.gz +WINDOWS_ARCHIVE=$(APP)-$(VERSION)-windows.zip +WINDOWS_BUILD_FOLDER=build/windows +TAR_ARGS=cfz +RELEASE_FILES=LICENSE README.md + +ARCH=GOARCH=amd64 +CGO=CGO_ENABLED=1 +BUILD_OPTS=-ldflags="-s -w" -buildmode=c-shared +WINDOWS_GO_ENV=GOOS=windows $(ARCH) $(CGO) CC=x86_64-w64-mingw32-gcc +LINUX_GO_ENV=GOOS=linux $(ARCH) $(CGO) + +.DEFAULT_GOAL := help + +.PHONY: help +help: + @echo -e "$(OK_COLOR)==== $(APP) [$(VERSION)] ====$(NO_COLOR)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(MAKE_COLOR) : %s\n", $$1, $$2}' + +.PHONY: windows +windows: ## Make binary for Windows + @echo -e "$(OK_COLOR)[$(APP)] Build for Windows$(NO_COLOR)" + @$(WINDOWS_GO_ENV) $(GO) build -o $(APP).dll $(BUILD_OPTS) . + +.PHONY: linux +linux: ## Make binary for linux + @echo -e "$(OK_COLOR)[$(APP)] Build for Linux$(NO_COLOR)" + @$(LINUX_GO_ENV) $(GO) build -o $(APP).so $(BUILD_OPTS) . + +.PHONY: build +build: ## Make binary + @echo -e "$(OK_COLOR)[$(APP)] Build$(NO_COLOR)" + @$(GO) build -o $(APP) $(BUILD_OPTS) . + +.PHONY: clean +clean: ## Remove build artifacts + @echo -e "$(OK_COLOR)[$(APP)] Clean$(NO_COLOR)" + @rm -fr $(APP_FILES) build 2> /dev/null + +.PHONY: docker_container +docker_container: ## Build build-container + @echo -e "$(OK_COLOR)[$(APP)] Build docker container$(NO_COLOR)" + @docker build -t $(CONTAINER_NAME):latest $(DOCKER_FOLDER)/ + +$(APP_FILES): + @echo -e "$(OK_COLOR)[$(APP)] Build using docker container$(NO_COLOR)" + @docker run -it --rm -u $(UID):$(GID) -v $(GOPATH):/go $(CONTAINER_NAME) + @cat structs.h >> libgore.h + +.PHONY: release + +$(LINUX_ARCHIVE): $(APP).so $(APP).h + @mkdir -p $(LINUX_BUILD_FOLDER)/$(PACKAGE) + @cp $(RELEASE_FILES) $(APP).so $(APP).h $(LINUX_BUILD_FOLDER)/$(PACKAGE)/. + @tar $(TAR_ARGS) $(LINUX_ARCHIVE) -C $(LINUX_BUILD_FOLDER) $(PACKAGE) + +$(WINDOWS_ARCHIVE): $(APP).dll $(APP).h + @mkdir -p $(WINDOWS_BUILD_FOLDER)/$(PACKAGE) + @cp $(RELEASE_FILES) $(APP).dll $(APP).h $(WINDOWS_BUILD_FOLDER)/$(PACKAGE)/. + @cd $(WINDOWS_BUILD_FOLDER) && zip -r $(DIR)/$(WINDOWS_ARCHIVE) $(PACKAGE) > /dev/null + +release: $(LINUX_ARCHIVE) $(WINDOWS_ARCHIVE) ## Make release archives + +docker_build: $(APP_FILES) ## Build using docker container + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/cgo.go b/cgo.go new file mode 100644 index 0000000..c36514f --- /dev/null +++ b/cgo.go @@ -0,0 +1,313 @@ +// Copyright 2019 The GoRE.tk Authors. All rights reserved. +// Use of this source code is governed by the license that +// can be found in the LICENSE file. + +package main + +/* +#include +#include "structs.h" +*/ +import "C" + +import ( + "unsafe" + + "github.com/goretk/gore" +) + +//export open +func open(filePath *C.char) C.int { + fp := C.GoString(filePath) + f, err := gore.Open(fp) + if err != nil { + return C.int(0) + } + a := new(arena) + addNewArena(fp, a) + addNewFile(fp, f) + return C.int(1) +} + +//export close +func close(filePath *C.char) { + fp := C.GoString(filePath) + f := getFile(fp) + if f == nil { + return + } + f.Close() + removeFile(fp) + + a := getArena(fp) + a.free() + removeArena(fp) +} + +//export getCompilerVersion +func getCompilerVersion(filePath *C.char) *C.struct_compilerVersion { + fp := C.GoString(filePath) + f := getFile(fp) + if f == nil { + return nil + } + arena := getArena(fp) + if arena == nil { + return nil + } + cv, err := f.GetCompilerVersion() + if err != nil { + return nil + } + cs := (*C.struct_compilerVersion)(arena.malloc(C.sizeof_struct_compilerVersion)) + cs.name = arena.cstring(cv.Name) + cs.sha = arena.cstring(cv.SHA) + cs.timestamp = arena.cstring(cv.Timestamp) + return cs +} + +//export getPackages +func getPackages(filePath *C.char) *C.struct_packages { + fp := C.GoString(filePath) + f := getFile(fp) + if f == nil { + return nil + } + a := getArena(fp) + if a == nil { + return nil + } + pkgs, err := f.GetPackages() + if err != nil { + return nil + } + return convertPackages(pkgs, a) +} + +//export getVendors +func getVendors(filePath *C.char) *C.struct_packages { + fp := C.GoString(filePath) + f := getFile(fp) + if f == nil { + return nil + } + a := getArena(fp) + if a == nil { + return nil + } + pkgs, err := f.GetVendors() + if err != nil { + return nil + } + return convertPackages(pkgs, a) +} + +//export getSTDLib +func getSTDLib(filePath *C.char) *C.struct_packages { + fp := C.GoString(filePath) + f := getFile(fp) + if f == nil { + return nil + } + a := getArena(fp) + if a == nil { + return nil + } + pkgs, err := f.GetSTDLib() + if err != nil { + return nil + } + return convertPackages(pkgs, a) +} + +//export getUnknown +func getUnknown(filePath *C.char) *C.struct_packages { + fp := C.GoString(filePath) + f := getFile(fp) + if f == nil { + return nil + } + a := getArena(fp) + if a == nil { + return nil + } + pkgs, err := f.GetUnknown() + if err != nil { + return nil + } + return convertPackages(pkgs, a) +} + +//export getTypes +func getTypes(filePath *C.char) *C.struct_types { + fp := C.GoString(filePath) + f := getFile(fp) + if f == nil { + return nil + } + a := getArena(fp) + if a == nil { + return nil + } + types, err := f.GetTypes() + if err != nil { + return nil + } + parsed := make(map[uint64]*C.struct_type) + return convertTypes(types, a, parsed) +} + +func convertTypes(types []*gore.GoType, arena *arena, parsed map[uint64]*C.struct_type) *C.struct_types { + val := (**C.struct_type)(arena.calloc(C.sizeof_struct_type, len(types))) + pval := (*[1 << 30]*C.struct_type)(unsafe.Pointer(val))[:len(types):len(types)] + + for i, t := range types { + ct := convertType(t, arena, parsed) + pval[i] = ct + } + pr := (*C.struct_types)(arena.malloc(C.sizeof_struct_types)) + pr.types = val + pr.length = C.ulong(len(types)) + return pr +} + +func convertType(t *gore.GoType, arena *arena, parsed map[uint64]*C.struct_type) *C.struct_type { + if t == nil { + return nil + } + if ct, ok := parsed[t.Addr]; ok { + return ct + } + ct := (*C.struct_type)(arena.malloc(C.sizeof_struct_type)) + parsed[t.Addr] = ct + ct.kind = C.uint(t.Kind) + ct.name = arena.cstring(t.Name) + ct.addr = C.ulonglong(t.Addr) + ct.ptrResolved = C.ulonglong(t.PtrResolvAddr) + ct.packagePath = arena.cstring(t.PackagePath) + ct.fields = convertTypes(t.Fields, arena, parsed) + ct.fieldName = arena.cstring(t.FieldName) + ct.fieldTag = arena.cstring(t.FieldTag) + if t.FieldAnon { + ct.fieldAnon = C.int(1) + } else { + ct.fieldAnon = C.int(0) + } + ct.element = convertType(t.Element, arena, parsed) + ct.length = C.int(t.Length) + ct.chanDir = C.int(t.ChanDir) + ct.key = convertType(t.Key, arena, parsed) + ct.funcArgs = convertTypes(t.FuncArgs, arena, parsed) + ct.funcReturns = convertTypes(t.FuncReturnVals, arena, parsed) + if t.IsVariadic { + ct.isVariadic = C.int(1) + } else { + ct.isVariadic = C.int(0) + } + methods := (**C.struct_method_type)(arena.calloc(C.sizeof_struct_method_type, len(t.Methods))) + pmethods := (*[1 << 30]*C.struct_method_type)(unsafe.Pointer(methods))[:len(t.Methods):len(t.Methods)] + for i, m := range t.Methods { + meth := (*C.struct_method_type)(arena.malloc(C.sizeof_struct_method_type)) + meth.name = arena.cstring(m.Name) + meth.gotype = convertType(m.Type, arena, parsed) + meth.ifaceCallOffset = C.ulonglong(m.IfaceCallOffset) + meth.funcCallOffset = C.ulonglong(m.FuncCallOffset) + pmethods[i] = meth + } + ms := (*C.struct_methods_type)(arena.malloc(C.sizeof_struct_methods_type)) + ms.methods = methods + ms.length = C.ulong(len(t.Methods)) + ct.methods = ms + return ct +} + +func convertPackages(pkgs []*gore.Package, arena *arena) *C.struct_packages { + // https://stackoverflow.com/a/42842309 + // https://groups.google.com/forum/#!topic/golang-nuts/sV_f0VkjZTA + pp := (**C.struct_package)(arena.malloc(C.size_t(len(pkgs)) * C.sizeof_struct_package)) + ppakgs := (*[1 << 30]*C.struct_package)(unsafe.Pointer(pp))[:len(pkgs):len(pkgs)] + + for i, p := range pkgs { + cp := (*C.struct_package)(arena.malloc(C.sizeof_struct_package)) + cp.name = arena.cstring(p.Name) + cp.filepath = arena.cstring(p.Filepath) + + // Populate funcs + pf := (**C.struct_function)(arena.malloc(C.size_t(len(p.Functions)) * C.sizeof_struct_function)) + af := (*[1 << 30]*C.struct_function)(unsafe.Pointer(pf))[:len(p.Functions):len(p.Functions)] + for j, f := range p.Functions { + cf := convertFunction(f, arena) + af[j] = cf + } + cp.function = pf + cp.numFuncs = C.ulong(len(p.Functions)) + + // Populate meths + pm := (**C.struct_method)(arena.malloc(C.size_t(len(p.Methods)) * C.sizeof_struct_method)) + am := (*[1 << 30]*C.struct_method)(unsafe.Pointer(pm))[:len(p.Methods):len(p.Methods)] + for j, m := range p.Methods { + cf := (*C.struct_function)(arena.malloc(C.sizeof_struct_function)) + cf.name = arena.cstring(m.Name) + cf.srcLineLength = C.int(m.SrcLineLength) + cf.srcLineStart = C.int(m.SrcLineStart) + cf.srcLineEnd = C.int(m.SrcLineEnd) + cf.offset = C.ulonglong(m.Offset) + cf.end = C.ulonglong(m.End) + cf.fileName = arena.cstring(m.Filename) + cf.packageName = arena.cstring(m.PackageName) + cm := (*C.struct_method)(arena.malloc(C.sizeof_struct_method)) + cm.receiver = arena.cstring(m.Receiver) + cm.function = cf + am[j] = cm + } + cp.method = pm + cp.numMeths = C.ulong(len(p.Methods)) + + ppakgs[i] = cp + } + pr := (*C.struct_packages)(arena.malloc(C.sizeof_struct_packages)) + pr.packages = pp + pr.length = C.ulong(len(pkgs)) + return pr +} + +func convertFunction(f *gore.Function, arena *arena) *C.struct_function { + cf := (*C.struct_function)(arena.malloc(C.sizeof_struct_function)) + cf.name = arena.cstring(f.Name) + cf.srcLineLength = C.int(f.SrcLineLength) + cf.srcLineStart = C.int(f.SrcLineStart) + cf.srcLineEnd = C.int(f.SrcLineEnd) + cf.offset = C.ulonglong(f.Offset) + cf.end = C.ulonglong(f.End) + cf.fileName = arena.cstring(f.Filename) + cf.packageName = arena.cstring(f.PackageName) + return cf +} + +type arena []unsafe.Pointer + +func (a *arena) malloc(size C.size_t) unsafe.Pointer { + ptr := C.malloc(size) + *a = append(*a, ptr) + return ptr +} + +func (a *arena) calloc(size C.size_t, n int) unsafe.Pointer { + ptr := C.calloc(C.size_t(n), size) + *a = append(*a, ptr) + return ptr +} + +func (a *arena) cstring(str string) *C.char { + cs := C.CString(str) + *a = append(*a, unsafe.Pointer(cs)) + return cs +} + +func (a *arena) free() { + for _, p := range *a { + C.free(p) + } +} + +func main() {} diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..d416c03 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,10 @@ +# FROM karalabe/xgo-latest +FROM golang +MAINTAINER Go Reverse Engineering Tool Kit +RUN \ + apt-get update && \ + apt-get install -y automake autogen build-essential ca-certificates gcc-mingw-w64 make \ + --no-install-recommends + +COPY build.sh /build.sh +ENTRYPOINT ["/build.sh"] diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 0000000..7bd6e2d --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd /go/src/github.com/goretk/libgore +HOME=/tmp make linux windows diff --git a/mem.go b/mem.go new file mode 100644 index 0000000..2185a64 --- /dev/null +++ b/mem.go @@ -0,0 +1,105 @@ +// Copyright 2019 The GoRE.tk Authors. All rights reserved. +// Use of this source code is governed by the license that +// can be found in the LICENSE file. + +package main + +import ( + gore "github.com/goretk/gore" +) + +var fman *manager + +func init() { + fman = &manager{ + openFiles: make(map[string]*gore.GoFile), + add: make(chan *struct { + key string + f *gore.GoFile + }), + get: make(chan string), + ret: make(chan *gore.GoFile), + remove: make(chan string), + openArenas: make(map[string]*arena), + addA: make(chan *struct { + key string + a *arena + }), + getA: make(chan string), + retA: make(chan *arena), + removeA: make(chan string), + } + go fman.handleLoop() +} + +type manager struct { + openFiles map[string]*gore.GoFile + add chan *struct { + key string + f *gore.GoFile + } + get chan string + ret chan *gore.GoFile + remove chan string + openArenas map[string]*arena + addA chan *struct { + key string + a *arena + } + getA chan string + retA chan *arena + removeA chan string +} + +func (m *manager) handleLoop() { + for { + select { + case newf := <-m.add: + m.openFiles[newf.key] = newf.f + case key := <-m.remove: + delete(m.openFiles, key) + case key := <-m.get: + f, _ := m.openFiles[key] + m.ret <- f + case newA := <-m.addA: + m.openArenas[newA.key] = newA.a + case key := <-m.removeA: + delete(m.openArenas, key) + case key := <-m.getA: + a, _ := m.openArenas[key] + m.retA <- a + } + } +} + +func addNewFile(path string, f *gore.GoFile) { + fman.add <- &struct { + key string + f *gore.GoFile + }{key: path, f: f} +} + +func getFile(path string) *gore.GoFile { + fman.get <- path + return <-fman.ret +} + +func removeFile(path string) { + fman.remove <- path +} + +func addNewArena(path string, a *arena) { + fman.addA <- &struct { + key string + a *arena + }{key: path, a: a} +} + +func getArena(path string) *arena { + fman.getA <- path + return <-fman.retA +} + +func removeArena(path string) { + fman.removeA <- path +} diff --git a/structs.h b/structs.h new file mode 100644 index 0000000..590caee --- /dev/null +++ b/structs.h @@ -0,0 +1,76 @@ +// Copyright 2019 The GoRE.tk Authors. All rights reserved. +// Use of this source code is governed by the license that +// can be found in the LICENSE file. + +struct compilerVersion{ + char* name; + char* sha; + char* timestamp; +}; + +struct function { + char* name; + int srcLineLength; + int srcLineStart; + int srcLineEnd; + unsigned long long offset; + unsigned long long end; + char* fileName; + char* packageName; +}; + +struct method { + char* receiver; + struct function* function; +}; + +struct package { + char* name; + char* filepath; + struct function** function; + struct method** method; + unsigned long numFuncs; + unsigned long numMeths; +}; + +struct packages { + struct package** packages; + unsigned long length; +}; + +struct method_type { + char* name; + struct type* gotype; + unsigned long long ifaceCallOffset; + unsigned long long funcCallOffset; +}; + +struct methods_type { + struct method_type** methods; + unsigned long length; +}; + +struct type { + unsigned int kind; + char* name; + unsigned long long addr; + unsigned long long ptrResolved; + char* packagePath; + struct types* fields; + char* fieldName; + char* fieldTag; + int fieldAnon; + struct type* element; + int length; + int chanDir; + struct type* key; + struct types* funcArgs; + struct types* funcReturns; + int isVariadic; + struct methods_type* methods; +}; + +struct types { + struct type** types; + unsigned long length; +};