forked from jiusanzhou/injgo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinject_windows.go
119 lines (97 loc) · 2.94 KB
/
inject_windows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package injgo
import (
"errors"
"unsafe"
"go.zoe.im/injgo/pkg/w32"
)
var (
ErrAlreadyInjected = errors.New("dll already injected")
ErrModuleNotExits = errors.New("can't found module")
ErrModuleSnapshot = errors.New("create module snapshot failed")
)
// WARNING: only 386 arch works well.
//
// Inject is the function inject dynamic library to a process
//
// In windows, name is a file with dll extion.If the file
// name exits, we will return error.
// The workflow of injection in windows is:
// 0. load kernel32.dll in current process.
// 1. open target process T.
// 2. malloc memory in T to store the name of the library.
// 3. get address of function LoadLibraryA from kernel32.dll
// in T.
// 4. call CreateRemoteThread method in kernel32.dll to execute
// LoadLibraryA in T.
func Inject(pid int, dllname string, replace bool) error {
// check is already injected
if !replace && IsInjected(pid, dllname) {
return ErrAlreadyInjected
}
// open process
hdlr, err := w32.OpenProcess(w32.PROCESS_ALL_ACCESS, false, uint32(pid))
if err != nil {
return err
}
defer w32.CloseHandle(hdlr)
// malloc space to write dll name
dlllen := len(dllname)
dllnameaddr, err := w32.VirtualAllocEx(hdlr, 0, dlllen, w32.MEM_COMMIT, w32.PAGE_EXECUTE_READWRITE)
if err != nil {
return err
}
// write dll name
err = w32.WriteProcessMemory(hdlr, uint32(dllnameaddr), []byte(dllname), uint(dlllen))
if err != nil {
return err
}
// test
tecase, _ := w32.ReadProcessMemory(hdlr, uint32(dllnameaddr), uint(dlllen))
if string(tecase) != dllname {
return errors.New("write dll name error")
}
// get LoadLibraryA address in target process
// TODO: can we get the address at from this process?
lddladdr, err := w32.GetProcAddress(w32.GetModuleHandleA("kernel32.dll"), "LoadLibraryA")
if err != nil {
return err
}
// call remote process
dllthread, _, err := w32.CreateRemoteThread(hdlr, nil, 0, uint32(lddladdr), dllnameaddr, 0)
if err != nil {
return err
}
w32.CloseHandle(dllthread)
return nil
}
// InjectByProcessName inject dll by process name
func InjectByProcessName(name string, dll string, replace bool) error {
p, err := FindProcessByName(name)
if err != nil {
return err
}
return Inject(p.ProcessID, dll, replace)
}
// FindModuleEntry find module entry of with dll name
func FindModuleEntry(pid int, dllname string) (*w32.MODULEENTRY32, error) {
hdlr := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPMODULE, uint32(pid))
defer w32.CloseHandle(hdlr)
if hdlr == 0 {
return nil, ErrModuleSnapshot
}
var entry w32.MODULEENTRY32
entry.Size = uint32(unsafe.Sizeof(entry))
next := w32.Module32First(hdlr, &entry)
for next {
if w32.UTF16PtrToString(&entry.SzExePath[0]) == dllname {
return &entry, nil
}
next = w32.Module32Next(hdlr, &entry)
}
return nil, ErrModuleNotExits
}
// IsInjected check is dll is already injected
func IsInjected(pid int, dllname string) bool {
_, err := FindModuleEntry(pid, dllname)
return err == nil
}