-
Notifications
You must be signed in to change notification settings - Fork 105
Expand file tree
/
Copy pathprocess_inject.lua
More file actions
189 lines (175 loc) · 5.31 KB
/
process_inject.lua
File metadata and controls
189 lines (175 loc) · 5.31 KB
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
local fs = require 'bee.filesystem'
local sp = require 'bee.subprocess'
local arch = require "bee.platform".Arch
local platform_os = require 'frontend.platform_os' ()
local _M = {}
local macos = "macOS"
local windows = "Windows"
local entry_launch = "launch"
local function macos_check_rosetta_process(process)
local rosetta_runtime <const> = "/usr/libexec/rosetta/runtime"
if not fs.exists(rosetta_runtime) then
return false
end
local p = sp.spawn {
"/usr/bin/fuser",
rosetta_runtime,
stdout = true,
stderr = true, -- for skip fuser output
}
if not p then
return false
end
local l = p.stdout:read "a"
return l:find(tostring(process)) ~= nil
end
function _M.check_injectdll(injectdll)
injectdll = injectdll or (WORKDIR / "bin" / "launcher.so"):string()
if not fs.exists(injectdll) then
return nil, "Not found launcher.so."
end
return injectdll
end
function _M.gdb_inject(pid, entry, injectdll, gdb_path)
local injectdll, err = _M.check_injectdll(injectdll)
if not injectdll then
return false, err
end
gdb_path = gdb_path or "gdb"
local pre_launcher = entry == entry_launch and
{
"-ex",
"break main",
"-ex",
"c",
} or {}
local launcher = {
"-ex",
-- 5 = RTDL_LAZY|RTDL_LOCAL
('print (void*)dlopen("%s", 5)'):format(injectdll),
"-ex",
('call ((void(*)())&%s)()'):format(entry),
"-ex",
"quit"
}
local p, err = sp.spawn {
gdb_path,
"-p", tostring(pid),
"--batch",
pre_launcher,
launcher,
stdout = true,
stderr = true,
}
if not p then
return false, "Spawn lldb failed:"..err
end
if p:wait() ~= 0 then
return false, "stdout:"..p.stdout:read "a".."\nstderr:"..p.stderr:read "a"
end
return true
end
function _M.lldb_inject(pid, entry, injectdll, lldb_path)
local injectdll, err = _M.check_injectdll(injectdll)
if not injectdll then
return false, err
end
lldb_path = lldb_path or "lldb"
local pre_launcher = entry == entry_launch and
{
"-o",
"breakpoint set -n main",
"-o",
"c",
} or {}
local launcher = {
"-o",
-- 5 = RTDL_LAZY|RTDL_LOCAL
('expression (void*)dlopen("%s", 5)'):format(injectdll),
"-o",
('expression ((void(*)())&%s)()'):format(entry),
"-o",
"quit"
}
local p, err = sp.spawn {
lldb_path,
"-p", tostring(pid),
"--batch",
pre_launcher,
launcher,
stdout = true,
stderr = true,
}
if not p then
return false, "Spawn lldb failed:"..err
end
if p:wait() ~= 0 then
return false, "stdout:"..p.stdout:read "a".."\nstderr:"..p.stderr:read "a"
end
return true
end
function _M.macos_inject(process, entry, injectdll)
local injectdll, err = _M.check_injectdll(injectdll)
if not injectdll then
return false, err
end
local helper = (WORKDIR / "bin" / "process_inject_helper"):string()
local p, err = sp.spawn {
"/usr/bin/osascript",
"-e",
([[do shell script "%s %d %s %s" with administrator privileges with prompt "lua-debug"]]):format(helper, process, injectdll, entry),
stderr = true,
}
if not p then
return false, "Spawn osascript failed:"..err
end
if p:wait() ~= 0 then
return false, p.stderr:read "a"
end
return true
end
function _M.windows_inject(process, entry)
local inject = require 'inject'
if not inject.injectdll(process
, (WORKDIR / "bin" / "launcher.x86.dll"):string()
, (WORKDIR / "bin" / "launcher.x64.dll"):string()
, entry
) then
return false, "injectdll failed."
end
return true
end
function _M.inject(process, entry, args)
if platform_os ~= windows and platform_os ~= macos then
return false, "unsupported inject"
end
if platform_os ~= windows and type(process) == "userdata" then
process = process:get_id()
end
if args.inject == "gdb" then
return _M.gdb_inject(process, entry, nil, args.inject_executable)
elseif args.inject == 'lldb' then
return _M.lldb_inject(process, entry, nil, args.inject_executable)
elseif args.inject == 'hook' then
if platform_os == macos then
local is_launch = entry == entry_launch
local is_rosetta = arch == "arm64" and macos_check_rosetta_process(process)
local force_lldb = is_launch or is_rosetta
if force_lldb then
return false, "force use lldb when " .. (is_launch and entry or "rosetta") .. ", please try lldb inject."
end
local ok, err = _M.macos_inject(process, entry)
if not ok then
return false, err .. "\nretry or try lldb inject."
end
return true
elseif platform_os == windows then
return _M.windows_inject(process, entry)
else
return false, ("Inject (use %s) is not supported in %s."):format(args.inject, platform_os)
end
else
return false, ("Inject (use %s) is not supported."):format(args.inject)
end
end
return _M