├── .gitignore ├── LICENSE ├── README.md ├── examples └── NtReadVirtualMemory │ └── main.go ├── go.mod ├── go.sum ├── img └── gopher.png └── pkg └── heaven ├── asm_386.s ├── functions.go ├── heaven.go ├── heaven_other.go ├── heaven_test.go ├── internal.go ├── ntdll.go └── types.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 aus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gopher Heaven 2 | 3 | [](img/gopher.png) 4 | 5 | __All gophers go to heaven__ 6 | 7 | gopherheaven is a Go implementation of the classic Heaven's Gate technique originally published by [roy g biv](https://vx-underground.org/archive/VxHeaven/lib/vrg02.html) on VX Heaven in 2009. gopherheaven can be used as an evasion technique to directly call 64-bit code from a 32-bit process. 8 | 9 | [@C-Sto](https://github.com/C-Sto) already [went to Go hell](https://github.com/C-Sto/BananaPhone) 😈, but @aus went to heaven. 😇 10 | 11 | ## Usage 12 | 13 | If you are familiar with GetModuleHandle, GetProcAddress, and Syscall on Windows, the process is largely the same. See [examples/](/examples) directory for more. The following example shows invoking 64-bit [NtReadVirtualMemory](http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FMemory%20Management%2FVirtual%20Memory%2FNtReadVirtualMemory.html) 14 | 15 | ```go 16 | ntdll, err := heaven.GetModuleHandle("ntdll.dll") 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | fn, err := heaven.GetProcAddress(ntdll, "NtReadVirtualMemory") 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | h := (uint64)(heaven.GetSelfHandle()) 27 | i := 6 28 | b := 3 29 | var read uint64 30 | 31 | errcode, err := heaven.Syscall( 32 | fn, 33 | h, 34 | uint64(uintptr(unsafe.Pointer(&i))), 35 | uint64(uintptr(unsafe.Pointer(&b))), 36 | 4, 37 | uint64(uintptr(unsafe.Pointer(&read))) 38 | ) 39 | ``` 40 | 41 | ## Build 42 | 43 | Make sure your architecture is set to `GOARCH=386` and that you are executing on x64 Windows system. gopherheaven does not currently support what I call reverse Heaven's Gate (executing 32-bit code from a 64-bit process). 44 | 45 | ## Background 46 | 47 | There's already alot of great publications on Heaven's Gate, so I will just you defer to these resources: 48 | 49 | - http://blog.rewolf.pl/blog/?p=102 50 | - https://vx-underground.org/archive/VxHeaven/lib/vrg02.html 51 | - http://www.alex-ionescu.com/?p=300 52 | - https://www.malwaretech.com/2013/06/rise-of-dual-architecture-usermode.html 53 | 54 | ## Why 55 | 56 | I asked myself several times. 57 | 58 | ## Other References 59 | 60 | - https://github.com/rwfpl/rewolf-wow64ext 61 | - https://github.com/JustasMasiulis/wow64pp 62 | - https://github.com/karinkasweet/Gopher-sticker-pack -------------------------------------------------------------------------------- /examples/NtReadVirtualMemory/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "unsafe" 7 | 8 | "github.com/aus/gopherheaven/pkg/heaven" 9 | ) 10 | 11 | func main() { 12 | ntdll, err := heaven.GetModuleHandle("ntdll.dll") 13 | if err != nil { 14 | fmt.Println(err) 15 | fmt.Scanln() 16 | os.Exit(1) 17 | } 18 | 19 | fmt.Printf("Module 64-bit Handle: 0x%016x\n", ntdll) 20 | 21 | fn, err := heaven.GetProcAddress(ntdll, "NtReadVirtualMemory") 22 | if err != nil { 23 | fmt.Println(err) 24 | fmt.Scanln() 25 | os.Exit(1) 26 | } 27 | 28 | fmt.Printf("NtReadVirtualMemory Addr: 0x%016x\n", ntdll) 29 | 30 | h := (uint64)(heaven.GetSelfHandle()) 31 | i := 6 32 | b := 3 33 | var read uint64 34 | 35 | fmt.Printf("Setup\n") 36 | fmt.Printf("`- handle: 0x%016X\n", h) 37 | fmt.Printf("`- i: %v @ 0x%016X\n", i, uint64(uintptr(unsafe.Pointer(&i)))) 38 | fmt.Printf("`- b: %v @ 0x%016X\n", b, uint64(uintptr(unsafe.Pointer(&b)))) 39 | 40 | fmt.Printf("Invoking NtReadVirtualMemory in 64-bit mode to read from &i and write to &b\n") 41 | 42 | errcode, err := heaven.Syscall(fn, h, uint64(uintptr(unsafe.Pointer(&i))), uint64(uintptr(unsafe.Pointer(&b))), 4, uint64(uintptr(unsafe.Pointer(&read)))) 43 | 44 | fmt.Printf("NtReadVirtualMemory result:\n") 45 | fmt.Printf("`- err: %v\n", err) 46 | fmt.Printf("`- errcode: %v\n", errcode) 47 | fmt.Printf("`- number of bytes read: %v\n", read) 48 | fmt.Printf("`- i: %v\n", i) 49 | fmt.Printf("`- b: %v\n", b) 50 | 51 | } 52 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aus/gopherheaven 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/Binject/debug v0.0.0-20210101210738-1b03ff50b8a5 7 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Binject/debug v0.0.0-20210101210738-1b03ff50b8a5 h1:uks5QpWybw0NHhiHQHJUWkz6BBY0mbhG6+FRicVDP9A= 2 | github.com/Binject/debug v0.0.0-20210101210738-1b03ff50b8a5/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ= 3 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= 4 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 5 | -------------------------------------------------------------------------------- /img/gopher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aus/gopherheaven/dbcb93e93e1cfa7b301600f9fd88d14b0157c2c2/img/gopher.png -------------------------------------------------------------------------------- /pkg/heaven/asm_386.s: -------------------------------------------------------------------------------- 1 | //func heaven(shellcodeAddr uintptr, functionAddr uint64, arg0 uint64, arg1 uint64, arg2 uint64, arg3 uint64, extSize uint64, extArgs uint64, pReturn uint32) 2 | TEXT ·heaven(SB),4,$64-64 3 | 4 | MOVL functionAddrA+4(FP), BX 5 | MOVL BX, 0(SP) 6 | MOVL functionAddrB+8(FP), BX 7 | MOVL BX, 4(SP) 8 | 9 | MOVL arg0A+12(FP), BX 10 | MOVL BX, 8(SP) 11 | MOVL arg0B+16(FP), BX 12 | MOVL BX, 12(SP) 13 | 14 | MOVL arg1A+20(FP), BX 15 | MOVL BX, 16(SP) 16 | MOVL arg1B+24(FP), BX 17 | MOVL BX, 20(SP) 18 | 19 | MOVL arg2A+28(FP), BX 20 | MOVL BX, 24(SP) 21 | MOVL arg2B+32(FP), BX 22 | MOVL BX, 28(SP) 23 | 24 | MOVL arg3A+36(FP), BX 25 | MOVL BX, 32(SP) 26 | MOVL arg3B+40(FP), BX 27 | MOVL BX, 36(SP) 28 | 29 | MOVL extSizeA+44(FP), BX 30 | MOVL BX, 40(SP) 31 | MOVL extSizeB+48(FP), BX 32 | MOVL BX, 44(SP) 33 | 34 | MOVL extArgsA+52(FP), BX 35 | MOVL BX, 48(SP) 36 | MOVL extArgsB+56(FP), BX 37 | MOVL BX, 52(SP) 38 | 39 | MOVL ret+60(FP), BX 40 | MOVL BX, 56(SP) 41 | 42 | MOVL shellcodeAddr+0(FP), AX 43 | CALL AX 44 | 45 | MOVL AX, ret+72(FP) 46 | RET 47 | -------------------------------------------------------------------------------- /pkg/heaven/functions.go: -------------------------------------------------------------------------------- 1 | // +build 386 2 | 3 | package heaven 4 | 5 | func heaven(shellcodeAddr uintptr, functionAddr uint64, arg0 uint64, arg1 uint64, arg2 uint64, arg3 uint64, extSize uint64, extArgs uint64, pReturn uint32) 6 | -------------------------------------------------------------------------------- /pkg/heaven/heaven.go: -------------------------------------------------------------------------------- 1 | // +build 386 2 | 3 | package heaven 4 | 5 | import ( 6 | "fmt" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | // GetModuleHandle returns a 64-bit handle to the specified module 12 | func GetModuleHandle(module string) (uint64, error) { 13 | return getModuleHandle(module) 14 | } 15 | 16 | // GetProcAddress returns the 64-bit address of the exported function from the given 64-bit module handle 17 | func GetProcAddress(handle uint64, proc string) (uint64, error) { 18 | return getProcAddress(handle, proc) 19 | } 20 | 21 | // Syscall initiates a 64-bit procedure at the specificed proc address 22 | func Syscall(proc uint64, args ...uint64) (errcode uint32, err error) { 23 | errcode = (uint32)(callFunction(proc, args...)) 24 | 25 | if errcode != 0 { 26 | err = fmt.Errorf("non-zero return from syscall") 27 | } 28 | return errcode, err 29 | } 30 | 31 | // GetSelfHandle returns a windows.Handle to the current process 32 | func GetSelfHandle() windows.Handle { 33 | var h windows.Handle 34 | windows.DuplicateHandle(windows.CurrentProcess(), windows.CurrentProcess(), windows.CurrentProcess(), &h, 0, false, 0x00000002) 35 | return h 36 | } 37 | -------------------------------------------------------------------------------- /pkg/heaven/heaven_other.go: -------------------------------------------------------------------------------- 1 | // +build !386 2 | 3 | package heaven 4 | 5 | import ( 6 | "fmt" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | // GetModuleHandle returns a 64-bit handle to the specified module 12 | func GetModuleHandle(module string) (uint64, error) { 13 | return 0, fmt.Errorf("unimplemented for this os/arch") 14 | } 15 | 16 | // GetProcAddress returns the 64-bit address of the exported function from the given 64-bit module handle 17 | func GetProcAddress(handle uint64, proc string) (uint64, error) { 18 | return 0, fmt.Errorf("unimplemented for this os/arch") 19 | } 20 | 21 | // Syscall initiates a 64-bit procedure at the specificed proc address 22 | func Syscall(proc uint64, args ...uint64) (errcode uint32, err error) { 23 | return 0, fmt.Errorf("unimplemented for this os/arch") 24 | } 25 | 26 | // GetSelfHandle returns a windows.Handle to the current process 27 | func GetSelfHandle() windows.Handle { 28 | return 0 29 | } 30 | -------------------------------------------------------------------------------- /pkg/heaven/heaven_test.go: -------------------------------------------------------------------------------- 1 | package heaven 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | "unsafe" 7 | ) 8 | 9 | func TestHeaven(t *testing.T) { 10 | ntdll, err := GetModuleHandle("ntdll.dll") 11 | if err != nil { 12 | t.Errorf("GetModuleHandle failed: %v", err) 13 | } 14 | 15 | fn, err := GetProcAddress(ntdll, "NtReadVirtualMemory") 16 | if err != nil { 17 | t.Errorf("GetProcAddress failed: %v", err) 18 | } 19 | 20 | h := (uint64)(GetSelfHandle()) 21 | i := 6 22 | b := 20 23 | var read uint64 24 | 25 | for idx := 0; idx < 200; idx++ { 26 | read = 0 27 | i = rand.Intn(100) 28 | b = rand.Intn(100) 29 | for j := 0; j < 60; j++ { 30 | _, err = Syscall(fn, h, uint64(uintptr(unsafe.Pointer(&i))), uint64(uintptr(unsafe.Pointer(&b))), 4, uint64(uintptr(unsafe.Pointer(&read)))) 31 | 32 | if read != 4 { 33 | t.Errorf("Syscall failed: NtReadVirtualMemory() expected to read 4 bytes but read %v", read) 34 | return 35 | } 36 | 37 | if i != b { 38 | t.Errorf("Syscall failed: NtReadVirtualMemory() buffer mismatch i=%v b=%v", i, b) 39 | return 40 | } 41 | 42 | } 43 | } 44 | 45 | if read != 4 { 46 | t.Errorf("Syscall failed: NtReadVirtualMemory() expected to read 4 bytes but read %v", read) 47 | } 48 | 49 | if i != b { 50 | t.Errorf("Syscall failed: NtReadVirtualMemory() buffer mismatch i=%v b=%v", i, b) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /pkg/heaven/internal.go: -------------------------------------------------------------------------------- 1 | // +build 386 2 | 3 | package heaven 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "syscall" 9 | "unsafe" 10 | 11 | "github.com/Binject/debug/pe" 12 | "golang.org/x/sys/windows" 13 | ) 14 | 15 | var ntdllSize uint32 16 | 17 | func getModuleHandle(module string) (uint64, error) { 18 | var h windows.Handle 19 | 20 | // Query 64-bit process information 21 | pInfo := PROCESS_BASIC_INFORMATION64{} 22 | size := uint32(unsafe.Sizeof(pInfo)) 23 | 24 | h = GetSelfHandle() 25 | err := NtWow64QueryInformationProcess64(Handle(h), 0, windows.Pointer(unsafe.Pointer(&pInfo)), size, nil) 26 | if err != nil { 27 | return 0, fmt.Errorf("Could not get x64 module handle: NtWow64QueryInformationProcess64, %v", err) 28 | } 29 | windows.CloseHandle(h) 30 | 31 | // Read and build PEB64 32 | peb := PEB64{} 33 | 34 | h = GetSelfHandle() 35 | err = NtWow64ReadVirtualMemory64(Handle(h), pInfo.PebBaseAddress, windows.Pointer(unsafe.Pointer(&peb)), uint64(unsafe.Sizeof(peb)), nil) 36 | if err != nil { 37 | return 0, fmt.Errorf("Could not get x64 module handle: NtWow64ReadVirtualMemory64(peb), %v", err) 38 | } 39 | windows.CloseHandle(h) 40 | 41 | // Read and build ldr 42 | ldr := PEB_LDR_DATA64{} 43 | h = GetSelfHandle() 44 | err = NtWow64ReadVirtualMemory64(Handle(h), (peb.LdrData), windows.Pointer(unsafe.Pointer(&ldr)), uint64(unsafe.Sizeof(ldr)), nil) 45 | if err != nil { 46 | return 0, fmt.Errorf("Could not get x64 module handle: NtWow64ReadVirtualMemory64(head), %v", err) 47 | } 48 | windows.CloseHandle(h) 49 | 50 | // Read and build ldr data 51 | head := LDR_DATA_TABLE_ENTRY64{} 52 | head.InLoadOrderLinks.Flink = ldr.InLoadOrderModuleList.Flink 53 | 54 | lastEntry := peb.LdrData + 0x10 55 | for head.InLoadOrderLinks.Flink != lastEntry { 56 | 57 | h = GetSelfHandle() 58 | err = NtWow64ReadVirtualMemory64(Handle(h), head.InLoadOrderLinks.Flink, windows.Pointer(unsafe.Pointer(&head)), uint64(unsafe.Sizeof(head)), nil) 59 | if err != nil { 60 | return 0, fmt.Errorf("Could not get x64 module handle: NtWow64ReadVirtualMemory64(head loop), %v", err) 61 | } 62 | windows.CloseHandle(h) 63 | 64 | otherModLen := head.BaseDllName.Length / 2 //sizeof(wchar_t) 65 | if otherModLen != uint16(len(module)) { 66 | continue 67 | } 68 | 69 | // length match found, now check name 70 | // read BaseDllName.Buffer to buffer, convert from UTF16 to string 71 | 72 | var buff [256]uint16 73 | 74 | h = GetSelfHandle() 75 | err = NtWow64ReadVirtualMemory64(Handle(h), head.BaseDllName.Buffer, windows.Pointer(unsafe.Pointer(&buff)), uint64(head.BaseDllName.Length), nil) 76 | if err != nil { 77 | return 0, fmt.Errorf("Could not get x64 module handle: NtWow64ReadVirtualMemory64(otherModName), %v", err) 78 | } 79 | windows.CloseHandle(h) 80 | 81 | otherModName := windows.UTF16PtrToString(&buff[0]) 82 | 83 | // check if module matches requested module, return or continue 84 | 85 | if otherModName == module { 86 | // hacky af 87 | if module == "ntdll.dll" { 88 | ntdllSize = head.SizeOfImage 89 | } 90 | return head.DllBase, nil 91 | } 92 | 93 | } 94 | 95 | return 0, fmt.Errorf("Could not get x64 module handle: module not found") 96 | } 97 | 98 | func getProcAddress(handle uint64, proc string) (uint64, error) { 99 | // get ldr proc base address 100 | ldrProcBaseAddress, err := getLdrProcedureAddress() 101 | if err != nil { 102 | return 0, err 103 | } 104 | 105 | // NtReadVirtualMemory 106 | // convert requested proc string to ANSI_STRING_WOW64 107 | buf := []byte(proc) 108 | pBuf := uint64((uintptr)(unsafe.Pointer(&buf[0]))) 109 | aProc := ANSI_STRING_WOW64{ 110 | Length: uint16(len(buf)), 111 | MaximumLength: uint16(len(buf)), 112 | Buffer: pBuf, 113 | } 114 | 115 | pProc := uint64((uintptr)(unsafe.Pointer(&aProc))) 116 | 117 | /* 118 | fmt.Println("callFunction(ldrProcBaseAddress, handle, pProc)") 119 | fmt.Printf("`- ldrProcBaseAddress:\t0x%016X\n", ldrProcBaseAddress) 120 | fmt.Printf("`- handle:\t\t0x%016X\n", handle) 121 | fmt.Printf("`- &proc:\t\t0x%016X\n", pProc) 122 | fmt.Printf(" `- proc.Length: 0x%04X\n", aProc.Length) 123 | fmt.Printf(" `- proc.Max: 0x%04X\n", aProc.MaximumLength) 124 | fmt.Printf(" `- proc.Buffer: 0x%016X\n", aProc.Buffer) 125 | */ 126 | 127 | // invoke LdrGetProcedureAddress passing proc unicode -> return Proc address 128 | var ret uint64 129 | fnret := callFunction(ldrProcBaseAddress, 130 | handle, 131 | pProc, 132 | 0, 133 | uint64((uintptr)(unsafe.Pointer(&ret))), 134 | ) 135 | 136 | if fnret > 0 { 137 | return 0, fmt.Errorf("Error in callFunction() ret: %v fnret: %v", ret, fnret) 138 | } 139 | 140 | return ret, nil 141 | } 142 | 143 | func getLdrProcedureAddress() (uint64, error) { 144 | var h windows.Handle 145 | var ntdll *pe.File 146 | 147 | ntdllBase, err := getModuleHandle("ntdll.dll") 148 | if err != nil { 149 | return 0, err 150 | } 151 | 152 | // read memory @ ntdllBase to size 153 | // TODO: clean this up 154 | var b [10000000]byte 155 | h = GetSelfHandle() 156 | err = NtWow64ReadVirtualMemory64(Handle(h), ntdllBase, windows.Pointer(unsafe.Pointer(&b)), uint64(ntdllSize), nil) 157 | if err != nil { 158 | return 0, fmt.Errorf("Could not get ldr procedure address: NtWow64ReadVirtualMemory64(ldr), %v", err) 159 | } 160 | windows.CloseHandle(h) 161 | 162 | buff := bytes.NewReader(b[:]) 163 | 164 | ntdll, err = pe.NewFileFromMemory(buff) 165 | if err != nil { 166 | return 0, fmt.Errorf("Could not parse PE: %v", err) 167 | } 168 | 169 | exports, err := ntdll.Exports() 170 | if err != nil { 171 | return 0, err 172 | } 173 | 174 | for _, export := range exports { 175 | if export.Name == "LdrGetProcedureAddress" { 176 | return uint64(export.VirtualAddress) + ntdllBase, nil 177 | } 178 | } 179 | 180 | return 0, fmt.Errorf("LdrGetProcedureAddress not found") 181 | 182 | } 183 | 184 | func callFunction(proc uint64, args ...uint64) (errorcode uint64) { 185 | // probably should move this to a const and only allocate once instead of each time 186 | // via: https://github.com/JustasMasiulis/wow64pp 187 | shellcode := []byte{ 188 | 0x55, // push ebp 189 | 0x89, 0xE5, // mov ebp, esp 190 | 191 | 0x83, 0xE4, 0xF0, // and esp, 0xFFFFFFF0 192 | 193 | // enter 64 bit mode 194 | 0x6A, 0x33, // push 33h 195 | 0xE8, 0x00, 0x00, 0x00, 0x00, // call 196 | 0x83, 0x04, 0x24, 0x05, // add dword ptr [rsp],5 197 | 0xCB, // retf 198 | 199 | 0x67, 0x48, 0x8B, 0x4D, 16, // mov rcx, [ebp + 16] arg0 200 | 0x67, 0x48, 0x8B, 0x55, 24, // mov rdx, [ebp + 24] arg1 201 | 0x67, 0x4C, 0x8B, 0x45, 32, // mov r8, [ebp + 32] arg2 202 | 0x67, 0x4C, 0x8B, 0x4D, 40, // mov r9, [ebp + 40] arg3 203 | 204 | 0x67, 0x48, 0x8B, 0x45, 48, // mov rax, [ebp + 48] extSize 205 | 206 | 0xA8, 0x01, // test al, 1 207 | 0x75, 0x04, // jne 8, _no_adjust 208 | 0x48, 0x83, 0xEC, 0x08, // sub rsp, 8 209 | // _no adjust: 210 | 0x57, // push rdi 211 | 0x67, 0x48, 0x8B, 0x7D, 0x38, // mov rdi, [ebp + 56] extArgs 212 | 0x48, 0x85, 0xC0, // je _ls_e 213 | 0x74, 0x16, 0x48, 0x8D, 0x7C, 0xC7, 0xF8, // lea rdi,[rdi+rax*8-8] 214 | // _ls: 215 | 0x48, 0x85, 0xC0, // test rax, rax 216 | 0x74, 0x0C, // je _ls_e 217 | 0xFF, 0x37, // push [rdi] 218 | 0x48, 0x83, 0xEF, 0x08, // sub rdi, 8 219 | 0x48, 0x83, 0xE8, 0x01, // sub rax, 1 220 | 0xEB, 0xEF, // jmp _ls 221 | // _ls_e: 222 | 0x67, 0x8B, 0x7D, 0x40, // mov edi, [ebp + 64] 223 | 0x48, 0x83, 0xEC, 0x20, // sub rsp, 0x20 224 | 0x67, 0xFF, 0x55, 0x08, // call; [ebp + 0x8] func 225 | 0x67, 0x89, 0x07, // mov [edi], eax 226 | 0x67, 0x48, 0x8B, 0x4D, 0x30, // mov rcx, [ebp+48] 227 | 0x48, 0x8D, 0x64, 0xCC, 0x20, // lea rsp,[rsp+rcx*8+0x20] 228 | 0x5F, // pop rdi 229 | 230 | // exit 64 bit mode 231 | 0xE8, 0, 0, 0, 0, 0xC7, 0x44, 0x24, 4, 0x23, 0, 0, 0, 0x83, 4, 0x24, 0xD, 0xCB, 232 | 233 | 0x66, 0x8C, 0xD8, // mov ax, ds 234 | 0x8E, 0xD0, // mov ss, eax 235 | 236 | 0x89, 0xEC, // mov esp, ebp 237 | 0x5D, // pop ebp 238 | 0xC3, // ret 239 | } 240 | 241 | kernel32 := syscall.MustLoadDLL("kernel32.dll") 242 | VirtualAlloc := kernel32.MustFindProc("VirtualAlloc") 243 | 244 | addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE) 245 | 246 | if err != nil && err.Error() != "The operation completed successfully." { 247 | syscall.Exit(0) 248 | } 249 | 250 | ntdll := windows.NewLazySystemDLL("ntdll.dll") 251 | RtlMoveMemory := ntdll.NewProc("RtlMoveMemory") 252 | _, _, err = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode))) 253 | 254 | var ret uint32 255 | pret := (uint32)(uintptr(unsafe.Pointer(&ret))) 256 | 257 | // prep args array with minimum 4 elements 258 | l := 4 259 | if len(args) > 4 { 260 | l = len(args) 261 | } 262 | arrArgs := make([]uint64, l) 263 | for i, a := range args { 264 | arrArgs[i] = a 265 | } 266 | 267 | // how many additional args after args[3] 268 | extSize := uint64(l - 4) 269 | 270 | // if more args, set extArgs to pointer of 5th arg 271 | extArgs := uint64(0) 272 | if extSize > 0 { 273 | extArgs = (uint64)(uintptr(unsafe.Pointer(&arrArgs[4]))) 274 | } 275 | 276 | /* 277 | fmt.Println("invoking heaven...") 278 | fmt.Printf("`- shellcode addr: 0x%08X\n", addr) 279 | fmt.Println(" `- args:") 280 | fmt.Printf(" `- proc: 0x%016X\n", proc) 281 | fmt.Printf(" `- arg0: 0x%016X\n", arrArgs[0]) 282 | fmt.Printf(" `- arg1: 0x%016X\n", arrArgs[1]) 283 | fmt.Printf(" `- arg2: 0x%016X\n", arrArgs[2]) 284 | fmt.Printf(" `- arg3: 0x%016X\n", arrArgs[3]) 285 | fmt.Printf(" `- extSize: 0x%016X\n", extSize) 286 | fmt.Printf(" `- extArgs: 0x%016X\n", extArgs) 287 | fmt.Printf(" `- &ret: 0x%08X\n", pret) 288 | fmt.Scanln() 289 | */ 290 | 291 | heaven(addr, proc, arrArgs[0], arrArgs[1], arrArgs[2], arrArgs[3], extSize, extArgs, pret) 292 | 293 | /* 294 | fmt.Println("we didn't crash <3") 295 | fmt.Printf("ret: 0x%016X\n", ret) 296 | fmt.Scanln() 297 | */ 298 | 299 | return uint64(ret) 300 | } 301 | -------------------------------------------------------------------------------- /pkg/heaven/ntdll.go: -------------------------------------------------------------------------------- 1 | package heaven 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | const ERROR_SUCCESS syscall.Errno = 0 12 | const ptrSize = unsafe.Sizeof(uintptr(0)) 13 | 14 | type Handle = syscall.Handle 15 | 16 | var ( 17 | ntdll *windows.DLL 18 | ntWow64QueryInformationProcess64 *windows.Proc 19 | ntWow64ReadVirtualMemory64 *windows.Proc 20 | ) 21 | 22 | func init() { 23 | var err error 24 | ntdll, err = windows.LoadDLL("ntdll.dll") 25 | if err == nil { 26 | ntWow64QueryInformationProcess64, _ = ntdll.FindProc("NtWow64QueryInformationProcess64") 27 | ntWow64ReadVirtualMemory64, _ = ntdll.FindProc("NtWow64ReadVirtualMemory64") 28 | } 29 | } 30 | 31 | func NtWow64QueryInformationProcess64(processHandle Handle, processInformationClass int32, 32 | processInformation windows.Pointer, processInformationLength uint32, returnLength *uint32) error { 33 | 34 | if ntWow64QueryInformationProcess64 == nil { 35 | return fmt.Errorf("ntWow64QueryInformationProcess64==nil") 36 | } 37 | 38 | r1, _, err := ntWow64QueryInformationProcess64.Call(uintptr(processHandle), uintptr(processInformationClass), 39 | uintptr(unsafe.Pointer(processInformation)), uintptr(processInformationLength), 40 | uintptr(unsafe.Pointer(returnLength))) 41 | 42 | if int(r1) < 0 { 43 | if err != ERROR_SUCCESS { 44 | return err 45 | } else { 46 | return syscall.EINVAL 47 | } 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func NtWow64ReadVirtualMemory64(processHandle Handle, baseAddress uint64, 54 | bufferData windows.Pointer, bufferSize uint64, returnSize *uint64) error { 55 | 56 | if ntWow64ReadVirtualMemory64 == nil { 57 | return fmt.Errorf("ntWow64ReadVirtualMemory64==nil") 58 | } 59 | 60 | var r1 uintptr 61 | var err error 62 | 63 | // this shouldnt ever happen 64 | if ptrSize == 8 { 65 | r1, _, err = ntWow64ReadVirtualMemory64.Call(uintptr(processHandle), uintptr(baseAddress), 66 | uintptr(unsafe.Pointer(bufferData)), uintptr(bufferSize), uintptr(unsafe.Pointer(returnSize))) 67 | } else { 68 | r1, _, err = ntWow64ReadVirtualMemory64.Call(uintptr(processHandle), 69 | uintptr(baseAddress&0xFFFFFFFF), 70 | uintptr(baseAddress>>32), 71 | uintptr(unsafe.Pointer(bufferData)), 72 | uintptr(bufferSize), 73 | uintptr(0), 74 | uintptr(unsafe.Pointer(returnSize))) 75 | } 76 | 77 | if int(r1) < 0 { 78 | if err != ERROR_SUCCESS { 79 | return err 80 | } else { 81 | return syscall.EINVAL 82 | } 83 | } 84 | 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /pkg/heaven/types.go: -------------------------------------------------------------------------------- 1 | package heaven 2 | 3 | type LIST_ENTRY struct { 4 | Flink uint64 5 | Blink uint64 6 | } 7 | 8 | type PEB64 struct { 9 | Reserved [24]byte 10 | LdrData uint64 11 | ProcessParameters uint64 12 | } 13 | 14 | type PEB_LDR_DATA64 struct { 15 | Length uint32 16 | Initialized uint32 17 | SsHandle uint64 18 | InLoadOrderModuleList LIST_ENTRY 19 | } 20 | 21 | type UNICODE_STRING_WOW64 struct { 22 | Length uint16 23 | MaximumLength uint16 24 | Buffer uint64 25 | } 26 | 27 | type UNICODE_STRING_WTF struct { 28 | Length uint16 29 | MaximumLength uint16 30 | WtfIsThis uint32 31 | Buffer uint64 32 | } 33 | 34 | type ANSI_STRING_WOW64 struct { 35 | Length uint16 36 | MaximumLength uint16 37 | WtfIsThis uint32 38 | Buffer uint64 39 | } 40 | 41 | type LDR_DATA_TABLE_ENTRY64 struct { 42 | InLoadOrderLinks LIST_ENTRY 43 | InMemoryOrderLinks LIST_ENTRY 44 | InInitializationOrderLinks LIST_ENTRY 45 | DllBase uint64 46 | EntryPoint uint64 47 | SizeOfImage uint32 48 | Dummy uint64 49 | FullDllName UNICODE_STRING_WOW64 50 | BaseDllName UNICODE_STRING_WTF // [Length][Max][??extra 4 bytes??][Buffer] 51 | } 52 | 53 | type PROCESS_BASIC_INFORMATION64 struct { 54 | ExitStatus uint64 55 | PebBaseAddress uint64 56 | AffinityMask uint64 57 | BasePriority uint64 58 | UniqueProcessId uint64 59 | InheritedFromUniqueProcessId uint64 60 | } 61 | --------------------------------------------------------------------------------