Projects STRLCPY BananaPhone Commits 39eb883f
🤬
  • Add mkwinsyscall to main (#1)

    * dirty bad mkwinsyscall example
    
    * this feels bad but it works so im ok with it
    
    * start of rewrite
    
    * technically make works, need docs
    
    * hanlde errors better, stage for PR
    
    * no more global
    
    * docs and alt function names
    
    * remove old folder
    
    * minor updates etc
    
    * properly support tracing and raw mode
    
    * update readme
    
    * lazymerge
  • Loading...
  • C_Sto committed with GitHub 4 years ago
    39eb883f
    1 parent 72a7306a
  • ■ ■ ■ ■
    README.md
    skipped 13 lines
    14 14  - `GetPEB` return the memory location of the PEB without performing any API calls. At it's core, just does this: `MOVQ 0x60(GS), AX ; MOVQ AX, ret+0(FP)`(this is the Go ASM syntax, incase you're confused.)
    15 15  - `GetNtdllStart` return the start address of ntdll loaded in process memory. Does not make any API calls (see asm_x64.s for details)
    16 16  - `WriteMemory` take a byte slice, and write it to a certain memory address (may panic if not writable etc lol)
    17  -- A handful of predefined kernel calls like `NtAllocateVirtualMemory` etc. See source for more details and whatnot.
     17 +- ~A handful of predefined kernel calls like `NtAllocateVirtualMemory` etc. See source for more details and whatnot.~
     18 +- A direct version of `mkwinsyscall` (`mkdirectwinsyscall`in the cmd dir) which should make it easy for you to resolve and use syscalls, and now I don't have to support them :).
    18 19   
    19 20  All of the PE parsing and extraction of interesting information is provided by https://github.com/Binject/debug, which adds on to the stdlib `pe` library in some very cool ways.
    20 21   
    21 22  # Usage
    22 23   
    23 24  See examples in `example/`.
     25 + 
     26 +See mkdirectwinsyscall readme in `cmd/mkdirectwinsyscall`, and example of use in `example`.
    24 27   
    25 28  # Why
    26 29   
    skipped 19 lines
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/function.go
     1 +package main
     2 + 
     3 +import (
     4 + "errors"
     5 + "fmt"
     6 + "strings"
     7 +)
     8 + 
     9 +// Fn describes syscall function.
     10 +type Fn struct {
     11 + Name string
     12 + Params []*Param
     13 + Rets *Rets
     14 + PrintTrace bool
     15 + dllname string
     16 + dllfuncname string
     17 + src string
     18 + // TODO: get rid of this field and just use parameter index instead
     19 + curTmpVarIdx int // insure tmp variables have uniq names
     20 + mode string //bananananamode
     21 + Propermode string
     22 + Global bool
     23 + Internal bool //is the function internal?
     24 +}
     25 + 
     26 +// extractSection extracts text out of string s starting after start
     27 +// and ending just before end. found return value will indicate success,
     28 +// and prefix, body and suffix will contain correspondent parts of string s.
     29 +func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) {
     30 + s = strings.TrimSpace(s)
     31 + if strings.HasPrefix(s, string(start)) {
     32 + // no prefix
     33 + body = s[1:]
     34 + } else {
     35 + a := strings.SplitN(s, string(start), 2)
     36 + if len(a) != 2 {
     37 + suffix = s
     38 + found = false
     39 + return
     40 + }
     41 + prefix = a[0]
     42 + body = a[1]
     43 + }
     44 + a := strings.SplitN(body, string(end), 2)
     45 + if len(a) != 2 {
     46 + //has no end marker. suffix won't be set, but body/prefix may be
     47 + found = false
     48 + return
     49 + }
     50 + return prefix, a[0], a[1], true
     51 +}
     52 + 
     53 +//NewFn parses string s and return created function Fn.
     54 +func NewFn(s, mode string, global, internal bool) (*Fn, error) {
     55 + s = strings.TrimSpace(s) //pesky spaces
     56 + 
     57 + f := &Fn{
     58 + Rets: &Rets{}, //todo:
     59 + src: s,
     60 + PrintTrace: *printTraceFlag,
     61 + Internal: internal,
     62 + Propermode: mode,
     63 + Global: global,
     64 + }
     65 + // function name and args (get everything between first set of brackets)
     66 + prefix, body, s, found := extractSection(s, '(', ')')
     67 + if !found || prefix == "" {
     68 + return nil, fmt.Errorf("Could not extract function name and parameters from %s", f.src)
     69 + }
     70 + 
     71 + f.Name = prefix //this is the name of the function that appears in the final src
     72 + var err error
     73 + f.Params, err = extractParams(body, f) //this is all the params we found
     74 + if err != nil {
     75 + return nil, err
     76 + }
     77 + if f.Propermode == "raw" {
     78 + f.Params = append([]*Param{&Param{
     79 + Name: "sysid",
     80 + Type: "uint16",
     81 + }}, f.Params...)
     82 + }
     83 + 
     84 + // return values
     85 + // must have *all* return values in ()'s I guess?
     86 + _, body, s, found = extractSection(s, '(', ')')
     87 + if found {
     88 + //we have a bunch of name/type pairs
     89 + r, err := extractParams(body, f)
     90 + if err != nil {
     91 + return nil, err
     92 + }
     93 + switch len(r) {
     94 + case 0: //no return - probs not a good sign tbh
     95 + case 1: //one return, can only be err or some ret val I guess?
     96 + if r[0].IsError() {
     97 + f.Rets.ReturnsError = true
     98 + } else {
     99 + f.Rets.Name = r[0].Name
     100 + f.Rets.Type = r[0].Type
     101 + }
     102 + case 2: //two returns
     103 + if !r[1].IsError() { //this seems kinda cooked, but whatever. second return must be an error type
     104 + return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"")
     105 + }
     106 + f.Rets.ReturnsError = true
     107 + f.Rets.Name = r[0].Name
     108 + f.Rets.Type = r[0].Type
     109 + default:
     110 + return nil, errors.New("Too many return values in \"" + f.src + "\"")
     111 + }
     112 + }
     113 + 
     114 + // success condition
     115 + _, body, s, found = extractSection(s, '[', ']')
     116 + if found {
     117 + f.Rets.SuccessCond = body
     118 + }
     119 + 
     120 + s = strings.TrimSpace(s)
     121 + if s == "" {
     122 + return f, nil
     123 + }
     124 + if !strings.HasPrefix(s, "=") {
     125 + return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
     126 + }
     127 + s = strings.TrimSpace(s[1:])
     128 + a := strings.Split(s, ".")
     129 + switch len(a) {
     130 + case 1:
     131 + f.dllfuncname = a[0]
     132 + case 2:
     133 + f.dllname = a[0]
     134 + f.dllfuncname = a[1]
     135 + default:
     136 + return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
     137 + }
     138 + 
     139 + return f, nil
     140 +}
     141 + 
     142 +// HasStringParam is true, if f has at least one string parameter.
     143 +// Otherwise it is false. This requires us to wrap it, and provide a byte pointer - this is done by creating a helper function rather than wrapping it in the func body because?????? idk
     144 +func (f *Fn) HasStringParam() bool {
     145 + for _, p := range f.Params {
     146 + if p.Type == "string" {
     147 + return true
     148 + }
     149 + }
     150 + return false
     151 +}
     152 + 
     153 +// HelperName returns name of function f helper.
     154 +func (f *Fn) HelperName() string {
     155 + if !f.HasStringParam() {
     156 + return f.Name
     157 + }
     158 + return "_" + f.Name
     159 +}
     160 + 
     161 +// HelperParamList returns source code for helper function f parameters.
     162 +func (f *Fn) HelperParamList() string {
     163 + r := join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ")
     164 + if f.mode == "raw" {
     165 + r = "sysid uint16, " + r
     166 + }
     167 + return r
     168 +}
     169 + 
     170 +// StrconvType returns Go type name used for OS string for f.
     171 +func (f *Fn) StrconvType() string {
     172 + if f.IsUTF16() {
     173 + return "*uint16"
     174 + }
     175 + return "*byte"
     176 +}
     177 + 
     178 +// IsUTF16 is true, if f is W (utf16) function. It is false
     179 +// for all A (ascii) functions.
     180 +func (f *Fn) IsUTF16() bool {
     181 + s := f.DLLFuncName()
     182 + return s[len(s)-1] == 'W'
     183 +}
     184 + 
     185 +// DLLFuncName returns DLL function name for function f.
     186 +func (f *Fn) DLLFuncName() string {
     187 + if f.dllfuncname == "" {
     188 + return f.Name
     189 + }
     190 + return f.dllfuncname
     191 +}
     192 + 
     193 +//BananaLoader is which technique BananaPhone should use to resolve syscalls. A raw loader does not load syscalls at all (if the user wants to bundle syscalls directly, without resolving dynamic)
     194 +func (f *Fn) BananaLoader() string {
     195 + if f.Propermode == "raw" {
     196 + return `` //no loader, because user indicates they know what they are doing :smirkemoji:
     197 + }
     198 + yaboi := ``
     199 + if f.Global {
     200 + yaboi = `if bpGlobal == nil {` + //check if our bp is nill or not (maybe something broke it during init?)
     201 + `
     202 + err = fmt.Errorf("BananaPhone uninitialised: %%s", bperr.Error())
     203 + return
     204 + }
     205 + `
     206 + } else {
     207 + t := `bp, bperr := %sNewBananaPhone(%s%s)`
     208 + if f.Internal {
     209 + yaboi = fmt.Sprintf(t, "", "", f.Propermode)
     210 + } else {
     211 + yaboi = fmt.Sprintf(t, "bananaphone.", "bananaphone.", f.Propermode)
     212 + }
     213 + yaboi += `
     214 + if bperr != nil{
     215 + return bperr
     216 + }`
     217 + }
     218 + yaboi += `
     219 + sysid, e := bp%s.GetSysID("%s") ` + //resolve the functions and extract the syscall ID
     220 + `
     221 + if e != nil {
     222 + err = e
     223 + return
     224 + }`
     225 + return fmt.Sprintf(yaboi, f.GetGlobalVar(), f.DLLFuncName())
     226 +}
     227 + 
     228 +func (f *Fn) GetGlobalVar() string {
     229 + if f.Global {
     230 + return "Global"
     231 + }
     232 + return ""
     233 +}
     234 + 
     235 +// StrconvFunc returns name of Go string to OS string function for f.
     236 +func (f *Fn) StrconvFunc() string {
     237 + if f.IsUTF16() {
     238 + return "syscall.UTF16PtrFromString"
     239 + }
     240 + return "syscall.BytePtrFromString"
     241 +}
     242 + 
     243 +// BananaphoneSyscall returns the syscall function with a package init thing if needed
     244 +func (f *Fn) BananaphoneSyscall() string {
     245 + prefix := "bananaphone."
     246 + if f.Internal {
     247 + prefix = ""
     248 + }
     249 + return prefix + "Syscall"
     250 +}
     251 + 
     252 +// ParamPrintList returns source code of trace printing part correspondent
     253 +// to syscall input parameters.
     254 +func (f *Fn) ParamPrintList() string {
     255 + return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
     256 +}
     257 + 
     258 +// SyscallParamList returns source code for SyscallX parameters for function f.
     259 +func (f *Fn) SyscallParamList() string {
     260 + a := make([]string, 0)
     261 + for i, p := range f.Params {
     262 + if f.Propermode == "raw" && i == 0 {
     263 + continue
     264 + }
     265 + a = append(a, p.SyscallArgList()...)
     266 + }
     267 + 
     268 + return strings.Join(a, ", ")
     269 +}
     270 + 
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/mkdirectsyscall.go
     1 +package main
     2 + 
     3 +import (
     4 + "bytes"
     5 + "flag"
     6 + "fmt"
     7 + "go/format"
     8 + "io/ioutil"
     9 + "log"
     10 + "os"
     11 +)
     12 + 
     13 +func usage() {
     14 + fmt.Fprintf(os.Stderr, "usage: mkwinsyscall [flags] [path ...]\n")
     15 + flag.PrintDefaults()
     16 + os.Exit(1)
     17 +}
     18 + 
     19 +const (
     20 + marker = "//dsys"
     21 +)
     22 + 
     23 +var (
     24 + filename = flag.String("output", "", "output file name (standard output if omitted)")
     25 + printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
     26 + mode = flag.String("mode", "auto", "Which bananaphone mode to use (default auto, anything not in the following options results in auto) Options: disk,memory,raw")
     27 + noglobal = flag.Bool("noglobal", false, "Do not use a global var (embed the bananaphone object into each function)")
     28 +)
     29 + 
     30 +func main() {
     31 + log.SetFlags(log.LstdFlags | log.Lshortfile)
     32 + flag.Usage = usage
     33 + flag.Parse()
     34 + if len(flag.Args()) <= 0 {
     35 + fmt.Fprintf(os.Stderr, "no files to parse provided\n")
     36 + usage()
     37 + }
     38 + 
     39 + src, err := ParseFiles(flag.Args(), *mode, !*noglobal)
     40 + if err != nil {
     41 + log.Fatal(err)
     42 + }
     43 + 
     44 + var buf bytes.Buffer
     45 + if err := src.Generate(&buf); err != nil {
     46 + log.Println(string(buf.Bytes()))
     47 + log.Fatal(err)
     48 + }
     49 + data, err := format.Source(buf.Bytes())
     50 + if err != nil {
     51 + log.Println(string(buf.Bytes()))
     52 + log.Fatal(err)
     53 + }
     54 + if *filename == "" {
     55 + _, err = os.Stdout.Write(data)
     56 + } else {
     57 + err = ioutil.WriteFile(*filename, data, 0644)
     58 + }
     59 + if err != nil {
     60 + log.Fatal(err)
     61 + }
     62 + 
     63 +}
     64 + 
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/ntstatus.go
     1 +package main
     2 + 
     3 +/*
     4 +https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values?redirectedfrom=MSDN
     5 +NT_SUCCESS(Status)
     6 +Evaluates to TRUE if the return value specified by Status is a success type (0 − 0x3FFFFFFF) or an informational type (0x40000000 − 0x7FFFFFFF).
     7 + 
     8 +NT_INFORMATION(Status)
     9 +Evaluates to TRUE if the return value specified by Status is an informational type (0x40000000 − 0x7FFFFFFF).
     10 + 
     11 +NT_WARNING(Status)
     12 +Evaluates to TRUE if the return value specified by Status is a warning type (0x80000000 − 0xBFFFFFFF).
     13 + 
     14 +NT_ERROR(Status)
     15 +Evaluates to TRUE if the return value specified by Status is an error type (0xC0000000 - 0xFFFFFFFF).
     16 +*/
     17 + 
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/oldsrcTemplate.go
     1 +package main
     2 + 
     3 +//this file is here so that I don't lose track of what the template used to look like. Template is probably not the best option for this tbh.
     4 + 
     5 +// package main
     6 + 
     7 +// const srcTemplate = `
     8 +// {{define "main"}}// Code generated by 'go generate'; DO NOT EDIT.
     9 + 
     10 +// package {{packagename}}
     11 +// import (
     12 +// {{range .StdLibImports}}"{{.}}"
     13 +// {{end}}
     14 +// {{range .ExternalImports}}"{{.}}"
     15 +// {{end}}
     16 +// {{.BananaImport}}
     17 +// )
     18 + 
     19 +// var _ unsafe.Pointer
     20 + 
     21 +// // Do the interface allocations only once for common
     22 +// // Errno values. C_Sto note: this feels like it might be important, so I'm keeping it.
     23 +// const (
     24 +// errnoERROR_IO_PENDING = 997
     25 +// )
     26 + 
     27 +// var (
     28 +// errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
     29 +// )
     30 + 
     31 +// // errnoErr returns common boxed Errno values, to prevent
     32 +// // allocations at runtime. (yep, again, dunno what it does, but it looks important..)
     33 +// func errnoErr(e syscall.Errno) error {
     34 +// switch e {
     35 +// case 0:
     36 +// return nil
     37 +// case errnoERROR_IO_PENDING:
     38 +// return errERROR_IO_PENDING
     39 +// }
     40 +// // TODO: add more here, after collecting data on the common
     41 +// // error values see on Windows. (perhaps when running
     42 +// // all.bat?)
     43 +// return e
     44 +// }
     45 + 
     46 +// {{.VarBlock}}
     47 + 
     48 +// {{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
     49 +// {{end}}
     50 + 
     51 +// {{define "funcbody"}}
     52 +// func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
     53 +// {{.BananaLoader}}
     54 +// {{template "tmpvars" .}} {{template "syscall" .}} {{template "tmpvarsreadback" .}}
     55 +// {{template "seterror" .}}{{template "printtrace" .}} return
     56 +// }
     57 +// {{end}}
     58 + 
     59 +// {{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
     60 + 
     61 +// {{define "bananaloader"}} {{end}}
     62 + 
     63 +// {{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}}
     64 +// {{end}}{{end}}{{end}}
     65 + 
     66 +// {{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.BananaphoneSyscall}}(sysid, {{.SyscallParamList}}){{end}}
     67 + 
     68 +// {{define "tmpvarsreadback"}}{{range .Params}}{{if .TmpVarReadbackCode}}
     69 +// {{.TmpVarReadbackCode}}{{end}}{{end}}{{end}}
     70 + 
     71 +// {{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}}
     72 +// {{end}}{{end}}
     73 + 
     74 +// {{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
     75 +// {{end}}{{end}}
     76 + 
     77 +// `
     78 + 
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/params.go
     1 +package main
     2 + 
     3 +import (
     4 + "fmt"
     5 + "strings"
     6 +)
     7 + 
     8 +// Param is function parameter
     9 +type Param struct {
     10 + Name string
     11 + Type string
     12 + fn *Fn
     13 + tmpVarIdx int
     14 +}
     15 + 
     16 +// extractParams parses s to extract function parameters.
     17 +func extractParams(s string, f *Fn) ([]*Param, error) {
     18 + s = strings.TrimSpace(s)
     19 + if s == "" {
     20 + return nil, nil
     21 + }
     22 + allparams := strings.Split(s, ",")
     23 + retlist := make([]*Param, len(allparams))
     24 + for i, eachparam := range allparams {
     25 + param := strings.TrimSpace(eachparam)
     26 + paramtypepair := strings.Split(param, " ") //split on space
     27 + if len(paramtypepair) != 2 { //whoopsie, either too many spaces, or not enough!
     28 + paramtypepair = strings.Split(param, "\t") //hmmm maybe it's a tab?
     29 + if len(paramtypepair) != 2 { //nope, abort
     30 + return nil, fmt.Errorf("Could not extract function parameter (check for weird spaces) from %s", param)
     31 + }
     32 + }
     33 + retlist[i] = &Param{
     34 + Name: strings.TrimSpace(paramtypepair[0]), //var name
     35 + Type: strings.TrimSpace(paramtypepair[1]), //var type
     36 + fn: f,
     37 + tmpVarIdx: -1, //who knows
     38 + }
     39 + }
     40 + return retlist, nil
     41 +}
     42 + 
     43 +// IsError determines if p parameter is used to return error.
     44 +func (p *Param) IsError() bool {
     45 + return p.Name == "err" && p.Type == "error" //todo: relax requirement to name this param err
     46 +}
     47 + 
     48 +// join concatenates parameters ps into a string with sep separator.
     49 +// Each parameter is converted into string by applying fn to it
     50 +// before conversion.
     51 +func join(ps []*Param, fn func(*Param) string, sep string) string {
     52 + if len(ps) == 0 {
     53 + return ""
     54 + }
     55 + a := make([]string, 0)
     56 + for _, p := range ps {
     57 + a = append(a, fn(p))
     58 + }
     59 + return strings.Join(a, sep)
     60 +}
     61 + 
     62 +// HelperType returns type of parameter p used in helper function.
     63 +func (p *Param) HelperType() string {
     64 + if p.Type == "string" {
     65 + return p.fn.StrconvType()
     66 + }
     67 + return p.Type
     68 +}
     69 + 
     70 +// tmpVar returns temp variable name that will be used to represent p during syscall.
     71 +func (p *Param) tmpVar() string {
     72 + if p.tmpVarIdx < 0 {
     73 + p.tmpVarIdx = p.fn.curTmpVarIdx
     74 + p.fn.curTmpVarIdx++
     75 + }
     76 + return fmt.Sprintf("_p%d", p.tmpVarIdx)
     77 +}
     78 + 
     79 +// BoolTmpVarCode returns source code for bool temp variable.
     80 +func (p *Param) BoolTmpVarCode() string {
     81 + const code = `var %s uint32
     82 + if %s {
     83 + %s = 1
     84 + } else {
     85 + %s = 0
     86 + }`
     87 + tmp := p.tmpVar()
     88 + return fmt.Sprintf(code, tmp, p.Name, tmp, tmp)
     89 +}
     90 + 
     91 +// BoolPointerTmpVarCode returns source code for bool temp variable.
     92 +func (p *Param) BoolPointerTmpVarCode() string {
     93 + const code = `var %s uint32
     94 + if *%s {
     95 + %s = 1
     96 + } else {
     97 + %s = 0
     98 + }`
     99 + tmp := p.tmpVar()
     100 + return fmt.Sprintf(code, tmp, p.Name, tmp, tmp)
     101 +}
     102 + 
     103 +// SliceTmpVarCode returns source code for slice temp variable.
     104 +func (p *Param) SliceTmpVarCode() string {
     105 + const code = `var %s *%s
     106 + if len(%s) > 0 {
     107 + %s = &%s[0]
     108 + }`
     109 + tmp := p.tmpVar()
     110 + return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name)
     111 +}
     112 + 
     113 +// StringTmpVarCode returns source code for string temp variable.
     114 +func (p *Param) StringTmpVarCode() string {
     115 + errvar := p.fn.Rets.ErrorVarName()
     116 + if errvar == "" {
     117 + errvar = "_"
     118 + }
     119 + tmp := p.tmpVar()
     120 + const code = `var %s %s
     121 + %s, %s = %s(%s)`
     122 + s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name)
     123 + if errvar == "-" {
     124 + return s
     125 + }
     126 + const morecode = `
     127 + if %s != nil {
     128 + return
     129 + }`
     130 + return s + fmt.Sprintf(morecode, errvar)
     131 +}
     132 + 
     133 +// TmpVarCode returns source code for temp variable.
     134 +func (p *Param) TmpVarCode() string {
     135 + switch {
     136 + case p.Type == "bool":
     137 + return p.BoolTmpVarCode()
     138 + case p.Type == "*bool":
     139 + return p.BoolPointerTmpVarCode()
     140 + case strings.HasPrefix(p.Type, "[]"):
     141 + return p.SliceTmpVarCode()
     142 + default:
     143 + return ""
     144 + }
     145 +}
     146 + 
     147 +// SyscallArgList returns source code fragments representing p parameter
     148 +// in syscall. Slices are translated into 2 syscall parameters: pointer to
     149 +// the first element and length.
     150 +func (p *Param) SyscallArgList() []string {
     151 + t := p.HelperType()
     152 + var s string
     153 + switch {
     154 + case t == "*bool":
     155 + s = fmt.Sprintf("unsafe.Pointer(&%s)", p.tmpVar())
     156 + case t[0] == '*':
     157 + s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name)
     158 + case t == "bool":
     159 + s = p.tmpVar()
     160 + case strings.HasPrefix(t, "[]"):
     161 + return []string{
     162 + fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()),
     163 + fmt.Sprintf("uintptr(len(%s))", p.Name),
     164 + }
     165 + default:
     166 + s = p.Name
     167 + }
     168 + return []string{fmt.Sprintf("uintptr(%s)", s)}
     169 +}
     170 + 
     171 +// TmpVarReadbackCode returns source code for reading back the temp variable into the original variable.
     172 +func (p *Param) TmpVarReadbackCode() string {
     173 + switch {
     174 + case p.Type == "*bool":
     175 + return fmt.Sprintf("*%s = %s != 0", p.Name, p.tmpVar())
     176 + default:
     177 + return ""
     178 + }
     179 +}
     180 + 
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/readme.md
     1 +# mkdirectwinsyscall
     2 + 
     3 +**warning** This code, and interface is likely to change. If you use it and it works, that's rad, but it might not work and/or break interfaces on the next commit. pls no rely on it (yet) thx.
     4 + 
     5 +This is mostly a re-write of go/src/golang.org/x/sys/windows/mkwinsyscall to fit in with bananaphone good.
     6 + 
     7 +## Flags
     8 +### Mode
     9 +`-mode`
     10 +Option | Description
     11 +------------- | -------------
     12 +auto | Automatically resolves sysID. First attempts to resolve in-memory, then falls back to disk. This is the default option.
     13 +memory | Resolves sysID's by locating ntdll in memory by parsing the PEB, enumerating exports, and extracting the sysID from the function pointers.
     14 +disk | Resolves sysID's by parsing ntdll on-disk. Uses filesystem read API's built in to go.
     15 +raw | Does not resolve sysID's. Functions must be provided the sysID externally. (this may be useful if you want to hard-code them into the bin to avoid detection on resolution).
     16 + 
     17 +### Globals
     18 +`-noglobal`
     19 + 
     20 +This option will not use a global var for sys ID resolution. This means the PEB will be parsed/ntdll.dll on disk will be read *every time* the generated function is called. This might be what you want for some insane reason, no judgement here.
     21 + 
     22 +### Tracing
     23 +`-trace`
     24 + 
     25 +Is everything just not really working, and you have no idea what values are being given to your syscalls? This option will print every param + the returns etc for your debugging pleasure. It uses `print(` which I didn't even realise was a thing until I saw it in mkwinsyscall! TIL!
     26 + 
     27 +### Output
     28 +`-output`
     29 + 
     30 +Specify the output filename. This overwrites the file if it exists (because that's useful when doing `go generate`).
     31 + 
     32 +## Usage
     33 + 
     34 +It's recommended that you have a file specific for your syscalls, but in theory, you only need the following signature:
     35 + 
     36 +```golang
     37 +//dsys funcname(param type) (err error)
     38 +```
     39 + 
     40 +You can optionally include a suffix that allows you to have a different go function name, but calls a specified Windows API call. Below will create a func named `whateveryouwant()`, and resolve the `NtAllocateVirtualMemory` syscall ID.
     41 + 
     42 +```golang
     43 +//dsys whateveryouwant() (err error) = NtAllocateVirtualMemory
     44 +```
     45 + 
     46 +so for example, if you wanted to bananaphone up a `NtAllocateVirtualMemory` call, but not export it you would do this:
     47 +```golang
     48 +/*
     49 +//dsys ntAllocateVirtualMemory(processHandle syscall.Handle, baseAddress *uintptr, zeroBits uintptr, regionSize *uintptr, allocationType uint64, protect uint64) (err error) = NtAllocateVirtualMemory
     50 +```
     51 + 
     52 +Once you have your syscall.go file (or whatever it's called), you can run this program against it (`mkdirectwinsyscall syscall.go`). By default this will print to stdout.
     53 + 
     54 +Pro move is to use `go generate` to do it all for you. Include a line like this in `syscall.go` and then run `go generate .` in the package that has the file:
     55 + 
     56 +```golang
     57 +//go:generate go run github.com/C-Sto/BananaPhone/cmd/mkdirectwinsyscall -output zsyscall_windows.go syscall.go
     58 +```
     59 + 
     60 +This will run the generator, and replace the zsyscall_windows.go file with a newly generated version.
     61 + 
     62 + 
     63 +## Success/Error values:
     64 + 
     65 +See ntstatus.go
     66 + 
     67 +As far as I can tell, all Nt function calls (which are the only relevant function calls in this lib besides Zw) have a single return value of type NtStatus. This is a 32 bit value (https://doxygen.reactos.org/d9/d6f/base_2applications_2drwtsn32_2precomp_8h.html#a1b7a3ae6a8dcfbde0f16bfaffa13b024) with a bunch of different possible meanings. The `os` or `syscall` package should probably support these values better than I can.
     68 + 
     69 +At present, the return value for the `Syscall` function is *always* checked against 0, and if not 0, err is filled with a message that contains the hex representation of NtStatus (incase your func def omitted it, but still had the err value for some reason). If your return is between 0 and 0x3FFFFFFF, you need to check the err value for that, or specify the expected value in `[]`'s, just like the normal mkwinsyscall.
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/rets.go
     1 +package main
     2 + 
     3 +import (
     4 + "fmt"
     5 + "strings"
     6 +)
     7 + 
     8 +// Rets describes function return parameters.
     9 +type Rets struct {
     10 + Name string
     11 + Type string
     12 + ReturnsError bool
     13 + SuccessCond string
     14 +}
     15 + 
     16 +// List returns source code of syscall return parameters.
     17 +func (r *Rets) List() string {
     18 + s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ")
     19 + if len(s) > 0 {
     20 + s = "(" + s + ")"
     21 + }
     22 + return s
     23 +}
     24 + 
     25 +// ToParams converts r into slice of *Param.
     26 +func (r *Rets) ToParams() []*Param {
     27 + ps := make([]*Param, 0)
     28 + if len(r.Name) > 0 {
     29 + ps = append(ps, &Param{Name: r.Name, Type: r.Type})
     30 + }
     31 + if r.ReturnsError {
     32 + ps = append(ps, &Param{Name: "err", Type: "error"})
     33 + }
     34 + return ps
     35 +}
     36 + 
     37 +// PrintList returns source code of trace printing part correspondent
     38 +// to syscall return values.
     39 +func (r *Rets) PrintList() string {
     40 + return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
     41 +}
     42 + 
     43 +// ErrorVarName returns error variable name for r.
     44 +func (r *Rets) ErrorVarName() string {
     45 + if r.ReturnsError {
     46 + return "err"
     47 + }
     48 + if r.Type == "error" {
     49 + return r.Name
     50 + }
     51 + return ""
     52 +}
     53 + 
     54 +// SetReturnValuesCode returns source code that accepts syscall return values.
     55 +func (r *Rets) SetReturnValuesCode() string {
     56 + if r.Name == "" && !r.ReturnsError {
     57 + return ""
     58 + }
     59 + retvar := "r0"
     60 + if r.Name == "" {
     61 + retvar = "r1"
     62 + }
     63 + errvar := "_"
     64 + if r.ReturnsError {
     65 + errvar = "_"
     66 + }
     67 + return fmt.Sprintf("%s, %s := ", retvar, errvar)
     68 +}
     69 + 
     70 +// SetErrorCode returns source code that sets return parameters.
     71 +func (r *Rets) SetErrorCode() string {
     72 + const code = `if r0 != 0 {
     73 + %s = %sErrno(r0)
     74 + }`
     75 + if r.Name == "" && !r.ReturnsError {
     76 + return ""
     77 + }
     78 + if r.Name == "" {
     79 + return r.useLongHandleErrorCode("r1")
     80 + }
     81 + if r.Type == "error" {
     82 + return fmt.Sprintf(code, r.Name, "syscall.")
     83 + }
     84 + s := ""
     85 + switch {
     86 + case r.Type[0] == '*':
     87 + s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type)
     88 + case r.Type == "bool":
     89 + s = fmt.Sprintf("%s = r0 != 0", r.Name)
     90 + default:
     91 + s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type)
     92 + }
     93 + if !r.ReturnsError {
     94 + return s
     95 + }
     96 + return s + "\n\t" + r.useLongHandleErrorCode(r.Name)
     97 +}
     98 + 
     99 +func (r *Rets) useLongHandleErrorCode(retvar string) string {
     100 + 
     101 + const code = `if %s {
     102 + err = fmt.Errorf("error code: %%x",%s)
     103 + }`
     104 + cond := retvar + " != 0"
     105 + if r.SuccessCond != "" {
     106 + cond = strings.Replace(r.SuccessCond, "succretval", retvar, 1)
     107 + }
     108 + return fmt.Sprintf(code, cond, retvar)
     109 +}
     110 + 
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/source.go
     1 +package main
     2 + 
     3 +import (
     4 + "bufio"
     5 + "errors"
     6 + "fmt"
     7 + "go/parser"
     8 + "go/token"
     9 + "io"
     10 + "os"
     11 + "path/filepath"
     12 + "sort"
     13 + "strings"
     14 + "text/template"
     15 +)
     16 + 
     17 +// ParseFiles parses files listed in fs and extracts all syscall
     18 +// functions listed in dsys comments. It returns a files
     19 +// and functions collection *Source if successful.
     20 +func ParseFiles(files []string, mode string, global bool) (*Source, error) {
     21 + src := &Source{
     22 + //Funcs: make([]*Fn, 0),
     23 + Files: make([]string, 0),
     24 + StdLibImports: []string{
     25 + "unsafe",
     26 + },
     27 + ExternalImports: make([]string, 0),
     28 + Mode: setmode(mode),
     29 + Global: global,
     30 + }
     31 + 
     32 + for _, file := range files {
     33 + if err := src.ParseFile(file); err != nil {
     34 + return nil, err
     35 + }
     36 + }
     37 + return src, nil
     38 +}
     39 + 
     40 +// Source files and functions.
     41 +type Source struct {
     42 + Funcs []*Fn
     43 + Files []string
     44 + StdLibImports []string
     45 + ExternalImports []string
     46 + PackageName string
     47 + Mode string
     48 + Global bool
     49 +}
     50 + 
     51 +//Import adds an import to the current source (this should never really be called?)
     52 +func (src *Source) Import(pkg string) {
     53 + src.StdLibImports = append(src.StdLibImports, pkg)
     54 + sort.Strings(src.StdLibImports)
     55 +}
     56 + 
     57 +//ExternalImport adds an external import (eg, an import not from bananaphone)
     58 +func (src *Source) ExternalImport(pkg string) {
     59 + src.ExternalImports = append(src.ExternalImports, pkg)
     60 + sort.Strings(src.ExternalImports)
     61 +}
     62 + 
     63 +// DLLs return dll names for a source set src. (this shouldn't really be called either, we don't deal with dlls in bananaphone ok)
     64 +/*
     65 +func (src *Source) DLLs() []string {
     66 + log.Println("Erm, maybe check this one eh! something is calling for DLL's lol")
     67 + uniq := make(map[string]bool)
     68 + r := make([]string, 0)
     69 + for _, f := range src.Funcs {
     70 + name := f.DLLName()
     71 + if _, found := uniq[name]; !found {
     72 + uniq[name] = true
     73 + r = append(r, name)
     74 + }
     75 + }
     76 + return r
     77 +}
     78 +*/
     79 + 
     80 +// ParseFile adds additional file path to a source set src.
     81 +// **note** this is a pretty weird way of doing it, we can probably tokenise and parse it better but uh.. you do you stdlib..
     82 +func (src *Source) ParseFile(path string) error {
     83 + file, err := os.Open(path)
     84 + if err != nil {
     85 + return err
     86 + }
     87 + defer file.Close()
     88 + 
     89 + s := bufio.NewScanner(file)
     90 + for s.Scan() { //checks each line
     91 + t := strings.TrimSpace(s.Text()) //ignore whitespace
     92 + /*
     93 + this probably avoids some false-positives but like... what?
     94 + if len(t) < 7 {
     95 + continue
     96 + }
     97 + */
     98 + if !strings.HasPrefix(t, marker) { //not what we are lookin for fam!
     99 + continue
     100 + }
     101 + 
     102 + t = t[len(marker):] //skip marker
     103 + 
     104 + //check for space/tab after the //dsys marker. Just incase someone wrote //dsysnuts or something I guess? idk
     105 + if !(t[0] == ' ' || t[0] == '\t') {
     106 + continue
     107 + }
     108 + t = strings.TrimSpace(t)
     109 + 
     110 + //what's left should be a function definition. Let's parse that badboi!
     111 + std, err := src.IsStdRepo(path)
     112 + if err != nil {
     113 + return err
     114 + }
     115 + f, err := NewFn(t, setmode(*mode), src.Global, std)
     116 + if err != nil {
     117 + return err
     118 + }
     119 + src.Funcs = append(src.Funcs, f)
     120 + 
     121 + }
     122 + if err := s.Err(); err != nil {
     123 + return err
     124 + }
     125 + 
     126 + src.Files = append(src.Files, path)
     127 + 
     128 + // get package name
     129 + fset := token.NewFileSet()
     130 + _, err = file.Seek(0, 0)
     131 + if err != nil {
     132 + return err
     133 + }
     134 + pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly)
     135 + if err != nil {
     136 + return err
     137 + }
     138 + src.PackageName = pkg.Name.Name
     139 + 
     140 + return nil
     141 +}
     142 + 
     143 +// Generate output source file
     144 +func (src *Source) Generate(w io.Writer) error {
     145 + 
     146 + //work out if this is being used from bananaphone or not :O bananaception! the phone is ringing from inside the package!
     147 + /*
     148 + isBananaRepo, err := src.IsStdRepo()
     149 + if err != nil {
     150 + return err
     151 + }
     152 + */
     153 + src.Import("syscall") //I think we will always need this?
     154 + src.Import("fmt") //error creation (todo: should look into how to remove this)
     155 + 
     156 + funcMap := template.FuncMap{ //I don't fully understand what's going on here. Update: I get it
     157 + "packagename": src.GetPackageName,
     158 + "bananaphonedot": src.BananaPhonedot,
     159 + }
     160 + 
     161 + t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
     162 + err := t.Execute(w, src)
     163 + if err != nil {
     164 + return errors.New("Failed to execute template: " + err.Error())
     165 + }
     166 + return nil
     167 +}
     168 + 
     169 +// IsStdRepo reports whether src is part of standard library.
     170 +func (src *Source) IsStdRepo(path string) (bool, error) {
     171 + if len(src.Files) == 0 && path == "" {
     172 + return false, errors.New("no input files provided")
     173 + }
     174 + var (
     175 + abspath string
     176 + err error
     177 + )
     178 + if len(src.Files) == 0 {
     179 + abspath, err = filepath.Abs(path)
     180 + } else {
     181 + abspath, err = filepath.Abs(src.Files[0])
     182 + }
     183 + if err != nil {
     184 + return false, err
     185 + }
     186 + 
     187 + abspath = strings.ToLower(abspath)
     188 + 
     189 + //this probably isn't the best way of checking, but it seems to work OK
     190 + return strings.Contains(abspath, filepath.Join("github.com", "c-sto", "bananaphone", "pkg", "bananaphone")), nil
     191 +}
     192 + 
     193 +//BananaPhonedot returns the prefix to any bananaphone calls (or none, if it's inside the bp repo)
     194 +func (src Source) BananaPhonedot() string {
     195 + if src.PackageName == "bananaphone" {
     196 + return ""
     197 + }
     198 + return "bananaphone."
     199 +}
     200 + 
     201 +//GetPackageName returns the package name for the source set
     202 +func (src Source) GetPackageName() string {
     203 + return src.PackageName
     204 +}
     205 + 
     206 +//BananaImport will return the import path for bananaphone, or an empty string if it's called from within this repo
     207 +func (src *Source) BananaImport() string {
     208 + std, err := src.IsStdRepo("")
     209 + if err != nil {
     210 + panic(err)
     211 + }
     212 + if !std {
     213 + return `bananaphone "github.com/C-Sto/BananaPhone/pkg/BananaPhone"`
     214 + }
     215 + return ``
     216 +}
     217 + 
     218 +//VarBlock will return the source needed for the var block - nothing if in raw mode etc
     219 +func (src *Source) VarBlock() string {
     220 + if src.Mode == "raw" || !src.Global {
     221 + return ``
     222 + }
     223 + 
     224 + tpl := `var (
     225 + bpGlobal, bperr = %sNewBananaPhone(%s%s)
     226 +)`
     227 + return fmt.Sprintf(tpl, src.BananaPhonedot(), src.BananaPhonedot(), src.Mode)
     228 +}
     229 + 
     230 +func setmode(mode string) string {
     231 + switch mode {
     232 + case "memory":
     233 + return "MemoryBananaPhoneMode"
     234 + case "disk":
     235 + return "DiskBananaPhoneMode"
     236 + case "raw":
     237 + return "raw"
     238 + }
     239 + return "AutoBananaPhoneMode"
     240 +}
     241 + 
  • ■ ■ ■ ■ ■ ■
    cmd/mkdirectwinsyscall/srcTemplate.go
     1 +package main
     2 + 
     3 +const srcTemplate = `
     4 +{{define "main"}}// Code generated by 'go generate'; DO NOT EDIT.
     5 + 
     6 +package {{packagename}}
     7 +import (
     8 + {{range .StdLibImports}}"{{.}}"
     9 + {{end}}
     10 + {{range .ExternalImports}}"{{.}}"
     11 + {{end}}
     12 + {{.BananaImport}}
     13 +)
     14 + 
     15 +var _ unsafe.Pointer
     16 + 
     17 +{{.VarBlock}}
     18 + 
     19 +{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
     20 +{{end}}
     21 + 
     22 +{{define "funcbody"}}
     23 +func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
     24 + {{.BananaLoader}}
     25 +{{template "tmpvars" .}} {{template "syscall" .}} {{template "tmpvarsreadback" .}}
     26 +{{template "seterror" .}}{{template "printtrace" .}} return
     27 +}
     28 +{{end}}
     29 + 
     30 + 
     31 +{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
     32 + 
     33 +{{define "bananaloader"}} {{end}}
     34 + 
     35 +{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}}
     36 +{{end}}{{end}}{{end}}
     37 + 
     38 +{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.BananaphoneSyscall}}(sysid, {{.SyscallParamList}}){{end}}
     39 + 
     40 +{{define "tmpvarsreadback"}}{{range .Params}}{{if .TmpVarReadbackCode}}
     41 +{{.TmpVarReadbackCode}}{{end}}{{end}}{{end}}
     42 + 
     43 +{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}}
     44 +{{end}}{{end}}
     45 + 
     46 +{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
     47 +{{end}}{{end}}
     48 + 
     49 +`
     50 + 
  • ■ ■ ■ ■ ■ ■
    example/calcshellcode/main.go
    skipped 1 lines
    2 2   
    3 3  import (
    4 4   "fmt"
     5 + "syscall"
     6 + "unsafe"
    5 7   
    6 8   bananaphone "github.com/C-Sto/BananaPhone/pkg/BananaPhone"
    7 9  )
    skipped 38 lines
    46 48   if e != nil {
    47 49   panic(e)
    48 50   }
    49  - //use the createthread helper function to.. create thread.
    50  - bananaphone.CreateThread(shellcode, uintptr(0xffffffffffffffff), alloc, protect, createthread)
     51 + 
     52 + createThread(shellcode, uintptr(0xffffffffffffffff), alloc, protect, createthread)
     53 +}
     54 + 
     55 +func createThread(shellcode []byte, handle uintptr, NtAllocateVirtualMemorySysid, NtProtectVirtualMemorySysid, NtCreateThreadExSysid uint16) {
     56 + 
     57 + const (
     58 + thisThread = uintptr(0xffffffffffffffff) //special macro that says 'use this thread/process' when provided as a handle.
     59 + memCommit = uintptr(0x00001000)
     60 + memreserve = uintptr(0x00002000)
     61 + )
     62 + 
     63 + var baseA uintptr
     64 + regionsize := uintptr(len(shellcode))
     65 + r1, r := bananaphone.Syscall(
     66 + NtAllocateVirtualMemorySysid, //ntallocatevirtualmemory
     67 + handle,
     68 + uintptr(unsafe.Pointer(&baseA)),
     69 + 0,
     70 + uintptr(unsafe.Pointer(&regionsize)),
     71 + uintptr(memCommit|memreserve),
     72 + syscall.PAGE_READWRITE,
     73 + )
     74 + if r != nil {
     75 + fmt.Printf("1 %s %x\n", r, r1)
     76 + return
     77 + }
     78 + //write memory
     79 + bananaphone.WriteMemory(shellcode, baseA)
     80 + 
     81 + var oldprotect uintptr
     82 + r1, r = bananaphone.Syscall(
     83 + NtProtectVirtualMemorySysid, //NtProtectVirtualMemory
     84 + handle,
     85 + uintptr(unsafe.Pointer(&baseA)),
     86 + uintptr(unsafe.Pointer(&regionsize)),
     87 + syscall.PAGE_EXECUTE_READ,
     88 + uintptr(unsafe.Pointer(&oldprotect)),
     89 + )
     90 + if r != nil {
     91 + fmt.Printf("1 %s %x\n", r, r1)
     92 + return
     93 + }
     94 + var hhosthread uintptr
     95 + r1, r = bananaphone.Syscall(
     96 + NtCreateThreadExSysid, //NtCreateThreadEx
     97 + uintptr(unsafe.Pointer(&hhosthread)), //hthread
     98 + 0x1FFFFF, //desiredaccess
     99 + 0, //objattributes
     100 + handle, //processhandle
     101 + baseA, //lpstartaddress
     102 + 0, //lpparam
     103 + uintptr(0), //createsuspended
     104 + 0, //zerobits
     105 + 0, //sizeofstackcommit
     106 + 0, //sizeofstackreserve
     107 + 0, //lpbytesbuffer
     108 + )
     109 + syscall.WaitForSingleObject(syscall.Handle(hhosthread), 0xffffffff)
     110 + if r != nil {
     111 + fmt.Printf("1 %s %x\n", r, r1)
     112 + return
     113 + }
    51 114  }
    52 115   
  • ■ ■ ■ ■ ■ ■
    example/mkwinsyscall/main.go
     1 +package main
     2 + 
     3 +import (
     4 + "syscall"
     5 +)
     6 + 
     7 +func main() {
     8 + var phandle syscall.Handle
     9 + var baseA, zerob, regionsize uintptr
     10 + var alloctype, protect uint64
     11 + 
     12 + phandle = 0xffffffffffffffff //special macro to say 'this process'
     13 + regionsize = uintptr(0x50000)
     14 + protect = syscall.PAGE_EXECUTE_READWRITE
     15 + alloctype = 0x3000 //MEM_COMMIT | MEM_RESERVE
     16 + 
     17 + e := NtAllocateVirtualMemory(phandle, &baseA, zerob, &regionsize, alloctype, protect)
     18 + if e != nil {
     19 + panic(e)
     20 + }
     21 +}
     22 + 
  • ■ ■ ■ ■ ■ ■
    example/mkwinsyscall/syscall.go
     1 +package main
     2 + 
     3 +/*
     4 +__kernel_entry NTSYSCALLAPI NTSTATUS NtAllocateVirtualMemory(
     5 + HANDLE ProcessHandle,
     6 + PVOID *BaseAddress,
     7 + ULONG_PTR ZeroBits,
     8 + PSIZE_T RegionSize,
     9 + ULONG AllocationType,
     10 + ULONG Protect
     11 +);
     12 +*/
     13 +//dsys NtAllocateVirtualMemory(processHandle syscall.Handle, baseAddress *uintptr, zeroBits uintptr, regionSize *uintptr, allocationType uint64, protect uint64) (err error)
     14 + 
     15 +//go:generate go run github.com/C-Sto/BananaPhone/cmd/mkdirectwinsyscall -output zsyscall_windows.go syscall.go
     16 + 
  • ■ ■ ■ ■ ■ ■
    pkg/BananaPhone/functions.go
    1 1  package bananaphone
    2 2   
    3 3  import (
    4  - "fmt"
    5  - "syscall"
    6  - "time"
    7 4   "unsafe"
    8  - 
    9  - "golang.org/x/sys/windows"
    10 5  )
    11 6   
    12 7  //Syscall calls the system function specified by callid with n arguments. Works much the same as syscall.Syscall - return value is the call error code and optional error text. All args are uintptrs to make it easy.
    skipped 71 lines
    84 79   return getSysIDFromDisk(funcname, 0, false)
    85 80  }
    86 81   
    87  -const (
    88  - thisThread = uintptr(0xffffffffffffffff) //special macro that says 'use this thread/process' when provided as a handle.
    89  - memCommit = uintptr(0x00001000)
    90  - memreserve = uintptr(0x00002000)
    91  -)
    92  - 
    93  -//NtAllocateVirtualMemory is the function signature for the as-named syscall. Provide the syscall ID either dynamically (using a GetSysID function or similar), or hard-code it (but be aware that the ID changes in different versions of Windows).
    94  -/*
    95  - __kernel_entry NTSYSCALLAPI NTSTATUS NtAllocateVirtualMemory(
    96  - HANDLE ProcessHandle,
    97  - PVOID *BaseAddress,
    98  - ULONG_PTR ZeroBits,
    99  - PSIZE_T RegionSize,
    100  - ULONG AllocationType,
    101  - ULONG Protect
    102  - );
    103  -*/
    104  -func NtAllocateVirtualMemory(sysid uint16, processHandle uintptr, baseAddress *uintptr, zeroBits uintptr, regionSize *uintptr, allocationType, protect uintptr) (uint32, error) {
    105  - return Syscall(
    106  - sysid, //ntallocatevirtualmemory
    107  - thisThread,
    108  - uintptr(unsafe.Pointer(baseAddress)),
    109  - 0,
    110  - uintptr(unsafe.Pointer(regionSize)),
    111  - allocationType,
    112  - protect,
    113  - )
    114  -}
    115  - 
    116 82  //WriteMemory writes the provided memory to the specified memory address. Does **not** check permissions, may cause panic if memory is not writable etc.
    117 83  func WriteMemory(inbuf []byte, destination uintptr) {
    118 84   for index := uint32(0); index < uint32(len(inbuf)); index++ {
    skipped 3 lines
    122 88   }
    123 89  }
    124 90   
    125  -//NtProtectVirtualMemory is the function signature for the as-named syscall.
    126  -/*
    127  - NtProtectVirtualMemory(
    128  - IN HANDLE ProcessHandle,
    129  - IN OUT PVOID *BaseAddress,
    130  - IN OUT PULONG RegionSize,
    131  - IN ULONG NewProtect,
    132  - OUT PULONG OldProtect
    133  - );
    134  -*/
    135  -func NtProtectVirtualMemory(sysid uint16, processHandle uintptr, baseAddress, regionSize *uintptr, NewProtect uintptr, oldprotect *uintptr) (uint32, error) {
    136  - 
    137  - return Syscall(
    138  - sysid,
    139  - thisThread,
    140  - uintptr(unsafe.Pointer(baseAddress)),
    141  - uintptr(unsafe.Pointer(regionSize)),
    142  - NewProtect,
    143  - uintptr(unsafe.Pointer(oldprotect)),
    144  - )
    145  -}
    146  - 
    147  -//NtCreateThreadEx is the function signature for the as-named syscall. Provide the syscall ID either dynamically (using a GetSysID function or similar), or hard-code it (but be aware that the ID changes in different versions of Windows).
    148  -/*
    149  - NTSTATUS (WINAPI *LPFUN_NtCreateThreadEx)
    150  - (
    151  - OUT PHANDLE hThread,
    152  - IN ACCESS_MASK DesiredAccess,
    153  - IN LPVOID ObjectAttributes,
    154  - IN HANDLE ProcessHandle,
    155  - IN LPTHREAD_START_ROUTINE lpStartAddress,
    156  - IN LPVOID lpParameter,
    157  - IN BOOL CreateSuspended,
    158  - IN ULONG StackZeroBits,
    159  - IN ULONG SizeOfStackCommit,
    160  - IN ULONG SizeOfStackReserve,
    161  - OUT LPVOID lpBytesBuffer
    162  - );
    163  -*/
    164  -func NtCreateThreadEx(sysid uint16, hostThread *uintptr,
    165  - DesiredAccess, ObjectAttributes, ProcessHandle, LpStartAddress,
    166  - LpParameter, Createsuspended, StackZeroBits, sizeofstackcommit,
    167  - SizeOfStackReserve, lpBytesBuffer uintptr) (uint32, error) {
    168  - 
    169  - return Syscall(
    170  - sysid, //NtCreateThreadEx
    171  - uintptr(unsafe.Pointer(hostThread)), //hthread
    172  - DesiredAccess, //desiredaccess
    173  - ObjectAttributes, //objattributes
    174  - ProcessHandle, //processhandle
    175  - LpStartAddress, //lpstartaddress
    176  - LpParameter, //lpparam
    177  - Createsuspended, //createsuspended
    178  - StackZeroBits, //zerobits
    179  - sizeofstackcommit, //sizeofstackcommit
    180  - SizeOfStackReserve, //sizeofstackreserve
    181  - lpBytesBuffer, //lpbytesbuffer
    182  - )
    183  -}
    184  - 
    185  -//CreateThreadDisk executes shellcode in the current thread, resolving sysid's from disk. (See CreateThread for more details)
    186  -func CreateThreadDisk(shellcode []byte) {
    187  - ntalloc, _ := GetSysIDFromDisk("NtAllocateVirtualMemory")
    188  - ntprotect, _ := GetSysIDFromDisk("NtProtectVirtualMemory")
    189  - ntcreate, _ := GetSysIDFromDisk("NtCreateThreadEx")
    190  - CreateThread(shellcode, thisThread, ntalloc, ntprotect, ntcreate)
    191  -}
    192  - 
    193  -//CreateThreadMem executes shellcode in the current thread, resolving sysid's from memory. (See CreateThread for more details)
    194  -func CreateThreadMem(shellcode []byte) {
    195  - ntalloc, _ := GetSysIDFromMemory("NtAllocateVirtualMemory")
    196  - ntprotect, _ := GetSysIDFromMemory("NtProtectVirtualMemory")
    197  - ntcreate, _ := GetSysIDFromMemory("NtCreateThreadEx")
    198  - CreateThread(shellcode, thisThread, ntalloc, ntprotect, ntcreate)
    199  -}
    200  - 
    201  -const (
    202  - //ProcessAllAccess STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff
    203  - ProcessAllAccess = windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xfff
    204  -)
    205  - 
    206  -//CreateRemoteThreadMemory does CreateThread with the specified shellcode and PID, reading sysid's from memory. (See CreateRemoteThread for more info).
    207  -func CreateRemoteThreadMemory(shellcode []byte, pid uint32) {
    208  - h, _ := windows.OpenProcess(ProcessAllAccess, false, pid)
    209  - ntalloc, _ := GetSysIDFromMemory("NtAllocateVirtualMemory")
    210  - ntprotect, _ := GetSysIDFromMemory("NtProtectVirtualMemory")
    211  - ntcreate, _ := GetSysIDFromMemory("NtCreateThreadEx")
    212  - CreateThread(shellcode, uintptr(h), ntalloc, ntprotect, ntcreate)
    213  -}
    214  - 
    215  -//CreateRemoteThreadDisk does CreateThread with the specified shellcode and PID, reading sysid's from disk. (See CreateRemoteThread for more info).
    216  -func CreateRemoteThreadDisk(shellcode []byte, pid uint32) {
    217  - h, _ := windows.OpenProcess(ProcessAllAccess, false, pid)
    218  - ntalloc, _ := GetSysIDFromDisk("NtAllocateVirtualMemory")
    219  - ntprotect, _ := GetSysIDFromDisk("NtProtectVirtualMemory")
    220  - ntcreate, _ := GetSysIDFromDisk("NtCreateThreadEx")
    221  - CreateThread(shellcode, uintptr(h), ntalloc, ntprotect, ntcreate)
    222  -}
    223  - 
    224  -/*
    225  -CreateThread takes shellcode, and a handle, and performs NtAllocate, NtProtect, and finally an NtCreateThread. Provide Syscall ID's either dynamically (using a GetSysID function or similar), or hard-code it (but be aware that the ID changes in different versions of Windows).
    226  - 
    227  -**Fair warning**: there is no wait in here. Threads are hard, and creating a thread in a remote process seems to create a race condition of some sort that kills a bunch of stuff. YMMV, use with caution etc.
    228  - 
    229  -Relevant enums for each of the functions can be found below:
    230  - NtAllocateVirtualMemory
    231  - memCommit|memreserve,
    232  - syscall.PAGE_READWRITE,
    233  - 
    234  - NtProtectVirtualMemory
    235  - syscall.PAGE_EXECUTE_READ,
    236  - 
    237  - NtCreateThreadEx
    238  - 0x1FFFFF (THREAD_ALL_ACCESS)
    239  -*/
    240  -func CreateThread(shellcode []byte, handle uintptr, NtAllocateVirtualMemorySysid, NtProtectVirtualMemorySysid, NtCreateThreadExSysid uint16) {
    241  - var baseA uintptr
    242  - regionsize := uintptr(len(shellcode))
    243  - r, _ := NtAllocateVirtualMemory(
    244  - NtAllocateVirtualMemorySysid, //ntallocatevirtualmemory
    245  - handle,
    246  - &baseA,
    247  - 0,
    248  - &regionsize,
    249  - memCommit|memreserve,
    250  - syscall.PAGE_READWRITE,
    251  - )
    252  - if r != 0 {
    253  - fmt.Printf("1 %x\n", r)
    254  - return
    255  - }
    256  - //write memory
    257  - WriteMemory(shellcode, baseA)
    258  - 
    259  - var oldprotect uintptr
    260  - r, _ = NtProtectVirtualMemory(
    261  - NtProtectVirtualMemorySysid, //NtProtectVirtualMemory
    262  - handle,
    263  - &baseA,
    264  - &regionsize,
    265  - syscall.PAGE_EXECUTE_READ,
    266  - &oldprotect,
    267  - )
    268  - if r != 0 {
    269  - fmt.Printf("2 %x\n", r)
    270  - return
    271  - }
    272  - var hhosthread uintptr
    273  - r, _ = NtCreateThreadEx(
    274  - NtCreateThreadExSysid, //NtCreateThreadEx
    275  - &hhosthread, //hthread
    276  - 0x1FFFFF, //desiredaccess
    277  - 0, //objattributes
    278  - handle, //processhandle
    279  - baseA, //lpstartaddress
    280  - 0, //lpparam
    281  - uintptr(0), //createsuspended
    282  - 0, //zerobits
    283  - 0, //sizeofstackcommit
    284  - 0, //sizeofstackreserve
    285  - 0, //lpbytesbuffer
    286  - )
    287  - time.Sleep(time.Second * 2)
    288  - if r != 0 {
    289  - fmt.Printf("3 %x\n", r)
    290  - return
    291  - }
    292  -}
    293  - 
Please wait...
Page is in error, reload to recover