├── .DS_Store
├── .gitignore
├── README.md
├── fix_failed_to_reach_single_threaded_state.txt
├── main_ubuntu_android.py
├── main_macos_android.py
└── main_macos_ios.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackcatml/ajeossida/HEAD/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | assets
3 | ajeossida
4 | venv
5 | .DS_Store
6 | ios-assets
7 | frida-java-bridge
8 | frida-orig
9 | /test/
10 |
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ajeossida (아저씨다)
2 | Frida with patches that definitively fix basic detection points on Android and iOS.
3 | Unfortunately, I discovered that the patches in several custom Frida builds for bypassing detections are incomplete and still detectable.
4 | For example, `frida_agent_main` in memory and the `gum-js-loop` thread name.
5 | Therefore, I created a Python build script to address these issues.
6 |
7 | Since this is a manual patch that doesn't automatically follow the Frida upstream,
8 | I will occasionally build it, verify that the patch works properly, and then release it.
9 |
10 | # Patches
11 | - Android
12 | - [x] No `frida_agent_main` in memory
13 | - [x] No `gum-js-loop, gmain, gdbus, frida-gadget` thread name in `/proc//task//status`
14 | - [x] No `libfrida-agent-raw.so` in linker's so list
15 | - [x] No libc hooking
16 |
17 | - iOS
18 | - [x] No `frida_agent_main` in memory
19 | - [x] No `gum-js-loop, gmain, gdbus, pool-frida, pool-spawner` thread name
20 | - [x] No `/usr/lib/frida/`
21 | - [x] No `exit, abort, task_threads` hooking
22 |
23 | # Run
24 | - MacOS
25 | Output: server, gadget (Android, iOS)
26 | `python3 main_macos.py`
27 |
28 | - Ubuntu 22.04
29 | Output: server, gagdet (Android)
30 | `python3 main_ubuntu.py`
31 |
32 | # MagiskAjeossida
33 | * A magisk module that automatically runs ajeossida-server on boot.
34 | * To run it in remote mode, use the following command. It will listen on `0.0.0.0:45678`.
35 | `adb shell "su -c sed -i 's/REMOTE=0/REMOTE=1/' /data/adb/modules/magisk_ajeossida/service.sh"`
36 | * You can attach Frida to a pairipcore protected app using this module.
37 | However, the app will crash after a few seconds. Bypassing the crash is up to you. (Spawning the app also causes it to crash)
38 |
39 | # Contact
40 | - Channel: https://t.me/hackcatml1
41 | - Chat: https://t.me/hackcatmlchat
42 |
43 | # References
44 | - [strongR-frida-android](https://github.com/hzzheyang/strongR-frida-android)
45 | - [Florida](https://github.com/Ylarod/Florida)
46 | - [magisk-frida](https://github.com/ViRb3/magisk-frida)
47 |
48 |
--------------------------------------------------------------------------------
/fix_failed_to_reach_single_threaded_state.txt:
--------------------------------------------------------------------------------
1 | // https://github.com/frida/frida-core/blob/b1fab8470aaa2edb4606d740466cd1446ed88918/lib/payload/cloak.vala
2 | namespace Frida {
3 | public class ThreadIgnoreScope {
4 | public enum Kind {
5 | APPLICATION_THREAD,
6 | FRIDA_THREAD
7 | }
8 |
9 | private Kind kind;
10 |
11 | private Gum.Interceptor interceptor;
12 |
13 | private Gum.ThreadId thread_id;
14 |
15 | private uint num_ranges;
16 | private Gum.MemoryRange ranges[2];
17 |
18 | public ThreadIgnoreScope (Kind kind) {
19 | this.kind = kind;
20 |
21 | interceptor = Gum.Interceptor.obtain ();
22 | interceptor.ignore_current_thread ();
23 |
24 | if (kind == FRIDA_THREAD) {
25 | thread_id = Gum.Process.get_current_thread_id ();
26 | Gum.Cloak.add_thread (thread_id);
27 |
28 | num_ranges = Gum.Thread.try_get_ranges (ranges);
29 | for (var i = 0; i != num_ranges; i++)
30 | Gum.Cloak.add_range (ranges[i]);
31 | }
32 | }
33 |
34 | ~ThreadIgnoreScope () {
35 | if (kind == FRIDA_THREAD) {
36 | for (var i = 0; i != num_ranges; i++)
37 | Gum.Cloak.remove_range (ranges[i]);
38 |
39 | Gum.Cloak.remove_thread (thread_id);
40 | }
41 |
42 | interceptor.unignore_current_thread ();
43 | }
44 | }
45 |
46 | #if LINUX
47 | public class ThreadCountCloaker : Object {
48 | private ReadListener listener;
49 |
50 | construct {
51 | listener = new ReadListener ();
52 | Gum.Interceptor.obtain ().attach (
53 | (void*) Gum.Module.find_export_by_name (Gum.Process.query_libc_name (), "read"),
54 | listener);
55 | }
56 |
57 | ~ThreadCountCloaker () {
58 | // Gum.Interceptor.obtain ().detach (listener);
59 | }
60 |
61 | private class ReadListener : Object, Gum.InvocationListener {
62 | private static string expected_magic = "%u (".printf (Posix.getpid ());
63 |
64 | public void on_enter (Gum.InvocationContext context) {
65 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
66 | invocation.fd = (int) context.get_nth_argument (0);
67 | invocation.buf = context.get_nth_argument (1);
68 | invocation.count = (size_t) context.get_nth_argument (2);
69 | }
70 |
71 | public void on_leave (Gum.InvocationContext context) {
72 | var n = (ssize_t) context.get_return_value ();
73 | if (n > 0) {
74 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
75 | if (file_content_might_be_from_proc_self_stat (invocation.buf, n)) {
76 | try {
77 | if (file_descriptor_is_proc_self_stat (invocation.fd)) {
78 | unowned string raw_str = (string) invocation.buf;
79 | string str = raw_str.substring (0, n);
80 |
81 | MatchInfo info;
82 | if (/^(\d+ \(.+\)(?: [^ ]+){17}) \d+ (.+)/s.match (str, 0, out info)) {
83 | string fields_before = info.fetch (1);
84 | string fields_after = info.fetch (2);
85 |
86 | // We cannot simply use the value we got from the kernel and subtract the number of cloaked threads,
87 | // as there's a chance the total may have changed in the last moment.
88 | uint num_uncloaked_threads = query_num_uncloaked_threads ();
89 |
90 | string adjusted_str = "%s %u %s".printf (fields_before, num_uncloaked_threads, fields_after);
91 |
92 | var adjusted_length = adjusted_str.length;
93 | if (adjusted_length <= invocation.count) {
94 | Memory.copy (invocation.buf, adjusted_str, adjusted_length);
95 | context.replace_return_value ((void *) adjusted_length);
96 | }
97 | }
98 | }
99 | } catch (FileError e) {
100 | }
101 | }
102 | }
103 | }
104 |
105 | private static bool file_content_might_be_from_proc_self_stat (void * content, ssize_t size) {
106 | if (size < expected_magic.length)
107 | return false;
108 | if (Memory.cmp (content, expected_magic, expected_magic.length) != 0)
109 | return false;
110 | unowned string raw_str = (string) content;
111 | return raw_str[size - 1] == '\n';
112 | }
113 |
114 | private static bool file_descriptor_is_proc_self_stat (int fd) throws FileError {
115 | string path = FileUtils.read_link ("/proc/self/fd/%d".printf (fd));
116 | uint pid = Posix.getpid ();
117 | return (path == "/proc/%u/stat".printf (pid)) ||
118 | (path == "/proc/%u/task/%u/stat".printf (pid, pid));
119 | }
120 |
121 | private static uint query_num_uncloaked_threads () throws FileError {
122 | uint n = 0;
123 | var dir = Dir.open ("/proc/self/task");
124 | string? name;
125 | while ((name = dir.read_name ()) != null) {
126 | var tid = uint.parse (name);
127 | if (!Gum.Cloak.has_thread (tid))
128 | n++;
129 | }
130 | return n;
131 | }
132 |
133 | private struct Invocation {
134 | public int fd;
135 | public void * buf;
136 | public size_t count;
137 | }
138 | }
139 | }
140 |
141 | public class ThreadListCloaker : Object, DirListFilter {
142 | private string our_dir_by_pid;
143 | private DirListCloaker cloaker;
144 |
145 | construct {
146 | our_dir_by_pid = "/proc/%u/task".printf (Posix.getpid ());
147 | cloaker = new DirListCloaker (this);
148 | }
149 |
150 | private bool matches_directory (string path) {
151 | return path == "/proc/self/task" || path == our_dir_by_pid;
152 | }
153 |
154 | private bool matches_file (string name) {
155 | var tid = (Gum.ThreadId) uint64.parse (name);
156 | return Gum.Cloak.has_thread (tid);
157 | }
158 | }
159 |
160 | public class FDListCloaker : Object, DirListFilter {
161 | private string our_dir_by_pid;
162 | private DirListCloaker cloaker;
163 |
164 | construct {
165 | our_dir_by_pid = "/proc/%u/fd".printf (Posix.getpid ());
166 | cloaker = new DirListCloaker (this);
167 | }
168 |
169 | private bool matches_directory (string path) {
170 | return path == "/proc/self/fd" || path == our_dir_by_pid;
171 | }
172 |
173 | private bool matches_file (string name) {
174 | var fd = int.parse (name);
175 | return Gum.Cloak.has_file_descriptor (fd);
176 | }
177 | }
178 |
179 | private class DirListCloaker : Object {
180 | public weak DirListFilter filter {
181 | get;
182 | construct;
183 | }
184 |
185 | private Gee.HashSet listeners = new Gee.HashSet ();
186 | private Gee.HashSet tracked_handles = new Gee.HashSet ();
187 |
188 | public DirListCloaker (DirListFilter filter) {
189 | Object (filter: filter);
190 | }
191 |
192 | construct {
193 | var interceptor = Gum.Interceptor.obtain ();
194 |
195 | unowned string libc = Gum.Process.query_libc_name ();
196 |
197 | var open_listener = new OpenDirListener (this);
198 | listeners.add (open_listener);
199 | interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "opendir"), open_listener);
200 |
201 | var close_listener = new CloseDirListener (this);
202 | listeners.add (close_listener);
203 | interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "closedir"), close_listener);
204 |
205 | var readdir_impl = Gum.Module.find_export_by_name (libc, "readdir");
206 | var readdir_listener = new ReadDirListener (this, LEGACY);
207 | listeners.add (readdir_listener);
208 | interceptor.attach ((void *) readdir_impl, readdir_listener);
209 |
210 | var readdir64_impl = Gum.Module.find_export_by_name (libc, "readdir64");
211 | if (readdir64_impl != 0 && readdir64_impl != readdir_impl) {
212 | var listener = new ReadDirListener (this, MODERN);
213 | listeners.add (listener);
214 | interceptor.attach ((void *) readdir64_impl, listener);
215 | }
216 |
217 | var readdir_r_impl = Gum.Module.find_export_by_name (libc, "readdir_r");
218 | var readdir_r_listener = new ReadDirRListener (this, LEGACY);
219 | listeners.add (readdir_r_listener);
220 | interceptor.attach ((void *) readdir_r_impl, readdir_r_listener);
221 |
222 | var readdir64_r_impl = Gum.Module.find_export_by_name (libc, "readdir64_r");
223 | if (readdir64_r_impl != 0 && readdir64_r_impl != readdir_r_impl) {
224 | var listener = new ReadDirRListener (this, MODERN);
225 | listeners.add (listener);
226 | interceptor.attach ((void *) readdir64_r_impl, listener);
227 | }
228 | }
229 |
230 | ~DirListCloaker () {
231 | var interceptor = Gum.Interceptor.obtain ();
232 |
233 | foreach (var listener in listeners)
234 | interceptor.detach (listener);
235 | }
236 |
237 | public void start_tracking (Posix.Dir handle) {
238 | lock (tracked_handles)
239 | tracked_handles.add (handle);
240 | }
241 |
242 | public void stop_tracking (Posix.Dir handle) {
243 | lock (tracked_handles)
244 | tracked_handles.remove (handle);
245 | }
246 |
247 | public bool is_tracking (Posix.Dir handle) {
248 | lock (tracked_handles)
249 | return tracked_handles.contains (handle);
250 | }
251 |
252 | private class OpenDirListener : Object, Gum.InvocationListener {
253 | public weak DirListCloaker parent {
254 | get;
255 | construct;
256 | }
257 |
258 | public OpenDirListener (DirListCloaker parent) {
259 | Object (parent: parent);
260 | }
261 |
262 | public void on_enter (Gum.InvocationContext context) {
263 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
264 |
265 | invocation.path = (string *) context.get_nth_argument (0);
266 | }
267 |
268 | public void on_leave (Gum.InvocationContext context) {
269 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
270 | if (!parent.filter.matches_directory (invocation.path))
271 | return;
272 |
273 | unowned Posix.Dir? handle = (Posix.Dir?) context.get_return_value ();
274 | if (handle != null)
275 | parent.start_tracking (handle);
276 | }
277 |
278 | private struct Invocation {
279 | public string * path;
280 | }
281 | }
282 |
283 | private class CloseDirListener : Object, Gum.InvocationListener {
284 | public weak DirListCloaker parent {
285 | get;
286 | construct;
287 | }
288 |
289 | public CloseDirListener (DirListCloaker parent) {
290 | Object (parent: parent);
291 | }
292 |
293 | public void on_enter (Gum.InvocationContext context) {
294 | unowned Posix.Dir? handle = (Posix.Dir?) context.get_nth_argument (0);
295 | if (handle != null)
296 | parent.stop_tracking (handle);
297 | }
298 | }
299 |
300 | private class ReadDirListener : Object, Gum.InvocationListener {
301 | public weak DirListCloaker parent {
302 | get;
303 | construct;
304 | }
305 |
306 | public DirEntKind kind {
307 | get;
308 | construct;
309 | }
310 |
311 | public ReadDirListener (DirListCloaker parent, DirEntKind kind) {
312 | Object (parent: parent, kind: kind);
313 | }
314 |
315 | public void on_enter (Gum.InvocationContext context) {
316 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
317 | invocation.handle = (Posix.Dir?) context.get_nth_argument (0);
318 | }
319 |
320 | public void on_leave (Gum.InvocationContext context) {
321 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
322 | if (!parent.is_tracking (invocation.handle))
323 | return;
324 |
325 | var entry = context.get_return_value ();
326 | do {
327 | if (entry == null)
328 | return;
329 |
330 | var name = parse_dirent_name (entry, kind);
331 |
332 | if (name == "." || name == "..")
333 | return;
334 |
335 | if (!parent.filter.matches_file (name))
336 | return;
337 |
338 | var impl = (ReadDirFunc) context.function;
339 | entry = impl (invocation.handle);
340 |
341 | context.replace_return_value (entry);
342 | } while (true);
343 | }
344 |
345 | private struct Invocation {
346 | public unowned Posix.Dir? handle;
347 | }
348 |
349 | [CCode (has_target = false)]
350 | private delegate void * ReadDirFunc (Posix.Dir dir);
351 | }
352 |
353 | private class ReadDirRListener : Object, Gum.InvocationListener {
354 | public weak DirListCloaker parent {
355 | get;
356 | construct;
357 | }
358 |
359 | public DirEntKind kind {
360 | get;
361 | construct;
362 | }
363 |
364 | public ReadDirRListener (DirListCloaker parent, DirEntKind kind) {
365 | Object (parent: parent, kind: kind);
366 | }
367 |
368 | public void on_enter (Gum.InvocationContext context) {
369 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
370 | invocation.handle = (Posix.Dir?) context.get_nth_argument (0);
371 | invocation.entry = context.get_nth_argument (1);
372 | invocation.result = context.get_nth_argument (2);
373 | }
374 |
375 | public void on_leave (Gum.InvocationContext context) {
376 | Invocation * invocation = context.get_listener_invocation_data (sizeof (Invocation));
377 | if (!parent.is_tracking (invocation.handle))
378 | return;
379 |
380 | var result = (int) context.get_return_value ();
381 | do {
382 | if (result != 0)
383 | return;
384 |
385 | if (*invocation.result == null)
386 | return;
387 |
388 | var name = parse_dirent_name (*invocation.result, kind);
389 |
390 | if (name == "." || name == "..")
391 | return;
392 |
393 | if (!parent.filter.matches_file (name))
394 | return;
395 |
396 | var impl = (ReadDirRFunc) context.function;
397 | result = impl (invocation.handle, invocation.entry, invocation.result);
398 |
399 | context.replace_return_value ((void *) result);
400 | } while (true);
401 | }
402 |
403 | private struct Invocation {
404 | public unowned Posix.Dir? handle;
405 | public void * entry;
406 | public void ** result;
407 | }
408 |
409 | [CCode (has_target = false)]
410 | private delegate int ReadDirRFunc (Posix.Dir dir, void * entry, void ** result);
411 | }
412 |
413 | private static unowned string parse_dirent_name (void * entry, DirEntKind kind) {
414 | unowned string? name = null;
415 |
416 | if (kind == LEGACY) {
417 | unowned Posix.DirEnt ent = (Posix.DirEnt) entry;
418 | name = (string) ent.d_name;
419 | } else if (kind == MODERN) {
420 | unowned DirEnt64 ent = (DirEnt64) entry;
421 | name = (string) ent.d_name;
422 | }
423 |
424 | return name;
425 | }
426 |
427 | private enum DirEntKind {
428 | LEGACY,
429 | MODERN
430 | }
431 | }
432 |
433 | [Compact]
434 | public class DirEnt64 {
435 | public uint64 d_ino;
436 | public int64 d_off;
437 | public uint16 d_reclen;
438 | public uint8 d_type;
439 | public char d_name[256];
440 | }
441 |
442 | public interface DirListFilter : Object {
443 | public abstract bool matches_directory (string path);
444 | public abstract bool matches_file (string name);
445 | }
446 | #else
447 | public class ThreadCountCloaker : Object {
448 | }
449 |
450 | public class ThreadListCloaker : Object {
451 | }
452 |
453 | public class FDListCloaker : Object {
454 | }
455 | #endif
456 | }
--------------------------------------------------------------------------------
/main_ubuntu_android.py:
--------------------------------------------------------------------------------
1 | import gzip
2 | import os
3 | import shutil
4 | import subprocess
5 | import sys
6 | import requests
7 |
8 | CUSTOM_NAME = "ajeossida"
9 |
10 | # Temporarily fixing the issue 'Failed to reach single-threaded state', Process.enumerateThreads() crash
11 | TEMP = 0
12 |
13 |
14 | def run_command(command, cwd=None):
15 | try:
16 | result = subprocess.run(command, shell=True, cwd=cwd, check=True, text=True)
17 | return result.returncode
18 | except subprocess.CalledProcessError as e:
19 | print(f"Error while running command: {command}\nError: {e}")
20 | sys.exit(1)
21 |
22 |
23 | def git_clone_repo():
24 | repo_url = "https://github.com/frida/frida.git"
25 | destination_dir = os.path.join(os.getcwd(), CUSTOM_NAME)
26 |
27 | print(f"\n[*] Cloning repository {repo_url} to {destination_dir}...")
28 | run_command(f"git clone --recurse-submodules {repo_url} {destination_dir}")
29 |
30 |
31 | def download_ndk():
32 | url = "https://dl.google.com/android/repository/android-ndk-r25c-linux.zip"
33 | file_name = "android-ndk-r25c-linux.zip"
34 | unzip_dir = "android-ndk-r25c"
35 |
36 | print(f"\n[*] Downloading {file_name}...")
37 | response = requests.get(url, stream=True)
38 | with open(file_name, 'wb') as file:
39 | for chunk in response.iter_content(chunk_size=128):
40 | file.write(chunk)
41 | print(f"\n[*] Downloaded {file_name}")
42 |
43 | run_command(f"unzip {file_name} >/dev/null")
44 |
45 | os.remove(file_name)
46 |
47 | return unzip_dir
48 |
49 |
50 | def configure_build(ndk_path, arch):
51 | build_dir = os.path.join(os.getcwd(), CUSTOM_NAME, arch)
52 | os.makedirs(build_dir, exist_ok=True)
53 |
54 | os.environ['ANDROID_NDK_ROOT'] = ndk_path
55 |
56 | result = run_command(f"{os.path.join('..', 'configure')} --host={arch}", cwd=build_dir)
57 |
58 | if result == 0:
59 | return build_dir
60 | else:
61 | print("\n[!] Failed to configure")
62 | sys.exit(1)
63 |
64 |
65 | def build(build_dir):
66 | run_command("make", cwd=build_dir)
67 |
68 |
69 | def replace_strings_in_files(directory, search_string, replace_string):
70 | if os.path.isfile(directory):
71 | file_path = directory
72 | with open(file_path, 'r+', encoding='utf-8') as file:
73 | content = file.read()
74 | if search_string in content:
75 | print(f"Patch {file.name}")
76 | patched_content = content.replace(search_string, replace_string)
77 | file.seek(0)
78 | file.write(patched_content)
79 | file.truncate()
80 | else:
81 | for root, dirs, files in os.walk(directory):
82 | for file_name in files:
83 | file_path = os.path.join(root, file_name)
84 | try:
85 | with open(file_path, 'r+', encoding='utf-8') as file:
86 | content = file.read()
87 | if search_string in content:
88 | print(f"Patch {file.name}")
89 | patched_content = content.replace(search_string, replace_string)
90 | file.seek(0)
91 | file.write(patched_content)
92 | file.truncate()
93 | except Exception as e:
94 | pass
95 |
96 |
97 | def compress_file(file_path):
98 | try:
99 | # Create a .gz file from the original file
100 | with open(file_path, 'rb') as f_in:
101 | with gzip.open(file_path + '.gz', 'wb') as f_out:
102 | shutil.copyfileobj(f_in, f_out)
103 | print(f"[*] Compressed {file_path} to {file_path}.gz")
104 | except Exception as e:
105 | print(f"[!] Error compressing {file_path}: {e}")
106 |
107 |
108 | def capitalize_first_lower_char(word):
109 | for index, char in enumerate(word):
110 | if char.islower():
111 | # Replace the first lowercase character with its uppercase equivalent
112 | return word[:index] + char.upper() + word[index + 1:]
113 | return word
114 |
115 |
116 | def fix_failed_to_reach_single_threaded_state(custom_dir):
117 | old_cloak_vala_path = os.path.join(custom_dir, "subprojects/frida-core/lib/payload/cloak.vala")
118 | new_cloak_vala_path = os.path.join(os.getcwd(), "fix_failed_to_reach_single_threaded_state.txt")
119 | os.remove(old_cloak_vala_path)
120 | shutil.copy(new_cloak_vala_path, old_cloak_vala_path)
121 |
122 |
123 | def fix_process_enumerate_threads_crash(custom_dir):
124 | # On the Pixel 4a, if there is a thread named perfetto_hprof_, Process.enumerateThreads() crashes with SEGV_ACCERR
125 | gumprocess_linux_path = os.path.join(custom_dir, "subprojects/frida-gum/gum/backend-linux/gumprocess-linux.c")
126 | replace_strings_in_files(gumprocess_linux_path,
127 | ' details.name = thread_name;',
128 | ' details.name = thread_name;\n'
129 | ' if (strcmp(details.name, "perfetto_hprof_") == 0)\n'
130 | ' continue;')
131 |
132 |
133 | def main():
134 | custom_dir = os.path.join(os.getcwd(), CUSTOM_NAME)
135 | if os.path.exists(custom_dir):
136 | print(f"\n[*] Cleaning {custom_dir}...")
137 | shutil.rmtree(custom_dir)
138 |
139 | assets_dir = os.path.join(os.getcwd(), "assets")
140 | if os.path.exists(assets_dir):
141 | print(f"\n[*] Cleaning {assets_dir}...")
142 | shutil.rmtree(assets_dir)
143 | os.mkdir(assets_dir)
144 |
145 | git_clone_repo()
146 |
147 | ndk_path = os.path.join(os.getcwd(), download_ndk())
148 |
149 | architectures = ["android-arm64", "android-arm", "android-x86_64", "android-x86"]
150 | if TEMP == 1:
151 | architectures = ["android-arm64"]
152 | build_dirs = [configure_build(ndk_path, arch) for arch in architectures]
153 |
154 | # libfrida-agent-raw.so patch
155 | print(f"\n[*] Patch 'libfrida-agent-raw.so' with 'lib{CUSTOM_NAME}-agent-raw.so' recursively...")
156 | patch_string = "libfrida-agent-raw.so"
157 | replace_strings_in_files(custom_dir,
158 | patch_string,
159 | patch_string.replace("frida", CUSTOM_NAME))
160 |
161 | # re.frida.server patch
162 | print(f"\n[*] Patch 're.frida.server' with 're.{CUSTOM_NAME}.server' recursively...")
163 | patch_string = "re.frida.server"
164 | replace_strings_in_files(custom_dir,
165 | patch_string,
166 | patch_string.replace("frida", CUSTOM_NAME))
167 |
168 | # frida-helper patch
169 | print(f"\n[*] Patch 'frida-helper' with '{CUSTOM_NAME}-helper' recursively...")
170 | patch_strings = ["frida-helper-32", "frida-helper-64", "get_frida_helper_", "\"/frida-\""]
171 | for patch_string in patch_strings:
172 | replace_strings_in_files(custom_dir,
173 | patch_string,
174 | patch_string.replace("frida", CUSTOM_NAME))
175 |
176 | # frida-agent patch
177 | print(f"\n[*] Patch 'frida-agent' with '{CUSTOM_NAME}-agent' recursively...")
178 | patch_strings = ["frida-agent-", "\"agent\" / \"frida-agent.", "\'frida-agent\'", "\"frida-agent\"",
179 | "get_frida_agent_", "\'FridaAgent\'"]
180 | for patch_string in patch_strings:
181 | replace_strings_in_files(custom_dir,
182 | patch_string,
183 | patch_string.replace("Frida", capitalize_first_lower_char(
184 | CUSTOM_NAME)) if "Frida" in patch_string else
185 | patch_string.replace("frida", CUSTOM_NAME))
186 |
187 | # Patch the original file back, which has incorrectly patched strings.
188 | wrong_patch_strings = [f'{CUSTOM_NAME}-agent-x86.symbols', f'{CUSTOM_NAME}-agent-android.version']
189 | for wrong_patch_string in wrong_patch_strings:
190 | replace_strings_in_files(custom_dir,
191 | wrong_patch_string,
192 | wrong_patch_string.replace(CUSTOM_NAME, 'frida'))
193 |
194 | # memfd_create patch, memfd:ajeossida-agent-64.so --> memfd:jit-cache
195 | print(f"\n[*] Patch MemoryFileDescriptor.memfd_create")
196 | frida_helper_backend_path = os.path.join(custom_dir,
197 | "subprojects/frida-core/src/linux/frida-helper-backend.vala")
198 | replace_strings_in_files(frida_helper_backend_path,
199 | 'return Linux.syscall (SysCall.memfd_create, name, flags);',
200 | 'return Linux.syscall (SysCall.memfd_create, \"jit-cache\", flags);')
201 |
202 | # frida-server patch
203 | print(f"\n[*] Patch 'frida-server' with '{CUSTOM_NAME}-server' recursively...")
204 | frida_server_meson_path = os.path.join(custom_dir, "subprojects/frida-core/server/meson.build")
205 | patch_strings = ["frida-server-raw", "\'frida-server\'", "\"frida-server\"", "frida-server-universal"]
206 | for patch_string in patch_strings:
207 | replace_strings_in_files(frida_server_meson_path,
208 | patch_string,
209 | patch_string.replace("frida", CUSTOM_NAME))
210 |
211 | frida_core_compat_build_path = os.path.join(custom_dir, "subprojects/frida-core/compat/build.py")
212 | patch_string = "frida-server"
213 | replace_strings_in_files(frida_core_compat_build_path,
214 | patch_string,
215 | patch_string.replace("frida", CUSTOM_NAME))
216 |
217 | # frida-gadget patch
218 | print(f"\n[*] Patch 'frida-gadget' with '{CUSTOM_NAME}-gadget' recursively...")
219 | patch_strings = ["\"frida-gadget\"", "\"frida-gadget-tcp", "\"frida-gadget-unix"]
220 | for patch_string in patch_strings:
221 | replace_strings_in_files(custom_dir,
222 | patch_string,
223 | patch_string.replace("frida", CUSTOM_NAME))
224 |
225 | frida_core_meson_path = os.path.join(custom_dir, "subprojects/frida-core/meson.build")
226 | patch_string = "gadget_name = 'frida-gadget' + shlib_suffix"
227 | replace_strings_in_files(frida_core_meson_path,
228 | patch_string,
229 | patch_string.replace("frida", CUSTOM_NAME))
230 |
231 | frida_core_compat_build_py_path = os.path.join(custom_dir, "subprojects/frida-core/compat/build.py")
232 | patch_string = "frida-gadget"
233 | replace_strings_in_files(frida_core_compat_build_py_path,
234 | patch_string,
235 | patch_string.replace("frida", CUSTOM_NAME))
236 |
237 | frida_gadget_meson_path = os.path.join(custom_dir, "subprojects/frida-core/lib/gadget/meson.build")
238 | patch_strings = ["frida-gadget-modulated", "libfrida-gadget-modulated", "frida-gadget-raw", "\'frida-gadget\'",
239 | "frida-gadget-universal", "FridaGadget.dylib"]
240 | for patch_string in patch_strings:
241 | replace_strings_in_files(frida_gadget_meson_path,
242 | patch_string,
243 | patch_string.replace("Frida", capitalize_first_lower_char(
244 | CUSTOM_NAME)) if "Frida" in patch_string else
245 | patch_string.replace("frida", CUSTOM_NAME))
246 |
247 | # gum-js-loop patch
248 | print(f"\n[*] Patch 'gum-js-loop' with '{CUSTOM_NAME}-js-loop' recursively...")
249 | patch_string = "\"gum-js-loop\""
250 | replace_strings_in_files(custom_dir,
251 | patch_string,
252 | patch_string.replace("gum", CUSTOM_NAME))
253 |
254 | # pool-frida patch
255 | print(f"\n[*] Patch 'pool-frida' with 'pool-{CUSTOM_NAME}' recursively...")
256 | patch_string = "g_set_prgname (\"frida\");"
257 | replace_strings_in_files(custom_dir,
258 | patch_string,
259 | patch_string.replace("frida", CUSTOM_NAME))
260 |
261 | # No libc.so libsystem_c.dylib hooking
262 | print(f"\n[*] Patch not to hook libc function")
263 | # frida/subprojects/frida-core/lib/payload/exit-monitor.vala
264 | exit_monitor_path = os.path.join(custom_dir, "subprojects/frida-core/lib/payload/exit-monitor.vala")
265 | patch_string = "interceptor.attach"
266 | replace_strings_in_files(exit_monitor_path,
267 | patch_string,
268 | "// " + patch_string)
269 |
270 | # frida/subprojects/frida-gum/gum/backend-posix/gumexceptor-posix.c
271 | gumexceptor_posix_path = os.path.join(custom_dir, "subprojects/frida-gum/gum/backend-posix/gumexceptor-posix.c")
272 | gumexceptor_posix_patch_strings = ["gum_interceptor_replace",
273 | "gum_exceptor_backend_replacement_signal, self, NULL);",
274 | "gum_exceptor_backend_replacement_sigaction, self, NULL);"]
275 | for patch_string in gumexceptor_posix_patch_strings:
276 | replace_strings_in_files(gumexceptor_posix_path,
277 | patch_string,
278 | "// " + patch_string)
279 |
280 | # Selinux patch for Android
281 | print(f"\n[*] Patch 'frida_file', 'frida_memfd with '{CUSTOM_NAME}_file', '{CUSTOM_NAME}_memfd recursively...")
282 | patch_strings = ["\"frida_file\"", "\"frida_memfd\"", ":frida_file", ":frida_memfd"]
283 | for patch_string in patch_strings:
284 | replace_strings_in_files(custom_dir,
285 | patch_string,
286 | patch_string.replace("frida", CUSTOM_NAME))
287 |
288 | # Perform the first build
289 | for build_dir in build_dirs:
290 | print(f"\n[*] First build for {build_dir.rsplit('/')[-1]}")
291 | build(build_dir)
292 |
293 | # frida_agent_main patch
294 | print(f"\n[*] Patch 'frida_agent_main' with '{CUSTOM_NAME}_agent_main' recursively...")
295 | patch_string = "frida_agent_main"
296 | replace_strings_in_files(custom_dir,
297 | patch_string,
298 | patch_string.replace("frida", CUSTOM_NAME))
299 |
300 | if TEMP == 1:
301 | fix_failed_to_reach_single_threaded_state(custom_dir)
302 | fix_process_enumerate_threads_crash(custom_dir)
303 |
304 | # Second build after patching
305 | for build_dir in build_dirs:
306 | print(f"\n[*] Second build for {build_dir.rsplit('/')[-1]}")
307 | build(build_dir)
308 |
309 | # Patch gmain, gdbus, pool-spawner
310 | gmain = bytes.fromhex('67 6d 61 69 6e 00')
311 | amain = bytes.fromhex('61 6d 61 69 6e 00')
312 |
313 | gdbus = bytes.fromhex('67 64 62 75 73 00')
314 | gdbug = bytes.fromhex('67 64 62 75 67 00')
315 |
316 | pool_spawner = bytes.fromhex('70 6f 6f 6c 2d 73 70 61 77 6e 65 72 00')
317 | pool_spoiler = bytes.fromhex('70 6f 6f 6c 2d 73 70 6f 69 6c 65 72 00')
318 |
319 | patch_list = [os.path.join(build_dir, f"subprojects/frida-core/server/{CUSTOM_NAME}-server") for build_dir in build_dirs] + \
320 | [os.path.join(build_dir, f"subprojects/frida-core/lib/agent/{CUSTOM_NAME}-agent.so") for build_dir in build_dirs] + \
321 | [os.path.join(build_dir, f"subprojects/frida-core/lib/gadget/{CUSTOM_NAME}-gadget.so") for build_dir in build_dirs]
322 |
323 | for file_path in patch_list:
324 | # Open the binary file for reading and writing
325 | with open(file_path, 'rb+') as f:
326 | print(f"\n[*] gmain, gdbus, pool-spawner patch for {file_path}")
327 | # Read the entire file content
328 | content = f.read()
329 | patched_content = content.replace(gmain, amain)
330 | patched_content = patched_content.replace(gdbus, gdbug)
331 | patched_content = patched_content.replace(pool_spawner, pool_spoiler)
332 |
333 | f.seek(0)
334 | f.write(patched_content)
335 | f.truncate()
336 |
337 | # Get frida version
338 | frida_version_py = os.path.join(custom_dir, "releng/frida_version.py")
339 | result = subprocess.run(['python3', frida_version_py], capture_output=True, text=True)
340 | frida_version = result.stdout.strip()
341 |
342 | # Rename
343 | for file_path in patch_list:
344 | if '-agent.so' in file_path:
345 | continue
346 |
347 | arch = [i for i in file_path.split(os.sep) if i.startswith('android-')]
348 | arch = arch[0] if arch else ''
349 |
350 | if file_path.endswith('.so'):
351 | new_file_path = f"{file_path.rsplit('.so', 1)[0]}-{frida_version}-{arch}.so"
352 | if TEMP == 1:
353 | new_file_path = f"{file_path.rsplit('.so', 1)[0]}-{frida_version}-{arch}-temp.so"
354 | else:
355 | new_file_path = f"{file_path}-{frida_version}-{arch}"
356 | if TEMP == 1:
357 | new_file_path = f"{file_path}-{frida_version}-{arch}-temp"
358 |
359 | try:
360 | os.rename(file_path, new_file_path)
361 | print(f"\n[*] Renamed {file_path} to {new_file_path}")
362 | compress_file(new_file_path)
363 |
364 | shutil.move(f"{new_file_path}.gz", f"{assets_dir}")
365 | except Exception as e:
366 | print(f"[!] Error renaming {file_path}: {e}")
367 |
368 |
369 | if __name__ == "__main__":
370 | main()
371 |
--------------------------------------------------------------------------------
/main_macos_android.py:
--------------------------------------------------------------------------------
1 | import gzip
2 | import os
3 | import shutil
4 | import subprocess
5 | import sys
6 |
7 | CUSTOM_NAME = "ajeossida"
8 |
9 | # Temporarily fixing the issue 'Failed to reach single-threaded state', Process.enumerateThreads() crash
10 | TEMP = 0
11 |
12 |
13 | def run_command(command, cwd=None):
14 | try:
15 | result = subprocess.run(command, shell=True, cwd=cwd, check=True, text=True)
16 | return result.returncode
17 | except subprocess.CalledProcessError as e:
18 | print(f"Error while running command: {command}\nError: {e}")
19 | sys.exit(1)
20 |
21 |
22 | def git_clone_repo():
23 | repo_url = "https://github.com/frida/frida.git"
24 | destination_dir = os.path.join(os.getcwd(), CUSTOM_NAME)
25 |
26 | print(f"\n[*] Cloning repository {repo_url} to {destination_dir}...")
27 | run_command(f"git clone --recurse-submodules {repo_url} {destination_dir}")
28 |
29 |
30 | def check_ndk_version():
31 | home_path = os.path.expanduser("~")
32 | ndk_base = os.path.join(home_path, "Library/Android/sdk/ndk")
33 |
34 | ndk_versions = []
35 |
36 | for ndk_version in os.listdir(ndk_base):
37 | dir_path = os.path.join(ndk_base, ndk_version)
38 |
39 | if os.path.isdir(dir_path) and ndk_version.startswith("25."):
40 | ndk_versions.append(ndk_version)
41 |
42 | if not ndk_versions:
43 | print("\n[!] Android NDK r25 is needed")
44 | sys.exit(1)
45 |
46 | # Sort versions and pick the largest
47 | biggest_version = max(ndk_versions, key=lambda v: list(map(int, v.split('.'))))
48 |
49 | biggest_version_path = os.path.join(ndk_base, biggest_version)
50 | print(f"[*] NDK version: {biggest_version}")
51 | return biggest_version_path
52 |
53 |
54 | def configure_build(ndk_path, arch):
55 | build_dir = os.path.join(os.getcwd(), CUSTOM_NAME, arch)
56 | os.makedirs(build_dir, exist_ok=True)
57 |
58 | os.environ['ANDROID_NDK_ROOT'] = ndk_path
59 |
60 | print(f"\n[*] Configuring the build for {arch}...")
61 | result = run_command(f"{os.path.join('..', 'configure')} --host={arch}", cwd=build_dir)
62 |
63 | if result == 0:
64 | return build_dir
65 | else:
66 | print("\n[!] Failed to configure")
67 | sys.exit(1)
68 |
69 |
70 | def build(build_dir):
71 | run_command("make", cwd=build_dir)
72 |
73 |
74 | def replace_strings_in_files(directory, search_string, replace_string):
75 | if os.path.isfile(directory):
76 | file_path = directory
77 | with open(file_path, 'r+', encoding='utf-8') as file:
78 | content = file.read()
79 | if search_string in content:
80 | print(f"Patch {file.name}")
81 | patched_content = content.replace(search_string, replace_string)
82 | file.seek(0)
83 | file.write(patched_content)
84 | file.truncate()
85 | else:
86 | for root, dirs, files in os.walk(directory):
87 | for file_name in files:
88 | file_path = os.path.join(root, file_name)
89 | try:
90 | with open(file_path, 'r+', encoding='utf-8') as file:
91 | content = file.read()
92 | if search_string in content:
93 | print(f"Patch {file.name}")
94 | patched_content = content.replace(search_string, replace_string)
95 | file.seek(0)
96 | file.write(patched_content)
97 | file.truncate()
98 | except Exception as e:
99 | pass
100 |
101 |
102 | def compress_file(file_path):
103 | try:
104 | # Create a .gz file from the original file
105 | with open(file_path, 'rb') as f_in:
106 | with gzip.open(file_path + '.gz', 'wb') as f_out:
107 | shutil.copyfileobj(f_in, f_out)
108 | print(f"[*] Compressed {file_path} to {file_path}.gz")
109 | except Exception as e:
110 | print(f"[!] Error compressing {file_path}: {e}")
111 |
112 |
113 | def capitalize_first_lower_char(word):
114 | for index, char in enumerate(word):
115 | if char.islower():
116 | # Replace the first lowercase character with its uppercase equivalent
117 | return word[:index] + char.upper() + word[index + 1:]
118 | return word
119 |
120 |
121 | def fix_failed_to_reach_single_threaded_state(custom_dir):
122 | old_cloak_vala_path = os.path.join(custom_dir, "subprojects/frida-core/lib/payload/cloak.vala")
123 | new_cloak_vala_path = os.path.join(os.getcwd(), "fix_failed_to_reach_single_threaded_state.txt")
124 | os.remove(old_cloak_vala_path)
125 | shutil.copy(new_cloak_vala_path, old_cloak_vala_path)
126 |
127 |
128 | def fix_process_enumerate_threads_crash(custom_dir):
129 | # On the Pixel 4a, if there's a thread named perfetto_hprof_, then Process.enumerateThreads() crashes with SEGV_ACCERR.
130 | gumprocess_linux_path = os.path.join(custom_dir, "subprojects/frida-gum/gum/backend-linux/gumprocess-linux.c")
131 | replace_strings_in_files(gumprocess_linux_path,
132 | ' details.name = thread_name;',
133 | ' details.name = thread_name;\n'
134 | ' if (strcmp(details.name, "perfetto_hprof_") == 0)\n'
135 | ' continue;')
136 |
137 |
138 | def main():
139 | custom_dir = os.path.join(os.getcwd(), CUSTOM_NAME)
140 | if os.path.exists(custom_dir):
141 | print(f"\n[*] Cleaning {custom_dir}...")
142 | shutil.rmtree(custom_dir)
143 |
144 | assets_dir = os.path.join(os.getcwd(), "assets")
145 | if os.path.exists(assets_dir):
146 | print(f"\n[*] Cleaning {assets_dir}...")
147 | shutil.rmtree(assets_dir)
148 | os.mkdir(assets_dir)
149 |
150 | git_clone_repo()
151 |
152 | ndk_version_path = check_ndk_version()
153 |
154 | architectures = ["android-arm64", "android-arm", "android-x86_64", "android-x86"]
155 | if TEMP == 1:
156 | architectures = ["android-arm64"]
157 | build_dirs = [configure_build(ndk_version_path, arch) for arch in architectures]
158 |
159 | # libfrida-agent-raw.so patch
160 | print(f"\n[*] Patch 'libfrida-agent-raw.so' with 'lib{CUSTOM_NAME}-agent-raw.so' recursively...")
161 | patch_string = "libfrida-agent-raw.so"
162 | replace_strings_in_files(custom_dir,
163 | patch_string,
164 | patch_string.replace("frida", CUSTOM_NAME))
165 |
166 | # re.frida.server patch
167 | print(f"\n[*] Patch 're.frida.server' with 're.{CUSTOM_NAME}.server' recursively...")
168 | patch_string = "re.frida.server"
169 | replace_strings_in_files(custom_dir,
170 | patch_string,
171 | patch_string.replace("frida", CUSTOM_NAME))
172 |
173 | # frida-helper patch
174 | print(f"\n[*] Patch 'frida-helper' with '{CUSTOM_NAME}-helper' recursively...")
175 | patch_strings = ["frida-helper-32", "frida-helper-64", "get_frida_helper_", "\"/frida-\""]
176 | for patch_string in patch_strings:
177 | replace_strings_in_files(custom_dir,
178 | patch_string,
179 | patch_string.replace("frida", CUSTOM_NAME))
180 |
181 | # frida-agent patch
182 | print(f"\n[*] Patch 'frida-agent' with '{CUSTOM_NAME}-agent' recursively...")
183 | patch_strings = ["frida-agent-", "\"agent\" / \"frida-agent.", "\'frida-agent\'", "\"frida-agent\"",
184 | "get_frida_agent_", "\'FridaAgent\'"]
185 | for patch_string in patch_strings:
186 | replace_strings_in_files(custom_dir,
187 | patch_string,
188 | patch_string.replace("Frida", capitalize_first_lower_char(
189 | CUSTOM_NAME)) if "Frida" in patch_string else
190 | patch_string.replace("frida", CUSTOM_NAME))
191 |
192 | # Patch the original file back, which has incorrectly patched strings.
193 | wrong_patch_strings = [f'{CUSTOM_NAME}-agent-x86.symbols', f'{CUSTOM_NAME}-agent-android.version']
194 | for wrong_patch_string in wrong_patch_strings:
195 | replace_strings_in_files(custom_dir,
196 | wrong_patch_string,
197 | wrong_patch_string.replace(CUSTOM_NAME, 'frida'))
198 |
199 | # memfd_create patch, memfd:ajeossida-agent-64.so --> memfd:jit-cache
200 | print(f"\n[*] Patch MemoryFileDescriptor.memfd_create")
201 | frida_helper_backend_path = os.path.join(custom_dir,
202 | "subprojects/frida-core/src/linux/frida-helper-backend.vala")
203 | replace_strings_in_files(frida_helper_backend_path,
204 | 'return Linux.syscall (SysCall.memfd_create, name, flags);',
205 | 'return Linux.syscall (SysCall.memfd_create, \"jit-cache\", flags);')
206 |
207 | # frida-server patch
208 | print(f"\n[*] Patch 'frida-server' with '{CUSTOM_NAME}-server' recursively...")
209 | frida_server_meson_path = os.path.join(custom_dir, "subprojects/frida-core/server/meson.build")
210 | patch_strings = ["frida-server-raw", "\'frida-server\'", "\"frida-server\"", "frida-server-universal"]
211 | for patch_string in patch_strings:
212 | replace_strings_in_files(frida_server_meson_path,
213 | patch_string,
214 | patch_string.replace("frida", CUSTOM_NAME))
215 |
216 | frida_core_compat_build_path = os.path.join(custom_dir, "subprojects/frida-core/compat/build.py")
217 | patch_string = "frida-server"
218 | replace_strings_in_files(frida_core_compat_build_path,
219 | patch_string,
220 | patch_string.replace("frida", CUSTOM_NAME))
221 |
222 | # frida-gadget patch
223 | print(f"\n[*] Patch 'frida-gadget' with '{CUSTOM_NAME}-gadget' recursively...")
224 | patch_strings = ["\"frida-gadget\"", "\"frida-gadget-tcp", "\"frida-gadget-unix"]
225 | for patch_string in patch_strings:
226 | replace_strings_in_files(custom_dir,
227 | patch_string,
228 | patch_string.replace("frida", CUSTOM_NAME))
229 |
230 | frida_core_meson_path = os.path.join(custom_dir, "subprojects/frida-core/meson.build")
231 | patch_string = "gadget_name = 'frida-gadget' + shlib_suffix"
232 | replace_strings_in_files(frida_core_meson_path,
233 | patch_string,
234 | patch_string.replace("frida", CUSTOM_NAME))
235 |
236 | frida_core_compat_build_py_path = os.path.join(custom_dir, "subprojects/frida-core/compat/build.py")
237 | patch_string = "frida-gadget"
238 | replace_strings_in_files(frida_core_compat_build_py_path,
239 | patch_string,
240 | patch_string.replace("frida", CUSTOM_NAME))
241 |
242 | frida_gadget_meson_path = os.path.join(custom_dir, "subprojects/frida-core/lib/gadget/meson.build")
243 | patch_strings = ["frida-gadget-modulated", "libfrida-gadget-modulated", "frida-gadget-raw", "\'frida-gadget\'",
244 | "frida-gadget-universal", "FridaGadget.dylib"]
245 | for patch_string in patch_strings:
246 | replace_strings_in_files(frida_gadget_meson_path,
247 | patch_string,
248 | patch_string.replace("Frida", capitalize_first_lower_char(
249 | CUSTOM_NAME)) if "Frida" in patch_string else
250 | patch_string.replace("frida", CUSTOM_NAME))
251 |
252 | # gum-js-loop patch
253 | print(f"\n[*] Patch 'gum-js-loop' with '{CUSTOM_NAME}-js-loop' recursively...")
254 | patch_string = "\"gum-js-loop\""
255 | replace_strings_in_files(custom_dir,
256 | patch_string,
257 | patch_string.replace("gum", CUSTOM_NAME))
258 |
259 | # pool-frida patch
260 | print(f"\n[*] Patch 'pool-frida' with 'pool-{CUSTOM_NAME}' recursively...")
261 | patch_string = "g_set_prgname (\"frida\");"
262 | replace_strings_in_files(custom_dir,
263 | patch_string,
264 | patch_string.replace("frida", CUSTOM_NAME))
265 |
266 | # No libc.so hooking
267 | print(f"\n[*] Patch not to hook libc function")
268 | # frida/subprojects/frida-core/lib/payload/exit-monitor.vala
269 | exit_monitor_path = os.path.join(custom_dir, "subprojects/frida-core/lib/payload/exit-monitor.vala")
270 | patch_string = "interceptor.attach"
271 | replace_strings_in_files(exit_monitor_path,
272 | patch_string,
273 | "// " + patch_string)
274 |
275 | # frida/subprojects/frida-gum/gum/backend-posix/gumexceptor-posix.c
276 | gumexceptor_posix_path = os.path.join(custom_dir, "subprojects/frida-gum/gum/backend-posix/gumexceptor-posix.c")
277 | gumexceptor_posix_patch_strings = ["gum_interceptor_replace",
278 | "gum_exceptor_backend_replacement_signal, self, NULL);",
279 | "gum_exceptor_backend_replacement_sigaction, self, NULL);"]
280 | for patch_string in gumexceptor_posix_patch_strings:
281 | replace_strings_in_files(gumexceptor_posix_path,
282 | patch_string,
283 | "// " + patch_string)
284 |
285 | # Selinux patch for Android
286 | print(f"\n[*] Patch 'frida_file', 'frida_memfd with '{CUSTOM_NAME}_file', '{CUSTOM_NAME}_memfd' recursively...")
287 | patch_strings = ["\"frida_file\"", "\"frida_memfd\"", ":frida_file", ":frida_memfd"]
288 | for patch_string in patch_strings:
289 | replace_strings_in_files(custom_dir,
290 | patch_string,
291 | patch_string.replace("frida", CUSTOM_NAME))
292 |
293 | # Perform the first build
294 | for build_dir in build_dirs:
295 | print(f"\n[*] First build for {build_dir.rsplit('/')[-1]}")
296 | build(build_dir)
297 |
298 | # frida_agent_main patch
299 | print(f"\n[*] Patch 'frida_agent_main' with '{CUSTOM_NAME}_agent_main' recursively...")
300 | patch_string = "frida_agent_main"
301 | replace_strings_in_files(custom_dir,
302 | patch_string,
303 | patch_string.replace("frida", CUSTOM_NAME))
304 |
305 | if TEMP == 1:
306 | fix_failed_to_reach_single_threaded_state(custom_dir)
307 | fix_process_enumerate_threads_crash(custom_dir)
308 |
309 | # Second build after patching
310 | for build_dir in build_dirs:
311 | print(f"\n[*] Second build for {build_dir.rsplit('/')[-1]}")
312 | build(build_dir)
313 |
314 | # Patch gmain, gdbus, pool-spawner
315 | gmain = bytes.fromhex('67 6d 61 69 6e 00')
316 | amain = bytes.fromhex('61 6d 61 69 6e 00')
317 |
318 | gdbus = bytes.fromhex('67 64 62 75 73 00')
319 | gdbug = bytes.fromhex('67 64 62 75 67 00')
320 |
321 | pool_spawner = bytes.fromhex('70 6f 6f 6c 2d 73 70 61 77 6e 65 72 00')
322 | pool_spoiler = bytes.fromhex('70 6f 6f 6c 2d 73 70 6f 69 6c 65 72 00')
323 |
324 | patch_list = [os.path.join(build_dir, f"subprojects/frida-core/server/{CUSTOM_NAME}-server") for build_dir in
325 | build_dirs] + \
326 | [os.path.join(build_dir, f"subprojects/frida-core/lib/agent/{CUSTOM_NAME}-agent.so") for build_dir in
327 | build_dirs] + \
328 | [os.path.join(build_dir, f"subprojects/frida-core/lib/gadget/{CUSTOM_NAME}-gadget.so") for build_dir in
329 | build_dirs]
330 |
331 | for file_path in patch_list:
332 | # Open the binary file for reading and writing
333 | with open(file_path, 'rb+') as f:
334 | print(f"\n[*] gmain, gdbus, pool-spawner patch for {file_path}")
335 | # Read the entire file content
336 | content = f.read()
337 | patched_content = content.replace(gmain, amain)
338 | patched_content = patched_content.replace(gdbus, gdbug)
339 | patched_content = patched_content.replace(pool_spawner, pool_spoiler)
340 |
341 | f.seek(0)
342 | f.write(patched_content)
343 | f.truncate()
344 |
345 | # Get frida version
346 | frida_version_py = os.path.join(custom_dir, "releng/frida_version.py")
347 | result = subprocess.run(['python3', frida_version_py], capture_output=True, text=True)
348 | frida_version = result.stdout.strip()
349 |
350 | # Rename
351 | for file_path in patch_list:
352 | if '-agent.so' in file_path:
353 | continue
354 |
355 | arch = [i for i in file_path.split(os.sep) if i.startswith('android-')]
356 | arch = arch[0] if arch else ''
357 |
358 | if file_path.endswith('.so'):
359 | new_file_path = f"{file_path.rsplit('.so', 1)[0]}-{frida_version}-{arch}.so"
360 | if TEMP == 1:
361 | new_file_path = f"{file_path.rsplit('.so', 1)[0]}-{frida_version}-{arch}-temp.so"
362 | else:
363 | new_file_path = f"{file_path}-{frida_version}-{arch}"
364 | if TEMP == 1:
365 | new_file_path = f"{file_path}-{frida_version}-{arch}-temp"
366 |
367 | try:
368 | os.rename(file_path, new_file_path)
369 | print(f"\n[*] Renamed {file_path} to {new_file_path}")
370 | compress_file(new_file_path)
371 |
372 | shutil.move(f"{new_file_path}.gz", f"{assets_dir}")
373 | except Exception as e:
374 | print(f"[!] Error renaming {file_path}: {e}")
375 |
376 | print(f"\n[*] Building of {CUSTOM_NAME} completed. The output is in the assets directory")
377 |
378 |
379 | if __name__ == "__main__":
380 | main()
381 |
--------------------------------------------------------------------------------
/main_macos_ios.py:
--------------------------------------------------------------------------------
1 | import gzip
2 | import os
3 | import shutil
4 | import subprocess
5 | import sys
6 |
7 | CUSTOM_NAME = "ajeossida"
8 |
9 |
10 | def run_command(command, cwd=None):
11 | try:
12 | result = subprocess.run(command, shell=True, cwd=cwd, check=True, text=True)
13 | return result.returncode
14 | except subprocess.CalledProcessError as e:
15 | print(f"Error while running command: {command}\nError: {e}")
16 | if "ios" in cwd:
17 | # frida-helper patch
18 | print(f"\n[*] Patch 'get_frida_helper_' with 'get_{CUSTOM_NAME}_helper_' recursively...")
19 | patch_strings = ["get_frida_helper_"]
20 | for patch_string in patch_strings:
21 | replace_strings_in_files(os.path.join(os.getcwd(), CUSTOM_NAME), patch_string,
22 | patch_string.replace("frida", f"{CUSTOM_NAME}"))
23 | # frida-agent patch
24 | print(f"\n[*] Patch 'get_frida_agent_' with 'get_{CUSTOM_NAME}_agent_' recursively...")
25 | patch_strings = ["get_frida_agent_"]
26 | for patch_string in patch_strings:
27 | replace_strings_in_files(os.path.join(os.getcwd(), CUSTOM_NAME), patch_string,
28 | patch_string.replace("frida", f"{CUSTOM_NAME}"))
29 | # build again
30 | build(cwd)
31 | else:
32 | sys.exit(1)
33 |
34 |
35 | def git_clone_repo():
36 | repo_url = "https://github.com/frida/frida.git"
37 | destination_dir = os.path.join(os.getcwd(), CUSTOM_NAME)
38 |
39 | print(f"\n[*] Cloning repository {repo_url} to {destination_dir}...")
40 | run_command(f"git clone --recurse-submodules {repo_url} {destination_dir}")
41 |
42 |
43 | def configure_build(arch):
44 | build_dir = os.path.join(os.getcwd(), CUSTOM_NAME, arch)
45 | os.makedirs(build_dir, exist_ok=True)
46 |
47 | ios_keychain = "frida-signing"
48 | print(f"\n[*] Check {ios_keychain}...")
49 | result = subprocess.run(f"security find-identity -v -p codesigning | grep {ios_keychain}", shell=True, text=False)
50 | if result.returncode != 0:
51 | print("\n[!] Cannot find \"frida-signing\" keychain")
52 | sys.exit(1)
53 | print(f"\n[*] Set 'MACOS_CERTID' and 'IOS_CERTID' environment variables with {ios_keychain}")
54 | os.environ['MACOS_CERTID'] = ios_keychain
55 | os.environ['IOS_CERTID'] = ios_keychain
56 |
57 | print(f"\n[*] Configuring the build for {arch}...")
58 | result = run_command(
59 | f"{os.path.join('..', 'configure')} --prefix=/usr --host={arch} --enable-portal -- -Dfrida-gum:devkits=gum,gumjs -Dfrida-core:assets=installed -Dfrida-core:devkits=core",
60 | cwd=build_dir)
61 |
62 | if result == 0:
63 | return build_dir
64 | else:
65 | print("\n[!] Failed to configure")
66 | sys.exit(1)
67 |
68 |
69 | def build(build_dir):
70 | run_command("make", cwd=build_dir)
71 |
72 |
73 | def replace_strings_in_files(directory, search_string, replace_string):
74 | if os.path.isfile(directory):
75 | file_path = directory
76 | with open(file_path, 'r+', encoding='utf-8') as file:
77 | content = file.read()
78 | if search_string in content:
79 | print(f"Patch {file.name}")
80 | patched_content = content.replace(search_string, replace_string)
81 | file.seek(0)
82 | file.write(patched_content)
83 | file.truncate()
84 | else:
85 | for root, dirs, files in os.walk(directory):
86 | for file_name in files:
87 | file_path = os.path.join(root, file_name)
88 | try:
89 | with open(file_path, 'r+', encoding='utf-8') as file:
90 | content = file.read()
91 | if search_string in content:
92 | print(f"Patch {file.name}")
93 | patched_content = content.replace(search_string, replace_string)
94 | file.seek(0)
95 | file.write(patched_content)
96 | file.truncate()
97 | except Exception as e:
98 | pass
99 |
100 |
101 | def capitalize_first_lower_char(word):
102 | for index, char in enumerate(word):
103 | if char.islower():
104 | # Replace the first lowercase character with its uppercase equivalent
105 | return word[:index] + char.upper() + word[index + 1:]
106 | return word
107 |
108 |
109 | def main():
110 | custom_dir = os.path.join(os.getcwd(), CUSTOM_NAME)
111 | if os.path.exists(custom_dir):
112 | print(f"\n[*] Cleaning {custom_dir}...")
113 | shutil.rmtree(custom_dir)
114 |
115 | assets_dir = os.path.join(os.getcwd(), "assets")
116 | if os.path.exists(assets_dir):
117 | print(f"\n[*] Cleaning {assets_dir}...")
118 | shutil.rmtree(assets_dir)
119 | os.mkdir(assets_dir)
120 |
121 | ios_assets_dir = os.path.join(os.getcwd(), "ios-assets")
122 | if os.path.exists(ios_assets_dir):
123 | print(f"\n[*] Cleaning {ios_assets_dir}...")
124 | shutil.rmtree(ios_assets_dir)
125 | ios_assets_frida_server_dir = os.path.join(ios_assets_dir, 'usr/bin')
126 | ios_assets_frida_agent_dir = os.path.join(ios_assets_dir, f'usr/lib/{CUSTOM_NAME}')
127 | os.makedirs(ios_assets_frida_server_dir, exist_ok=True)
128 | os.makedirs(ios_assets_frida_agent_dir, exist_ok=True)
129 |
130 | git_clone_repo()
131 |
132 | architectures = ["ios-arm64e"]
133 | build_dirs = [configure_build(arch) for arch in architectures]
134 |
135 | # re.frida.server patch
136 | print(f"\n[*] Patch 're.frida.server' with 're.{CUSTOM_NAME}.server' recursively...")
137 | patch_string = "re.frida.server"
138 | replace_strings_in_files(custom_dir,
139 | patch_string,
140 | patch_string.replace("frida", CUSTOM_NAME))
141 |
142 | # frida-helper patch
143 | print(f"\n[*] Patch 'frida-helper' with '{CUSTOM_NAME}-helper' recursively...")
144 | patch_strings = ["frida-helper-32", "frida-helper-64", "get_frida_helper_", "\"/frida-\""]
145 | for patch_string in patch_strings:
146 | replace_strings_in_files(custom_dir,
147 | patch_string,
148 | patch_string.replace("frida", CUSTOM_NAME))
149 |
150 | # frida-agent patch
151 | print(f"\n[*] Patch 'frida-agent' with '{CUSTOM_NAME}-agent' recursively...")
152 | patch_strings = ["frida-agent-", "\"agent\" / \"frida-agent.", "\'frida-agent\'", "\"frida-agent\"",
153 | "get_frida_agent_", "\'FridaAgent\'"]
154 | for patch_string in patch_strings:
155 | replace_strings_in_files(custom_dir,
156 | patch_string,
157 | patch_string.replace("Frida", capitalize_first_lower_char(
158 | CUSTOM_NAME)) if "Frida" in patch_string else
159 | patch_string.replace("frida", CUSTOM_NAME))
160 |
161 | # Patch the original file back, which has incorrectly patched strings.
162 | wrong_patch_strings = [f'{CUSTOM_NAME}-agent-x86.symbols', f'{CUSTOM_NAME}-agent-android.version']
163 | for wrong_patch_string in wrong_patch_strings:
164 | replace_strings_in_files(custom_dir,
165 | wrong_patch_string,
166 | wrong_patch_string.replace(CUSTOM_NAME, 'frida'))
167 |
168 | # frida-server patch
169 | print(f"\n[*] Patch 'frida-server' with '{CUSTOM_NAME}-server' recursively...")
170 | frida_server_meson_path = os.path.join(custom_dir, "subprojects/frida-core/server/meson.build")
171 | patch_strings = ["frida-server-raw", "\'frida-server\'", "\"frida-server\"", "frida-server-universal"]
172 | for patch_string in patch_strings:
173 | replace_strings_in_files(frida_server_meson_path,
174 | patch_string,
175 | patch_string.replace("frida", CUSTOM_NAME))
176 |
177 | frida_core_compat_build_path = os.path.join(custom_dir, "subprojects/frida-core/compat/build.py")
178 | patch_string = "frida-server"
179 | replace_strings_in_files(frida_core_compat_build_path,
180 | patch_string,
181 | patch_string.replace("frida", CUSTOM_NAME))
182 |
183 | # frida-gadget patch
184 | print(f"\n[*] Patch 'frida-gadget' with '{CUSTOM_NAME}-gadget' recursively...")
185 | patch_strings = ["\"frida-gadget\"", "\"frida-gadget-tcp", "\"frida-gadget-unix"]
186 | for patch_string in patch_strings:
187 | replace_strings_in_files(custom_dir,
188 | patch_string,
189 | patch_string.replace("frida", CUSTOM_NAME))
190 |
191 | frida_core_meson_path = os.path.join(custom_dir, "subprojects/frida-core/meson.build")
192 | patch_string = "gadget_name = 'frida-gadget' + shlib_suffix"
193 | replace_strings_in_files(frida_core_meson_path,
194 | patch_string,
195 | patch_string.replace("frida", CUSTOM_NAME))
196 |
197 | frida_core_compat_build_py_path = os.path.join(custom_dir, "subprojects/frida-core/compat/build.py")
198 | patch_string = "frida-gadget"
199 | replace_strings_in_files(frida_core_compat_build_py_path,
200 | patch_string,
201 | patch_string.replace("frida", CUSTOM_NAME))
202 |
203 | frida_gadget_meson_path = os.path.join(custom_dir, "subprojects/frida-core/lib/gadget/meson.build")
204 | patch_strings = ["frida-gadget-modulated", "libfrida-gadget-modulated", "frida-gadget-raw", "\'frida-gadget\'",
205 | "frida-gadget-universal", "FridaGadget.dylib"]
206 | for patch_string in patch_strings:
207 | replace_strings_in_files(frida_gadget_meson_path,
208 | patch_string,
209 | patch_string.replace("Frida", capitalize_first_lower_char(
210 | CUSTOM_NAME)) if "Frida" in patch_string else
211 | patch_string.replace("frida", CUSTOM_NAME))
212 |
213 | # gum-js-loop patch
214 | print(f"\n[*] Patch 'gum-js-loop' with '{CUSTOM_NAME}-js-loop' recursively...")
215 | patch_string = "\"gum-js-loop\""
216 | replace_strings_in_files(custom_dir,
217 | patch_string,
218 | patch_string.replace("gum", CUSTOM_NAME))
219 |
220 | # pool-frida patch
221 | print(f"\n[*] Patch 'pool-frida' with 'pool-{CUSTOM_NAME}' recursively...")
222 | patch_string = "g_set_prgname (\"frida\");"
223 | replace_strings_in_files(custom_dir,
224 | patch_string,
225 | patch_string.replace("frida", CUSTOM_NAME))
226 |
227 | # No libsystem_c.dylib hooking
228 | print(f"\n[*] Patch not to hook libc function")
229 | # frida/subprojects/frida-core/lib/payload/exit-monitor.vala
230 | exit_monitor_path = os.path.join(custom_dir, "subprojects/frida-core/lib/payload/exit-monitor.vala")
231 | patch_string = "interceptor.attach"
232 | replace_strings_in_files(exit_monitor_path,
233 | patch_string,
234 | "// " + patch_string)
235 |
236 | # frida/subprojects/frida-gum/gum/backend-posix/gumexceptor-posix.c
237 | gumexceptor_posix_path = os.path.join(custom_dir, "subprojects/frida-gum/gum/backend-posix/gumexceptor-posix.c")
238 | gumexceptor_posix_patch_strings = ["gum_interceptor_replace",
239 | "gum_exceptor_backend_replacement_signal, self, NULL);",
240 | "gum_exceptor_backend_replacement_sigaction, self, NULL);"]
241 | for patch_string in gumexceptor_posix_patch_strings:
242 | replace_strings_in_files(gumexceptor_posix_path,
243 | patch_string,
244 | "// " + patch_string)
245 |
246 | # No removing cloaked threads
247 | print(f"\n[*] Patch thread-suspend-monitor for iOS")
248 | # frida/subprojects/frida-core/lib/payload/thread-suspend-monitor.vala
249 | thread_suspend_monitor_path = os.path.join(custom_dir,
250 | "subprojects/frida-core/lib/payload/thread-suspend-monitor.vala")
251 | patch_string = "interceptor.replace ((void *) task_threads"
252 | replace_strings_in_files(thread_suspend_monitor_path,
253 | patch_string,
254 | "// " + patch_string)
255 |
256 | # # Need to patch?? frida/subprojects/frida-core/lib/payload/unwind-sitter.vala
257 | # print(f"\n[*] Patch unwind-sitter.vala for iOS")
258 | # unwind_sitter_path = os.path.join(custom_dir, "subprojects/frida-core/lib/payload/unwind-sitter.vala")
259 | # patch_string = "interceptor.replace"
260 | # replace_strings_in_files(unwind_sitter_path,
261 | # patch_string,
262 | # "// " + patch_string)
263 |
264 | # Perform the first build
265 | for build_dir in build_dirs:
266 | print(f"\n[*] First build for {build_dir.rsplit('/')[-1]}")
267 | build(build_dir)
268 |
269 | # frida_agent_main patch
270 | print(f"\n[*] Patch 'frida_agent_main' with '{CUSTOM_NAME}_agent_main' recursively...")
271 | patch_string = "frida_agent_main"
272 | replace_strings_in_files(custom_dir,
273 | patch_string,
274 | patch_string.replace("frida", CUSTOM_NAME))
275 |
276 | # Second build after patching
277 | for build_dir in build_dirs:
278 | print(f"\n[*] Second build for {build_dir.rsplit('/')[-1]}")
279 | build(build_dir)
280 | if 'ios-' in build_dir:
281 | # No patch gumexceptor-posix.c to prevent the crash issue when scanning the memory
282 | for patch_string in gumexceptor_posix_patch_strings:
283 | replace_strings_in_files(gumexceptor_posix_path,
284 | "// " + patch_string,
285 | patch_string)
286 |
287 | # /usr/lib/frida/frida-agent.dylib path patch
288 | print(f"\n[*] Patch 'usr/lib/frida' with 'usr/lib/{CUSTOM_NAME}' recursively...")
289 | patch_string = "usr/lib/frida"
290 | replace_strings_in_files(custom_dir,
291 | patch_string,
292 | patch_string.replace("frida", CUSTOM_NAME))
293 |
294 | # Third build for iOS
295 | print(f"\n[*] Third build for {build_dir.rsplit('/')[-1]}")
296 | build(build_dir)
297 |
298 | # Patch gmain, gdbus, pool-spawner
299 | gmain = bytes.fromhex('67 6d 61 69 6e 00')
300 | amain = bytes.fromhex('61 6d 61 69 6e 00')
301 |
302 | gdbus = bytes.fromhex('67 64 62 75 73 00')
303 | gdbug = bytes.fromhex('67 64 62 75 67 00')
304 |
305 | pool_spawner = bytes.fromhex('70 6f 6f 6c 2d 73 70 61 77 6e 65 72 00')
306 | pool_spoiler = bytes.fromhex('70 6f 6f 6c 2d 73 70 6f 69 6c 65 72 00')
307 |
308 | patch_list = [os.path.join(build_dir, f"subprojects/frida-core/server/{CUSTOM_NAME}-server") for build_dir in
309 | build_dirs] + \
310 | [os.path.join(build_dir,
311 | f"subprojects/frida-core/lib/agent/{CUSTOM_NAME}-agent.dylib") for build_dir in
312 | build_dirs] + \
313 | [os.path.join(build_dir,
314 | f"subprojects/frida-core/lib/gadget/{CUSTOM_NAME}-gadget.dylib") for build_dir in
315 | build_dirs]
316 |
317 | for file_path in patch_list:
318 | # Open the binary file for reading and writing
319 | with open(file_path, 'rb+') as f:
320 | print(f"\n[*] gmain, gdbus, pool-spawner patch for {file_path}")
321 | # Read the entire file content
322 | content = f.read()
323 | patched_content = content.replace(gmain, amain)
324 | patched_content = patched_content.replace(gdbus, gdbug)
325 | patched_content = patched_content.replace(pool_spawner, pool_spoiler)
326 |
327 | f.seek(0)
328 | f.write(patched_content)
329 | f.truncate()
330 |
331 | # Patch packcage-server-fruity.sh for iOS
332 | print(f"\n[*] Patch package-server-fruity.sh")
333 | # frida/subprojects/frida-core/tools/package-server-fruity.sh
334 | package_server_fruity_path = os.path.join(custom_dir, "subprojects/frida-core/tools/package-server-fruity.sh")
335 | patch_strings = ["frida-server", "frida-agent.dylib"]
336 | for patch_string in patch_strings:
337 | replace_strings_in_files(package_server_fruity_path,
338 | patch_string,
339 | patch_string.replace("frida", f"{CUSTOM_NAME}"))
340 |
341 | # Get frida version
342 | frida_version_py = os.path.join(custom_dir, "releng/frida_version.py")
343 | result = subprocess.run(['python3', frida_version_py], capture_output=True, text=True)
344 | frida_version = result.stdout.strip()
345 |
346 | # Rename
347 | for file_path in patch_list:
348 | if 'ios-' in file_path:
349 | try:
350 | if '-server' in file_path:
351 | shutil.move(file_path, ios_assets_frida_server_dir)
352 | elif '-agent.dylib' in file_path:
353 | shutil.move(file_path, ios_assets_frida_agent_dir)
354 | else:
355 | shutil.move(file_path, ios_assets_dir)
356 | except Exception as e:
357 | print(f"[!] Error {file_path}: {e}")
358 |
359 | # Create universal frida-server for iOS
360 | # lipo, codesign frida-server, codesign frida-agent.dylib
361 | for arch in ["arm64", "arm64e"]:
362 | run_command(f"lipo {CUSTOM_NAME}-server -thin {arch} -output {CUSTOM_NAME}-server-{arch}",
363 | cwd=f"{ios_assets_dir}/usr/bin")
364 | run_command(f"codesign -f -s \"-\" --preserve-metadata=entitlements {CUSTOM_NAME}-server-{arch}",
365 | cwd=f"{ios_assets_dir}/usr/bin")
366 | run_command(f"codesign -f -s \"-\" {CUSTOM_NAME}-agent.dylib", cwd=f"{ios_assets_dir}/usr/lib/{CUSTOM_NAME}")
367 | # Codesign frida-gadget.dylib
368 | run_command(f"codesign -f -s \"-\" {CUSTOM_NAME}-gadget.dylib", cwd=f"{ios_assets_dir}")
369 |
370 | # Make fat macho
371 | print("\n[*] Making fat macho...")
372 | mkfatmacho_py = os.path.join(custom_dir, "releng/mkfatmacho.py")
373 | result = subprocess.run(["python3", mkfatmacho_py, f"{CUSTOM_NAME}-server", f"{CUSTOM_NAME}-server-arm64",
374 | f"{CUSTOM_NAME}-server-arm64e"], capture_output=True, text=True,
375 | cwd=f"{ios_assets_dir}/usr/bin")
376 | if result.returncode == 0:
377 | for arch in ["arm64", "arm64e"]:
378 | os.remove(f"{ios_assets_dir}/usr/bin/{CUSTOM_NAME}-server-{arch}")
379 | else:
380 | print("Error while making fat macho")
381 | sys.exit(1)
382 |
383 | # deb packaging
384 | print("\n[*] Packaging deb...")
385 | os.environ['FRIDA_VERSION'] = frida_version
386 | deb_packaging_arch = "iphoneos-arm64"
387 | run_command(
388 | f"{custom_dir}/subprojects/frida-core/tools/package-server-fruity.sh {deb_packaging_arch} {ios_assets_dir} {CUSTOM_NAME}_{os.environ['FRIDA_VERSION']}_{deb_packaging_arch}.deb", cwd=assets_dir)
389 |
390 | print(f"\n[*] Building of {CUSTOM_NAME} completed. The output is in the assets directory")
391 |
392 |
393 | if __name__ == "__main__":
394 | main()
395 |
--------------------------------------------------------------------------------