├── .gitignore ├── README.md ├── cmd └── injgo │ └── main.go ├── go.mod ├── go.sum ├── inject.go ├── inject_windows.go ├── pkg └── w32 │ ├── advapi32.go │ ├── constants.go │ ├── kernel32.go │ ├── typedef.go │ ├── utils.go │ └── version.go └── process_windows.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/go,linux,macos,windows 3 | # Edit at https://www.gitignore.io/?templates=go,linux,macos,windows 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | ### Go Patch ### 23 | /vendor/ 24 | /Godeps/ 25 | 26 | ### Linux ### 27 | *~ 28 | 29 | # temporary files which can be created if a process still has a handle open of a deleted file 30 | .fuse_hidden* 31 | 32 | # KDE directory preferences 33 | .directory 34 | 35 | # Linux trash folder which might appear on any partition or disk 36 | .Trash-* 37 | 38 | # .nfs files are created when an open file is removed but is still being accessed 39 | .nfs* 40 | 41 | ### macOS ### 42 | # General 43 | .DS_Store 44 | .AppleDouble 45 | .LSOverride 46 | 47 | # Icon must end with two \r 48 | Icon 49 | 50 | # Thumbnails 51 | ._* 52 | 53 | # Files that might appear in the root of a volume 54 | .DocumentRevisions-V100 55 | .fseventsd 56 | .Spotlight-V100 57 | .TemporaryItems 58 | .Trashes 59 | .VolumeIcon.icns 60 | .com.apple.timemachine.donotpresent 61 | 62 | # Directories potentially created on remote AFP share 63 | .AppleDB 64 | .AppleDesktop 65 | Network Trash Folder 66 | Temporary Items 67 | .apdisk 68 | 69 | ### Windows ### 70 | # Windows thumbnail cache files 71 | Thumbs.db 72 | Thumbs.db:encryptable 73 | ehthumbs.db 74 | ehthumbs_vista.db 75 | 76 | # Dump file 77 | *.stackdump 78 | 79 | # Folder config file 80 | [Dd]esktop.ini 81 | 82 | # Recycle Bin used on file shares 83 | $RECYCLE.BIN/ 84 | 85 | # Windows Installer files 86 | *.cab 87 | *.msi 88 | *.msix 89 | *.msm 90 | *.msp 91 | 92 | # Windows shortcuts 93 | *.lnk 94 | 95 | # End of https://www.gitignore.io/api/go,linux,macos,windows 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # `injgo` 4 | 5 | **Injgo** is a tool for dynamic library injecting which written in Golang. 6 | 7 | 8 | [![](https://img.shields.io/travis/jiusanzhou/injgo.svg?label=build)](https://travis-ci.org/jiusanzhou/injgo) [![](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/jiusanzhou/injgo) [![](https://goreportcard.com/badge/github.com/jiusanzhou/injgo)](https://goreportcard.com/report/jiusanzhou/injgo) [![@Zoe on Twitter](https://img.shields.io/badge/twitter-@jiusanzhou-55acee.svg)](https://twitter.com/jiusanzhou "@Zoe on Twitter") [![InjGo on Sourcegraph](https://sourcegraph.com/github.com/jiusanzhou/injgo/-/badge.svg)](https://sourcegraph.com/github.com/jiusanzhou/injgo?badge "InjGo on Sourcegraph") 9 | 10 |
11 | 12 | |If you are a rustacean, try the Rust version: [injrs 🦀](https://github.com/jiusanzhou/injrs)| 13 | |:---| 14 | 15 | ### Features 16 | 17 | - **Pure `Go`** 18 | - **Zero dependency** 19 | - **Simple usage** 20 | 21 | ### Usage 22 | 23 | You can use `injgo` as a cli tool. 24 | 25 | **1. Install** 26 | 27 | ```bash 28 | go get go.zoe.im/injgo/cmd/... 29 | ``` 30 | 31 | **2. Inject** 32 | 33 | ```bash 34 | injgo PROCESS_NAME/PROCESS_ID DLL... 35 | ``` 36 | 37 | Also, you can use `injgo` as library. 38 | 39 | ### API 40 | 41 | - `Inject(pid int, dllname string, replace bool) error` 42 | - `InjectByProcessName(name string, dll string, replace bool) error` 43 | 44 | ### TODO 45 | 46 | - [ ] Use injector to handle result 47 | - [ ] Unload injected DLLs 48 | -------------------------------------------------------------------------------- /cmd/injgo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strconv" 8 | 9 | "go.zoe.im/injgo" 10 | ) 11 | 12 | const ( 13 | helpContent = `injgo is a injector written in Go. 14 | USAGE: 15 | injgo PROCESS_NAME/PROCESS_ID Libraies... 16 | 17 | EXAMPLES: 18 | 1. Inject test.dll to process Calc.exe 19 | $ injgo Calc.exe test.dll 20 | 21 | 2. Inject test.dll and demo.dll to process with PID: 1888 22 | $ injgo 1888 test.dll demo.dll 23 | ` 24 | ) 25 | 26 | func main() { 27 | if len(os.Args) <= 2 { 28 | fmt.Println(helpContent) 29 | return 30 | } 31 | 32 | pid, err := strconv.Atoi(os.Args[1]) 33 | if err != nil { 34 | p, err := injgo.FindProcessByName(os.Args[1]) 35 | if err != nil { 36 | fmt.Println("can't find process:", os.Args[1], "error:", err) 37 | return 38 | } 39 | pid = p.ProcessID 40 | } 41 | 42 | // find pid and or 43 | 44 | fmt.Println("injector ", pid) 45 | for _, name := range os.Args[2:] { 46 | // check if file exits 47 | name, _ = filepath.Abs(name) 48 | err = injgo.Inject(pid, name, false) 49 | if err != nil { 50 | fmt.Println("inject ", name, "error:", err) 51 | } else { 52 | fmt.Println("inject ", name, "success") 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go.zoe.im/injgo 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiusanzhou/injgo/2bbf76ea2f4c37d85f90b6173d3e1d5bb185a344/go.sum -------------------------------------------------------------------------------- /inject.go: -------------------------------------------------------------------------------- 1 | // Package injgo is a package for injecting in golang. 2 | package injgo 3 | -------------------------------------------------------------------------------- /inject_windows.go: -------------------------------------------------------------------------------- 1 | package injgo 2 | 3 | import ( 4 | "errors" 5 | "unsafe" 6 | 7 | "go.zoe.im/injgo/pkg/w32" 8 | ) 9 | 10 | var ( 11 | ErrAlreadyInjected = errors.New("dll already injected") 12 | ErrModuleNotExits = errors.New("can't found module") 13 | ErrModuleSnapshot = errors.New("create module snapshot failed") 14 | ) 15 | 16 | // WARNING: only 386 arch works well. 17 | // 18 | // Inject is the function inject dynamic library to a process 19 | // 20 | // In windows, name is a file with dll extion.If the file 21 | // name exits, we will return error. 22 | // The workflow of injection in windows is: 23 | // 0. load kernel32.dll in current process. 24 | // 1. open target process T. 25 | // 2. malloc memory in T to store the name of the library. 26 | // 3. get address of function LoadLibraryA from kernel32.dll 27 | // in T. 28 | // 4. call CreateRemoteThread method in kernel32.dll to execute 29 | // LoadLibraryA in T. 30 | func Inject(pid int, dllname string, replace bool) error { 31 | 32 | // check is already injected 33 | if !replace && IsInjected(pid, dllname) { 34 | return ErrAlreadyInjected 35 | } 36 | 37 | // open process 38 | hdlr, err := w32.OpenProcess(w32.PROCESS_ALL_ACCESS, false, uint32(pid)) 39 | if err != nil { 40 | return err 41 | } 42 | defer w32.CloseHandle(hdlr) 43 | 44 | // malloc space to write dll name 45 | dlllen := len(dllname) 46 | dllnameaddr, err := w32.VirtualAllocEx(hdlr, 0, dlllen, w32.MEM_COMMIT, w32.PAGE_EXECUTE_READWRITE) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // write dll name 52 | err = w32.WriteProcessMemory(hdlr, uint32(dllnameaddr), []byte(dllname), uint(dlllen)) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // test 58 | tecase, _ := w32.ReadProcessMemory(hdlr, uint32(dllnameaddr), uint(dlllen)) 59 | if string(tecase) != dllname { 60 | return errors.New("write dll name error") 61 | } 62 | 63 | // get LoadLibraryA address in target process 64 | // TODO: can we get the address at from this process? 65 | lddladdr, err := w32.GetProcAddress(w32.GetModuleHandleA("kernel32.dll"), "LoadLibraryA") 66 | if err != nil { 67 | return err 68 | } 69 | 70 | // call remote process 71 | dllthread, _, err := w32.CreateRemoteThread(hdlr, nil, 0, uint32(lddladdr), dllnameaddr, 0) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | w32.CloseHandle(dllthread) 77 | 78 | return nil 79 | } 80 | 81 | // InjectByProcessName inject dll by process name 82 | func InjectByProcessName(name string, dll string, replace bool) error { 83 | p, err := FindProcessByName(name) 84 | if err != nil { 85 | return err 86 | } 87 | return Inject(p.ProcessID, dll, replace) 88 | } 89 | 90 | // FindModuleEntry find module entry of with dll name 91 | func FindModuleEntry(pid int, dllname string) (*w32.MODULEENTRY32, error) { 92 | hdlr := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPMODULE, uint32(pid)) 93 | defer w32.CloseHandle(hdlr) 94 | 95 | if hdlr == 0 { 96 | return nil, ErrModuleSnapshot 97 | } 98 | 99 | var entry w32.MODULEENTRY32 100 | entry.Size = uint32(unsafe.Sizeof(entry)) 101 | 102 | next := w32.Module32First(hdlr, &entry) 103 | 104 | for next { 105 | if w32.UTF16PtrToString(&entry.SzExePath[0]) == dllname { 106 | return &entry, nil 107 | } 108 | 109 | next = w32.Module32Next(hdlr, &entry) 110 | } 111 | 112 | return nil, ErrModuleNotExits 113 | } 114 | 115 | // IsInjected check is dll is already injected 116 | func IsInjected(pid int, dllname string) bool { 117 | _, err := FindModuleEntry(pid, dllname) 118 | return err == nil 119 | } 120 | -------------------------------------------------------------------------------- /pkg/w32/advapi32.go: -------------------------------------------------------------------------------- 1 | package w32 2 | 3 | // +build windows 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | var ( 11 | modadvapi32 = syscall.NewLazyDLL("advapi32.dll") 12 | 13 | procRegQueryValueEx = modadvapi32.NewProc("RegQueryValueExW") 14 | ) 15 | 16 | func RegQueryValueEx(hKey HKEY, subKey string) string { 17 | var bufLen uint32 18 | ret, _, _ := procRegQueryValueEx.Call( 19 | uintptr(hKey), 20 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))), 21 | uintptr(0), 22 | uintptr(0), 23 | uintptr(0), 24 | uintptr(unsafe.Pointer(&bufLen)), 25 | ) 26 | 27 | if bufLen == 0 { 28 | return "" 29 | } 30 | 31 | buf := make([]uint16, bufLen) 32 | ret, _, _ = procRegQueryValueEx.Call( 33 | uintptr(hKey), 34 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))), 35 | uintptr(0), 36 | uintptr(0), 37 | uintptr(unsafe.Pointer(&buf[0])), 38 | uintptr(unsafe.Pointer(&bufLen)), 39 | ) 40 | 41 | if ret != ERROR_SUCCESS { 42 | return "" 43 | } 44 | 45 | return syscall.UTF16ToString(buf) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/w32/constants.go: -------------------------------------------------------------------------------- 1 | package w32 2 | 3 | // +build windows 4 | 5 | const ( 6 | NO_ERROR = 0 7 | ERROR_SUCCESS = 0 8 | ) 9 | 10 | const INVALID_HANDLE = ^HANDLE(0) 11 | 12 | // CreateToolhelp32Snapshot flags 13 | const ( 14 | TH32CS_SNAPHEAPLIST = 0x00000001 15 | TH32CS_SNAPPROCESS = 0x00000002 16 | TH32CS_SNAPTHREAD = 0x00000004 17 | TH32CS_SNAPMODULE = 0x00000008 18 | TH32CS_SNAPMODULE32 = 0x00000010 19 | TH32CS_INHERIT = 0x80000000 20 | TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD 21 | ) 22 | 23 | const ( 24 | MAX_MODULE_NAME32 = 255 25 | MAX_PATH = 260 26 | ) 27 | 28 | const ( 29 | MEM_COMMIT = 0x00001000 30 | MEM_RESERVE = 0x00002000 31 | MEM_RESET = 0x00080000 32 | MEM_RESET_UNDO = 0x1000000 33 | 34 | MEM_LARGE_PAGES = 0x20000000 35 | MEM_PHYSICAL = 0x00400000 36 | MEM_TOP_DOWN = 0x00100000 37 | 38 | MEM_DECOMMIT = 0x4000 39 | MEM_RELEASE = 0x8000 40 | ) 41 | 42 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx 43 | const ( 44 | PAGE_EXECUTE = 0x10 45 | PAGE_EXECUTE_READ = 0x20 46 | PAGE_EXECUTE_READWRITE = 0x40 47 | PAGE_EXECUTE_WRITECOPY = 0x80 48 | PAGE_NOACCESS = 0x01 49 | PAGE_READWRITE = 0x04 50 | PAGE_WRITECOPY = 0x08 51 | PAGE_TARGETS_INVALID = 0x40000000 52 | PAGE_TARGETS_NO_UPDATE = 0x40000000 53 | ) 54 | 55 | //Process Access Rights 56 | //https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx 57 | const ( 58 | PROCESS_CREATE_PROCESS = 0x0080 //Required to create a process. 59 | PROCESS_CREATE_THREAD = 0x0002 //Required to create a thread. 60 | PROCESS_DUP_HANDLE = 0x0040 //Required to duplicate a handle using DuplicateHandle. 61 | PROCESS_QUERY_INFORMATION = 0x0400 //Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken). 62 | PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 //Required to retrieve certain information about a process (see GetExitCodeProcess, GetPriorityClass, IsProcessInJob, QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted 63 | PROCESS_SET_INFORMATION = 0x0200 //Required to set certain information about a process, such as its priority class (see SetPriorityClass). 64 | PROCESS_SET_QUOTA = 0x0100 //Required to set memory limits using SetProcessWorkingSetSize. 65 | PROCESS_SUSPEND_RESUME = 0x0800 //Required to suspend or resume a process. 66 | PROCESS_TERMINATE = 0x0001 //Required to terminate a process using TerminateProcess. 67 | PROCESS_VM_OPERATION = 0x0008 //Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory). 68 | PROCESS_VM_READ = 0x0010 //Required to read memory in a process using ReadProcessMemory. 69 | PROCESS_VM_WRITE = 0x0020 //Required to write to memory in a process using WriteProcessMemory. 70 | PROCESS_ALL_ACCESS = 2035711 //This is not recommended. 71 | SYNCHRONIZE = 0x00100000 72 | ) 73 | -------------------------------------------------------------------------------- /pkg/w32/kernel32.go: -------------------------------------------------------------------------------- 1 | package w32 2 | 3 | // +build windows 4 | 5 | import ( 6 | "encoding/binary" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | var ( 12 | modkernel32 = syscall.NewLazyDLL("kernel32.dll") 13 | 14 | procCloseHandle = modkernel32.NewProc("CloseHandle") 15 | procCreateProcessA = modkernel32.NewProc("CreateProcessA") 16 | procCreateProcessW = modkernel32.NewProc("CreateProcessW") 17 | procGetModuleHandleA = modkernel32.NewProc("GetModuleHandleA") 18 | procLoadLibraryA = modkernel32.NewProc("LoadLibraryA") 19 | ProcFreeLibrary = modkernel32.NewProc("FreeLibrary") 20 | procCreateRemoteThread = modkernel32.NewProc("CreateRemoteThread") 21 | procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot") 22 | procTerminateProcess = modkernel32.NewProc("TerminateProcess") 23 | procOpenProcess = modkernel32.NewProc("OpenProcess") 24 | procVirtualAlloc = modkernel32.NewProc("VirtualAlloc") 25 | procVirtualAllocEx = modkernel32.NewProc("VirtualAllocEx") 26 | procVirtualFreeEx = modkernel32.NewProc("VirtualFreeEx") 27 | procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory") 28 | procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory") 29 | procProcess32First = modkernel32.NewProc("Process32FirstW") 30 | procProcess32Next = modkernel32.NewProc("Process32NextW") 31 | procModule32First = modkernel32.NewProc("Module32FirstW") 32 | procModule32Next = modkernel32.NewProc("Module32NextW") 33 | ) 34 | 35 | func OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle HANDLE, err error) { 36 | inherit := 0 37 | if inheritHandle { 38 | inherit = 1 39 | } 40 | 41 | ret, _, err := procOpenProcess.Call( 42 | uintptr(desiredAccess), 43 | uintptr(inherit), 44 | uintptr(processId)) 45 | if err != nil && IsErrSuccess(err) { 46 | err = nil 47 | } 48 | handle = HANDLE(ret) 49 | return 50 | } 51 | 52 | func TerminateProcess(hProcess HANDLE, uExitCode uint) bool { 53 | ret, _, _ := procTerminateProcess.Call( 54 | uintptr(hProcess), 55 | uintptr(uExitCode)) 56 | return ret != 0 57 | } 58 | 59 | func CloseHandle(object HANDLE) bool { 60 | ret, _, _ := procCloseHandle.Call( 61 | uintptr(object)) 62 | return ret != 0 63 | } 64 | 65 | func CreateToolhelp32Snapshot(flags, processId uint32) HANDLE { 66 | ret, _, _ := procCreateToolhelp32Snapshot.Call( 67 | uintptr(flags), 68 | uintptr(processId)) 69 | 70 | if ret <= 0 { 71 | return HANDLE(0) 72 | } 73 | 74 | return HANDLE(ret) 75 | } 76 | 77 | func Process32First(snapshot HANDLE, pe *PROCESSENTRY32) bool { 78 | if pe.Size == 0 { 79 | pe.Size = uint32(unsafe.Sizeof(*pe)) 80 | } 81 | ret, _, _ := procProcess32First.Call( 82 | uintptr(snapshot), 83 | uintptr(unsafe.Pointer(pe))) 84 | 85 | return ret != 0 86 | } 87 | 88 | func Process32Next(snapshot HANDLE, pe *PROCESSENTRY32) bool { 89 | if pe.Size == 0 { 90 | pe.Size = uint32(unsafe.Sizeof(*pe)) 91 | } 92 | ret, _, _ := procProcess32Next.Call( 93 | uintptr(snapshot), 94 | uintptr(unsafe.Pointer(pe))) 95 | 96 | return ret != 0 97 | } 98 | 99 | func Module32First(snapshot HANDLE, me *MODULEENTRY32) bool { 100 | ret, _, _ := procModule32First.Call( 101 | uintptr(snapshot), 102 | uintptr(unsafe.Pointer(me))) 103 | 104 | return ret != 0 105 | } 106 | 107 | func Module32Next(snapshot HANDLE, me *MODULEENTRY32) bool { 108 | ret, _, _ := procModule32Next.Call( 109 | uintptr(snapshot), 110 | uintptr(unsafe.Pointer(me))) 111 | 112 | return ret != 0 113 | } 114 | 115 | func GetModuleHandleA(modulename string) uintptr { 116 | var mn uintptr 117 | if modulename == "" { 118 | mn = 0 119 | } else { 120 | bytes := []byte(modulename) 121 | mn = uintptr(unsafe.Pointer(&bytes[0])) 122 | } 123 | ret, _, _ := procGetModuleHandleA.Call(mn) 124 | return ret 125 | } 126 | 127 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx 128 | func CreateProcessA(lpApplicationName *string, 129 | lpCommandLine string, 130 | lpProcessAttributes *syscall.SecurityAttributes, 131 | lpThreadAttributes *syscall.SecurityAttributes, 132 | bInheritHandles bool, 133 | dwCreationFlags uint32, 134 | lpEnvironment *string, 135 | lpCurrentDirectory *uint16, 136 | lpStartupInfo *syscall.StartupInfo, 137 | lpProcessInformation *syscall.ProcessInformation) { 138 | 139 | inherit := 0 140 | if bInheritHandles { 141 | inherit = 1 142 | } 143 | 144 | procCreateProcessA.Call( 145 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(*lpApplicationName))), 146 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpCommandLine))), 147 | uintptr(unsafe.Pointer(lpProcessAttributes)), 148 | uintptr(unsafe.Pointer(lpThreadAttributes)), 149 | uintptr(inherit), 150 | uintptr(dwCreationFlags), 151 | uintptr(unsafe.Pointer(lpEnvironment)), 152 | uintptr(unsafe.Pointer(lpCurrentDirectory)), 153 | uintptr(unsafe.Pointer(lpStartupInfo)), 154 | uintptr(unsafe.Pointer(lpProcessInformation))) 155 | } 156 | 157 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366890(v=vs.85).aspx 158 | func VirtualAllocEx(hProcess HANDLE, lpAddress int, dwSize int, flAllocationType int, flProtect int) (addr uintptr, err error) { 159 | ret, _, err := procVirtualAllocEx.Call( 160 | uintptr(hProcess), // The handle to a process. 161 | uintptr(lpAddress), // The pointer that specifies a desired starting address for the region of pages that you want to allocate. 162 | uintptr(dwSize), // The size of the region of memory to allocate, in bytes. 163 | uintptr(flAllocationType), 164 | uintptr(flProtect)) 165 | if int(ret) == 0 { 166 | return ret, err 167 | } 168 | return ret, nil 169 | } 170 | 171 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx 172 | func VirtualAlloc(lpAddress int, dwSize int, flAllocationType int, flProtect int) (addr uintptr, err error) { 173 | ret, _, err := procVirtualAlloc.Call( 174 | uintptr(lpAddress), // The starting address of the region to allocate 175 | uintptr(dwSize), // The size of the region of memory to allocate, in bytes. 176 | uintptr(flAllocationType), 177 | uintptr(flProtect)) 178 | if int(ret) == 0 { 179 | return ret, err 180 | } 181 | return ret, nil 182 | } 183 | 184 | // https://github.com/AllenDang/w32/pull/62/commits/08a52ff508c6b2b9b9bf5ee476109b903dcf219d 185 | func VirtualFreeEx(hProcess HANDLE, lpAddress, dwSize uintptr, dwFreeType uint32) bool { 186 | ret, _, _ := procVirtualFreeEx.Call( 187 | uintptr(hProcess), 188 | lpAddress, 189 | dwSize, 190 | uintptr(dwFreeType), 191 | ) 192 | 193 | return ret != 0 194 | } 195 | 196 | func GetProcAddress(h uintptr, name string) (uintptr, error) { 197 | return syscall.GetProcAddress(syscall.Handle(h), name) 198 | } 199 | 200 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682437(v=vs.85).aspx 201 | // Credit: https://github.com/contester/runlib/blob/master/win32/win32_windows.go#L577 202 | func CreateRemoteThread(hprocess HANDLE, sa *syscall.SecurityAttributes, 203 | stackSize uint32, startAddress uint32, parameter uintptr, creationFlags uint32) (HANDLE, uint32, error) { 204 | var threadId uint32 205 | r1, _, e1 := procCreateRemoteThread.Call( 206 | uintptr(hprocess), 207 | uintptr(unsafe.Pointer(sa)), 208 | uintptr(stackSize), 209 | uintptr(startAddress), 210 | uintptr(parameter), 211 | uintptr(creationFlags), 212 | uintptr(unsafe.Pointer(&threadId))) 213 | 214 | if int(r1) == 0 { 215 | return INVALID_HANDLE, 0, e1 216 | } 217 | return HANDLE(r1), threadId, nil 218 | } 219 | 220 | //Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or the operation fails. 221 | //https://msdn.microsoft.com/en-us/library/windows/desktop/ms681674(v=vs.85).aspx 222 | func WriteProcessMemory(hProcess HANDLE, lpBaseAddress uint32, data []byte, size uint) (err error) { 223 | var numBytesRead uintptr 224 | 225 | _, _, err = procWriteProcessMemory.Call(uintptr(hProcess), 226 | uintptr(lpBaseAddress), 227 | uintptr(unsafe.Pointer(&data[0])), 228 | uintptr(size), 229 | uintptr(unsafe.Pointer(&numBytesRead))) 230 | if !IsErrSuccess(err) { 231 | return 232 | } 233 | err = nil 234 | return 235 | } 236 | 237 | //Write process memory with a source of uint32 238 | func WriteProcessMemoryAsUint32(hProcess HANDLE, lpBaseAddress uint32, data uint32) (err error) { 239 | 240 | bData := make([]byte, 4) 241 | binary.LittleEndian.PutUint32(bData, data) 242 | err = WriteProcessMemory(hProcess, lpBaseAddress, bData, 4) 243 | if err != nil { 244 | return 245 | } 246 | return 247 | } 248 | 249 | //Reads data from an area of memory in a specified process. The entire area to be read must be accessible or the operation fails. 250 | //https://msdn.microsoft.com/en-us/library/windows/desktop/ms680553(v=vs.85).aspx 251 | func ReadProcessMemory(hProcess HANDLE, lpBaseAddress uint32, size uint) (data []byte, err error) { 252 | var numBytesRead uintptr 253 | data = make([]byte, size) 254 | 255 | _, _, err = procReadProcessMemory.Call(uintptr(hProcess), 256 | uintptr(lpBaseAddress), 257 | uintptr(unsafe.Pointer(&data[0])), 258 | uintptr(size), 259 | uintptr(unsafe.Pointer(&numBytesRead))) 260 | if !IsErrSuccess(err) { 261 | return 262 | } 263 | err = nil 264 | return 265 | } 266 | 267 | // Read process memory and convert the returned data to uint32 268 | func ReadProcessMemoryAsUint32(hProcess HANDLE, lpBaseAddress uint32) (buffer uint32, err error) { 269 | data, err := ReadProcessMemory(hProcess, lpBaseAddress, 4) 270 | if err != nil { 271 | return 272 | } 273 | buffer = binary.LittleEndian.Uint32(data) 274 | return 275 | } 276 | -------------------------------------------------------------------------------- /pkg/w32/typedef.go: -------------------------------------------------------------------------------- 1 | package w32 2 | 3 | // +build windows 4 | 5 | type ( 6 | HANDLE uintptr 7 | HKEY HANDLE 8 | BOOL int32 9 | HMODULE HANDLE 10 | ULONG_PTR uintptr 11 | ) 12 | 13 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms684225.aspx 14 | type MODULEENTRY32 struct { 15 | Size uint32 16 | ModuleID uint32 17 | ProcessID uint32 18 | GlblcntUsage uint32 19 | ProccntUsage uint32 20 | ModBaseAddr *uint8 21 | ModBaseSize uint32 22 | HModule HMODULE 23 | SzModule [MAX_MODULE_NAME32 + 1]uint16 24 | SzExePath [MAX_PATH]uint16 25 | } 26 | 27 | // https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/ns-tlhelp32-tagprocessentry32 28 | type PROCESSENTRY32 struct { 29 | Size uint32 30 | CntUsage uint32 31 | ProcessID uint32 32 | DefaultHeapID ULONG_PTR 33 | ModuleID uint32 34 | Threads uint32 35 | ParentProcessID uint32 36 | PriClassBase int32 37 | Flags uint32 38 | ExeFile [MAX_PATH]uint16 39 | } 40 | 41 | // VS_FIXEDFILEINFO presents file information 42 | type VS_FIXEDFILEINFO struct { 43 | Signature uint32 44 | StrucVersion uint32 45 | FileVersionMS uint32 46 | FileVersionLS uint32 47 | ProductVersionMS uint32 48 | ProductVersionLS uint32 49 | FileFlagsMask uint32 50 | FileFlags uint32 51 | FileOS uint32 52 | FileType uint32 53 | FileSubtype uint32 54 | FileDateMS uint32 55 | FileDateLS uint32 56 | } 57 | 58 | // WinVersion window version struct 59 | type WinVersion struct { 60 | Major uint32 61 | Minor uint32 62 | Patch uint32 63 | Build uint32 64 | } 65 | -------------------------------------------------------------------------------- /pkg/w32/utils.go: -------------------------------------------------------------------------------- 1 | package w32 2 | 3 | // +build windows 4 | 5 | import ( 6 | "encoding/binary" 7 | "encoding/hex" 8 | "syscall" 9 | "unicode/utf16" 10 | "unsafe" 11 | ) 12 | 13 | func MakeIntResource(id uint16) *uint16 { 14 | return (*uint16)(unsafe.Pointer(uintptr(id))) 15 | } 16 | 17 | func LOWORD(dw uint32) uint16 { 18 | return uint16(dw) 19 | } 20 | 21 | func HIWORD(dw uint32) uint16 { 22 | return uint16(dw >> 16 & 0xffff) 23 | } 24 | 25 | func LOBYTE(word uint16) uint8 { 26 | return uint8(word) 27 | } 28 | 29 | func HIBYTE(word uint16) uint8 { 30 | return uint8(word >> 8 & 0xff) 31 | } 32 | 33 | func BoolToBOOL(value bool) BOOL { 34 | if value { 35 | return 1 36 | } 37 | 38 | return 0 39 | } 40 | 41 | func UTF16PtrToString(cstr *uint16) string { 42 | if cstr != nil { 43 | us := make([]uint16, 0, 256) 44 | for p := uintptr(unsafe.Pointer(cstr)); ; p += 2 { 45 | u := *(*uint16)(unsafe.Pointer(p)) 46 | if u == 0 { 47 | return string(utf16.Decode(us)) 48 | } 49 | us = append(us, u) 50 | } 51 | } 52 | 53 | return "" 54 | } 55 | 56 | func UTF16ToStringArray(s []uint16) []string { 57 | var ret []string 58 | begin: 59 | for i, v := range s { 60 | if v == 0 { 61 | tmp := s[0:i] 62 | ret = append(ret, string(utf16.Decode(tmp))) 63 | if i+2 < len(s) && s[i+1] != 0 { 64 | s = s[i+1:] 65 | goto begin 66 | } else { 67 | break 68 | } 69 | } 70 | } 71 | return ret 72 | } 73 | 74 | // Convert a hex string to uint32 75 | func HexToUint32(hexString string) (result uint32, err error) { 76 | var data []byte 77 | data, err = hex.DecodeString(hexString) 78 | if err == nil { 79 | result = binary.BigEndian.Uint32(data) 80 | return 81 | } 82 | if err != hex.ErrLength { 83 | return 84 | } 85 | hexString = "0" + hexString 86 | data, err = hex.DecodeString(hexString) 87 | if err == nil { 88 | result = binary.BigEndian.Uint32(data) 89 | } 90 | return 91 | } 92 | 93 | // IsErrSuccess checks if an "error" returned is actually the 94 | // success code 0x0 "The operation completed successfully." 95 | // 96 | // This is the optimal approach since the error messages are 97 | // localized depending on the OS language. 98 | func IsErrSuccess(err error) bool { 99 | if errno, ok := err.(syscall.Errno); ok { 100 | if errno == 0 { 101 | return true 102 | } 103 | } 104 | return false 105 | } 106 | -------------------------------------------------------------------------------- /pkg/w32/version.go: -------------------------------------------------------------------------------- 1 | package w32 2 | 3 | import ( 4 | "errors" 5 | "syscall" 6 | "unsafe" 7 | ) 8 | 9 | var ( 10 | version = syscall.NewLazyDLL("version.dll") 11 | getFileVersionInfoSize = version.NewProc("GetFileVersionInfoSizeW") 12 | getFileVersionInfo = version.NewProc("GetFileVersionInfoW") 13 | verQueryValue = version.NewProc("VerQueryValueW") 14 | ) 15 | 16 | // FileVersion concatenates FileVersionMS and FileVersionLS to a uint64 value. 17 | func (fi VS_FIXEDFILEINFO) FileVersion() uint64 { 18 | return uint64(fi.FileVersionMS)<<32 | uint64(fi.FileVersionLS) 19 | } 20 | 21 | func GetFileVersionInfoSize(path string) uint32 { 22 | ret, _, _ := getFileVersionInfoSize.Call( 23 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), 24 | 0, 25 | ) 26 | return uint32(ret) 27 | } 28 | 29 | func GetFileVersionInfo(path string, data []byte) bool { 30 | ret, _, _ := getFileVersionInfo.Call( 31 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), 32 | 0, 33 | uintptr(len(data)), 34 | uintptr(unsafe.Pointer(&data[0])), 35 | ) 36 | return ret != 0 37 | } 38 | 39 | // VerQueryValueRoot calls VerQueryValue 40 | // (https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx) 41 | // with `\` (root) to retieve the VS_FIXEDFILEINFO. 42 | func VerQueryValueRoot(block []byte) (VS_FIXEDFILEINFO, error) { 43 | var offset uintptr 44 | var length uint 45 | blockStart := unsafe.Pointer(&block[0]) 46 | ret, _, _ := verQueryValue.Call( 47 | uintptr(blockStart), 48 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(`\`))), 49 | uintptr(unsafe.Pointer(&offset)), 50 | uintptr(unsafe.Pointer(&length)), 51 | ) 52 | if ret == 0 { 53 | return VS_FIXEDFILEINFO{}, errors.New("VerQueryValueRoot: verQueryValue failed") 54 | } 55 | start := int(offset) - int(uintptr(blockStart)) 56 | end := start + int(length) 57 | if start < 0 || start >= len(block) || end < start || end > len(block) { 58 | return VS_FIXEDFILEINFO{}, errors.New("VerQueryValueRoot: find failed") 59 | } 60 | data := block[start:end] 61 | info := *((*VS_FIXEDFILEINFO)(unsafe.Pointer(&data[0]))) 62 | return info, nil 63 | } 64 | 65 | func GetFileVersion(path string) (WinVersion, error) { 66 | var result WinVersion 67 | size := GetFileVersionInfoSize(path) 68 | if size <= 0 { 69 | return result, errors.New("GetFileVersionInfoSize failed") 70 | } 71 | 72 | info := make([]byte, size) 73 | ok := GetFileVersionInfo(path, info) 74 | if !ok { 75 | return result, errors.New("GetFileVersionInfo failed") 76 | } 77 | 78 | fixed, err := VerQueryValueRoot(info) 79 | if err != nil { 80 | return result, err 81 | } 82 | version := fixed.FileVersion() 83 | 84 | result.Major = uint32(version & 0xFFFF000000000000 >> 48) 85 | result.Minor = uint32(version & 0x0000FFFF00000000 >> 32) 86 | result.Patch = uint32(version & 0x00000000FFFF0000 >> 16) 87 | result.Build = uint32(version & 0x000000000000FFFF) 88 | 89 | return result, nil 90 | } 91 | -------------------------------------------------------------------------------- /process_windows.go: -------------------------------------------------------------------------------- 1 | package injgo 2 | 3 | import ( 4 | "errors" 5 | "syscall" 6 | "unsafe" 7 | 8 | "go.zoe.im/injgo/pkg/w32" 9 | ) 10 | 11 | // Process ... 12 | type Process struct { 13 | ProcessID int 14 | Name string 15 | ExePath string 16 | } 17 | 18 | var ( 19 | // ErrProcessNotFound ... 20 | ErrProcessNotFound = errors.New("process not found") 21 | // ErrCreateSnapshot ... 22 | ErrCreateSnapshot = errors.New("create snapshot error") 23 | ) 24 | 25 | // FindProcessByName get process information by name 26 | func FindProcessByName(name string) (*Process, error) { 27 | handle, _ := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) 28 | if handle == 0 { 29 | return nil, ErrCreateSnapshot 30 | } 31 | defer syscall.CloseHandle(handle) 32 | 33 | var entry = syscall.ProcessEntry32{} 34 | entry.Size = uint32(unsafe.Sizeof(entry)) 35 | var process Process 36 | 37 | for true { 38 | if nil != syscall.Process32Next(handle, &entry) { 39 | break 40 | } 41 | 42 | _exeFile := w32.UTF16PtrToString(&entry.ExeFile[0]) 43 | if name == _exeFile { 44 | process.Name = _exeFile 45 | process.ProcessID = int(entry.ProcessID) 46 | // TODO: 找到路径 47 | process.ExePath = _exeFile 48 | return &process, nil 49 | } 50 | 51 | } 52 | return nil, ErrProcessNotFound 53 | } 54 | 55 | // CreateProcess create a new process 56 | // 57 | // exePath execute path 58 | func CreateProcess(exePath string) (*Process, error) { 59 | // TODO: 60 | var sI syscall.StartupInfo 61 | var pI syscall.ProcessInformation 62 | 63 | argv := syscall.StringToUTF16Ptr(exePath) 64 | 65 | err := syscall.CreateProcess( 66 | nil, argv, nil, 67 | nil, true, 0, 68 | nil, nil, &sI, &pI, 69 | ) 70 | if err != nil { 71 | return nil, err 72 | } 73 | return &Process{ 74 | ProcessID: int(pI.ProcessId), 75 | // TODO: 76 | }, nil 77 | } 78 | --------------------------------------------------------------------------------