├── .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://travis-ci.org/jiusanzhou/injgo) [](https://godoc.org/github.com/jiusanzhou/injgo) [](https://goreportcard.com/report/jiusanzhou/injgo) [](https://twitter.com/jiusanzhou "@Zoe on Twitter") [](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 |
--------------------------------------------------------------------------------