UnCrackable Level 1 - Mobile Security Writeup
מאת שי מרדכי | 22 באפריל 2026
1. Root & Debug Detection
בתחילת האפליקציה, הפונקציה onCreate בודקת האם המכשיר פרוץ או מחובר לדיבאגר[cite: 2]:
@Override // android.app.Activity
protected void onCreate(Bundle bundle) {
if (root_detection.a() || root_detection.b() || root_detection.c()) {
a("Root detected!");
}
if (debug_detection.a(getApplicationContext())) {
a("App is debuggable!");
}
super.onCreate(bundle);
setContentView(2130903040);
}
ה-Debug בודק האם ה-debug flag שווה ל-2[cite: 2]:
public class debug_detection {
public static boolean a(Context context) {
return (context.getApplicationContext().getApplicationInfo().flags & 2) != 0;
}
}
2. Root Detection Breakdown
בדיקה 1, דרך ה-PATH: getenv מחזיר את ה-PATH - רשימת תיקיות שבה ה-OS מחפשת פקודות[cite: 2]. הבדיקה מחזירה אמת אם ב-PATH קיים קובץ su (Switch User), שמאפשר העלאת הרשאות כאינדיקציה ל-root[cite: 2]. שאלה למחשבה: מה אם האפליקציה תיגש ל-su שנמצא במקום לא סטנדרטי? הבדיקה הזו לא תגלה על כך[cite: 2].
בדיקה 2, דרך ה-ROM: המשתנה Build.TAGS שומר את סוג המפתחות ששימשו לחתימת ה-ROM[cite: 2]. במכשיר rooted לרוב יופיע test-keys במקום release-keys[cite: 2]. כיום הבדיקה פחות רלוונטית (למשל עם Magisk על ROM מקורי)[cite: 2].
בדיקה 3, דרך קבצי Root: השימוש ב-exists() מבצע syscall (כמו stat/access) דרך ה-JNI לבדיקה אם הנתיב קיים[cite: 2]. זהו מעבר מ-user mode ל-kernel mode ובדיקה מול מערכת הקבצים עצמה[cite: 2]. זו שאילתא שקטה שלא יוצרת תהליך, ולכן משאירה פחות עקבות[cite: 2].
3. Validation Logic (AES)
הפונקציה מפענחת (init=2) מחרוזת בעזרת מפתח ואלגוריתם AES/ECB/PKCS7Padding[cite: 2]. את ה-plaintext היא שולחת אח"כ לפונקציית ה-validation שמשווה אותו לקלט מהמשתמש[cite: 2].
4. Bypass Strategy & Frida Hook
כדי לעקוף את מנגנוני ההגנה, דרסתי כל אחת מפונקציות הבדיקה שיחזירו תמיד false[cite: 2]. לגבי פונקציית הולידציה, בחרתי לעטוף אותה (Spy) בפקודת הצצה למחרוזת המפוענחת bArrKey_and_ciphertext[cite: 2].
/**
* Frida Script for UnCrackable Level 1
* Objective: Bypassing security checks and extracting the secret key.
*/
Java.perform(function() {
// Bypass Root Detection Logic
var root_detection = Java.use("sg.vantagepoint.a.c");
['a', 'b', 'c'].forEach(function(method) {
root_detection[method].implementation = function() {
return false;
};
});
// Bypass Debug Detection
var debug_detection = Java.use("sg.vantagepoint.a.b");
debug_detection.a.implementation = function(Context) {
return false;
};
// The "Spy" Hook - Intercepting the Decryption process
var key_and_cypertext = Java.use("sg.vantagepoint.a.a");
key_and_cypertext.a.implementation = function(key, ciphertext) {
var plaintextBytes = this.a(key, ciphertext);
try {
var javaString = Java.use("java.lang.String");
var plaintext = javaString.$new(plaintextBytes);
console.log("Secret extracted: " + plaintext);
} catch (err) { console.error("Error in Spy Hook: " + err.message); }
return plaintextBytes;
};
});