Root Detection เป็นกลไกความปลอดภัยที่ป้องกันแอปพลิเคชัน Android จากการทำงานบนอุปกรณ์ที่มีสิทธิ์ผู้ใช้ระดับสูง (superuser) แอปพลิเคชันธนาคาร การชำระเงิน และองค์กรจะบล็อกหรือจำกัดฟังก์ชันการทำงานบนอุปกรณ์ที่รูทแล้ว เนื่องจากการเข้าถึง root จะลบข้อจำกัดของแซนด์บ็อกซ์ Android และเปิดโอกาสให้ดักจับทราฟฟิก อ่านหน่วยความจำของกระบวนการ และปลอมแปลงข้อมูล ตาม OWASP Mobile Top 10 (2024) การไม่มี Root Detection จัดอยู่ในหมวดหมู่ M8 (Security Decisions via Untrusted Inputs) Root Detection สร้างขึ้นจากการรวมกันของการตรวจสอบระบบไฟล์แบบสถิตและการวิเคราะห์พฤติกรรมรันไทม์แบบไดนามิก
ประเด็นสำคัญ
Root Detection เป็นกลไกซอฟต์แวร์ที่ตรวจจับการมีอยู่ของการเข้าถึง root บนอุปกรณ์ Android การเข้าถึง root ให้การควบคุมระบบปฏิบัติการอย่างสมบูรณ์ ทำให้แอปพลิเคชันและสคริปต์สามารถเรียกใช้คำสั่งด้วย UID 0 บนอุปกรณ์ที่รูทแล้ว การแยกแอปพลิเคชัน (Android Sandbox) จะสูญเสียไป ทำให้สามารถดักจับการป้อนข้อมูลจากแป้นพิมพ์ อ่านฐานข้อมูล SQLite ของแอปอื่น แทรกโค้ดเข้าไปในกระบวนการ และแทนที่ใบรับรอง SSL ในที่เก็บที่เชื่อถือได้
สำหรับแอปพลิเคชันทางการเงินและองค์กร การทำงานบนอุปกรณ์ที่รูทแล้วก่อให้เกิดความเสี่ยงที่ยอมรับไม่ได้ ผู้โจมตีสามารถเข้าถึงโทเค็น คีย์เซสชัน และข้อมูลส่วนบุคคล หน่วยงานกำกับดูแล รวมถึง PCI Security Standards Council กำหนดให้แอปพลิเคชันการชำระเงินต้องตรวจจับและตอบสนองต่อการเข้าถึง root ในการตอบสนอง นักพัฒนา Android จึงฝัง Root Detection เป็นส่วนหนึ่งของกลยุทธ์การป้องกันเชิงรุก
มีสองแนวทางในการตรวจจับ: แบบสถิต ซึ่งวิเคราะห์ระบบไฟล์และแพ็คเกจที่ติดตั้ง และแบบไดนามิก ซึ่งทำการตรวจสอบในรันไทม์ แนวทางแบบผสมผสานถือว่าเชื่อถือได้มากที่สุด เนื่องจากครอบคลุมเวกเตอร์การเลี่ยงที่หลากหลาย จากการศึกษาของ NowSecure (2025) พบว่า 76% ของแอปธนาคารใน 100 อันดับแรกของ Google Play มี Root Detection ในรูปแบบใดรูปแบบหนึ่ง
วิธีการแบบสถิตจะทำงานเมื่อเริ่มต้นแอปพลิเคชันและตรวจสอบสัญญาณของการเข้าถึง root ที่เครื่องมือรูททิ้งไว้ในระบบไฟล์ วิธีการเหล่านี้ไม่ต้องการการเรียกใช้คำสั่งที่มีสิทธิพิเศษและทำงานในบริบทของแอปพลิเคชันปกติ
ตัวบ่งชี้หลักของการเข้าถึง root คือการมีอยู่ของไฟล์ปฏิบัติการ su ในเส้นทางมาตรฐาน: /system/bin/su, /system/xbin/su, /sbin/su, /su/bin/su แอปพลิเคชันตรวจสอบการมีอยู่ของไฟล์ผ่าน File.exists() หรือการ implement แบบเนทีฟของ access() จาก libc นอกจากนี้ สามารถลองเรียกใช้ su --version หรือ su -c id และตรวจสอบรหัสออก
แอปพลิเคชันทั่วไปสำหรับจัดการการเข้าถึง root: Superuser, SuperSU, Magisk Manager, KingRoot การมีอยู่ของแอปเหล่านี้ตรวจสอบผ่าน PackageManager.getPackageInfo() หรือการอ่านไดเรกทอรี /data/app/ แพ็คเกจที่ต้องตรวจสอบ: com.topjohnwu.magisk, eu.chainfire.supersu, com.noshufou.android.su, com.thirdparty.superuser, com.koushikdutta.superuser, com.zacharee1.systemuituner
Android เก็บข้อมูลสถานะระบบในคุณสมบัติของระบบ ซึ่งเข้าถึงได้ผ่าน System.getProperty และ Build.TAGS หาก Build.TAGS มี test-keys แทน release-keys แสดงว่าเป็นเฟิร์มแวร์ที่กำหนดเอง ซึ่งมักมีการเข้าถึง root นอกจากนี้ ยังตรวจสอบ ro.build.tags, ro.debuggable และ ro.secure โดยการอ่าน /system/build.prop
public class RootDetectionChecker {
private static final String[] SU_PATHS = {
"/system/bin/su",
"/system/xbin/su",
"/sbin/su",
"/su/bin/su",
"/system/sd/xbin/su"
};
public boolean checkRootByFiles() {
for (String path : SU_PATHS) {
if (new File(path).exists()) {
return true;
}
}
return false;
}
public boolean checkRootByPackages(Context ctx) {
String[] packages = {
"com.topjohnwu.magisk",
"eu.chainfire.supersu",
"com.noshufou.android.su",
"com.koushikdutta.superuser"
};
for (String pkg : packages) {
try {
ctx.getPackageManager().getPackageInfo(pkg, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
// package not found
}
}
return false;
}
}
วิธีการแบบไดนามิกจะทำงานระหว่างการทำงานของแอปพลิเคชันและวิเคราะห์สภาพแวดล้อมการทำงาน แตกต่างจากวิธีการแบบสถิต วิธีการเหล่านี้สามารถตรวจจับการรูทที่ซ่อนผ่าน Magisk Hide หรือ Zygisk ได้ เนื่องจากตรวจสอบพฤติกรรมของระบบ ไม่ใช่เพียงโครงสร้างไฟล์
เมื่อมีการเข้าถึง root พาร์ติชันระบบบางส่วนจะถูกเมานต์ด้วยแฟล็ก rw (อ่าน-เขียน) แทน ro (อ่านอย่างเดียว) แอปพลิเคชันอ่าน /proc/mounts และตรวจสอบว่า /system ถูกเมานต์เป็น ro หาก /system ถูกเมานต์เป็น rw แสดงว่าระบบถูกแก้ไข นอกจากนี้ ยังตรวจสอบการมีอยู่ของการเมานต์ /su ผ่าน Magisk
เซฟโหมดของ Android ปิดการใช้งานแอปพลิเคชันของบุคคลที่สาม รวมถึงตัวจัดการ root การ implement Root Detection ที่ถูกต้องสามารถตรวจสอบว่าอุปกรณ์กำลังทำงานในเซฟโหมดหรือไม่ หากแอปพลิเคชันตรวจพบว่าตัวจัดการ root ไม่ปรากฏ แต่ไบนารี su มีอยู่ นี่คือ ตัวบ่งชี้ของ Magisk Hide
การพยายามเรียกใช้ su -c id ผ่าน ProcessBuilder หรือ Runtime.exec เป็นการทดสอบการเข้าถึง root โดยตรง อย่างไรก็ตาม Magisk สามารถสกัดกั้นการเรียกนี้ได้ วิธีการที่เชื่อถือได้มากขึ้นคือการตรวจสอบผ่านโค้ดเนทีฟ: เปิด /proc/1/limits หรือ /proc/self/maps และวิเคราะห์ UID ของกระบวนการที่ทำงานอยู่ หากแอปพลิเคชันสามารถได้รับ UID 0 หรืออ่านไฟล์ที่เฉพาะ root เท่านั้นที่เข้าถึงได้ แสดงว่าอุปกรณ์ถูกบุกรุก
public boolean checkRootDynamically() {
// Build flags check
String buildTags = Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
return true;
}
// Checking /system mount
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("/proc/mounts"))
);
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("/system")
&& line.contains("rw")) {
reader.close();
return true;
}
}
reader.close();
} catch (IOException e) {
// error reading mounts
}
return false;
}
Root Detection ที่ implement ด้วย Java สามารถเลี่ยงได้ง่ายผ่านโมดูล Xposed หรือ Frida ซึ่งสกัดกั้นเมธอด Java และแทนที่ค่าที่ส่งคืน การ implement แบบเนทีฟด้วย C++ ผ่าน JNI มีความต้านทานมากกว่าอย่างมีนัยสำคัญ: เครื่องมือวิเคราะห์แบบไดนามิกที่ทำงานในระดับ Java ไม่สามารถมองเห็นการเรียก libc แบบเนทีฟ เช่น stat, access, popen และ dlopen
#include <unistd.h>
#include <sys/stat.h>
#include <cstring>
#include <vector>
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_checker_RootCheck_nativeCheck(
JNIEnv* env, jobject instance) {
std::vector<const char*> paths = {
"/system/bin/su",
"/system/xbin/su",
"/sbin/su",
"/data/local/su"
};
struct stat st;
for (const char* path : paths) {
if (stat(path, &st) == 0) {
return JNI_TRUE;
}
}
return JNI_FALSE;
}
การตรวจสอบแบบเนทีฟไม่ใช้ Java API ทำให้มองไม่เห็นสำหรับเครื่องมือเลี่ยงที่ทำงานในระดับ Dalvik/ART สำหรับการป้องกันเพิ่มเติม แนะนำให้เก็บค่าคงที่ (รายการเส้นทาง) ไม่ใช่ในส่วนอ่านอย่างเดียว แต่คำนวณผ่านฟังก์ชันที่ย้อนกลับได้อย่างง่าย การเรียก stat จาก libc เข้าถึงเคอร์เนล Linux โดยตรง เลี่ยง wrapper ของ Java และไม่สามารถสกัดกั้นผ่าน Xposed ได้
นักพัฒนาการป้องกันจำเป็นต้องเข้าใจวิธีการเลี่ยงที่มีอยู่เพื่อสร้างระบบตรวจจับที่แข็งแกร่ง แต่ละวิธีการเลี่ยงต้องมีมาตรการตอบโต้ในระดับที่เหมาะสม
Magisk เป็นเครื่องมือรูทยอดนิยมที่สุดบน Android 9–14 Magisk Hide ซ่อนการมีอยู่ของ su จาก /proc และปลอมแปลงผลลัพธ์การตรวจสอบเส้นทาง Magisk ทำงานในระดับเคอร์เนลและสกัดกั้น stat() และ access() ก่อนที่แอปพลิเคชันจะเห็น มาตรการตอบโต้: ตรวจสอบการมีอยู่ของ Magisk เองผ่านการมีอยู่ของ /sbin/.magisk หรือตรวจสอบผ่านการอ่าน maps ของแอปพลิเคชันเอง — Magisk แทรกไลบรารีของมันเข้าไปในทุกกระบวนการ
Frida เป็นเครื่องมือวัดแบบไดนามิกที่สามารถสกัดกั้นฟังก์ชันเนทีฟผ่าน Ptrace หรือ Dobby Frida แทนที่ค่าที่ส่งคืนของการตรวจสอบใดๆ ปลอมแปลงผลลัพธ์ stat เป็น ENOENT มาตรการตอบโต้: ตรวจสอบความสมบูรณ์ของฟังก์ชันเนทีฟโดยการคำนวณ checksum ของคำสั่งในหน่วยความจำและตรวจจับ Frida ผ่านการวิเคราะห์ /proc/self/maps เพื่อหาการมีอยู่ของ frida-agent.so หรือ frida-helper
Root Detection ที่ implement ด้วย Java สามารถลบออกได้ใน 2–3 นาที: APK ถูกดีคอมไพล์ผ่าน apktool ค่าที่ส่งคืนของเมธอดถูกเปลี่ยนเป็น false ในโค้ด smali APK ถูกสร้างใหม่และเซ็นชื่อ มาตรการตอบโต้: ย้ายตรรกะสำคัญไปยังโค้ดเนทีฟและตรวจสอบลายเซ็นดิจิทัลของแอปพลิเคชันในรันไทม์ผ่าน Signature API หรือเปรียบเทียบแฮช APK กับข้อมูลอ้างอิงบนเซิร์ฟเวอร์
Root Detection ที่มีประสิทธิภาพสร้างขึ้นบนสถาปัตยกรรมหลายชั้น ไม่มีวิธีการใดวิธีการเดียวที่ให้การป้องกันที่เพียงพอ การรวมกันของการตรวจสอบแบบสถิตและไดนามิก โค้ดเนทีฟ และการตรวจสอบฝั่งเซิร์ฟเวอร์ให้ความต้านทานสูงสุด
อย่าพึ่งพาเฉพาะการตรวจสอบฝั่งไคลเอ็นต์ ส่งผลลัพธ์ Root Detection ไปยังเซิร์ฟเวอร์พร้อมกับโทเค็นเซสชันแบบใช้ครั้งเดียว เซิร์ฟเวอร์ตัดสินใจว่าจะบล็อกหรือจำกัดฟังก์ชันการทำงาน ซึ่งป้องกันการโจมตีในระดับ API ซึ่งแอปพลิเคชันไคลเอ็นต์อาจถูกแก้ไขในขณะที่เซิร์ฟเวอร์ยังคงเป็นฝ่ายที่เชื่อถือได้
โค้ด Root Detection ต้องถูกทำให้สับสน หากผู้โจมตีเห็นลำดับที่ชัดเจนของการตรวจสอบเส้นทาง su ใน jadx การเลี่ยงจะใช้เวลาเป็นนาที ใช้ ProGuard หรือ DexGuard เพื่อทำให้โฟลว์การควบคุมสับสนและเข้ารหัสสตริง การทำให้สับสน เพิ่มเวลาในการวิเคราะห์โค้ดป้องกันจากนาทีเป็นชั่วโมง
รายการเส้นทาง แพ็คเกจ และตัวบ่งชี้ที่ตรวจสอบต้องได้รับการอัปเดตทุกครั้งที่ออกแอปพลิเคชันเวอร์ชันใหม่ เครื่องมือรูทและการเลี่ยงใหม่ๆ ปรากฏขึ้นทุกเดือน รายการแบบสถิตที่ไม่เปลี่ยนแปลงเป็นเวลาหนึ่งปีจะไม่สามารถตรวจจับวิธีการสมัยใหม่ได้ แนะนำให้โหลดลายเซ็นปัจจุบันจากเซิร์ฟเวอร์เมื่อเริ่มต้นแอปพลิเคชันก่อนทำการตรวจสอบ
คำถามที่พบบ่อย
Root Detection ป้องกันการทำงานของแอปพลิเคชันบนอุปกรณ์ที่แซนด์บ็อกซ์ Android ถูกปิดใช้งาน บนอุปกรณ์ที่รูทแล้ว แอปพลิเคชันใดๆ ก็สามารถอ่านข้อมูลของแอปอื่นได้ แอปพลิเคชันธนาคารและชำระเงินมีหน้าที่ต้องบล็อกการทำงานบนอุปกรณ์ที่รูทตามข้อกำหนด PCI DSS และคำแนะนำ OWASP Mobile Security
Magisk Hide ใช้กลไกเนมสเปซการเมานต์ (mount namespace) สำหรับแต่ละกระบวนการในรายการยกเว้น Magisk จะสร้างเนมสเปซที่แยกออกมาซึ่งไบนารี su จะมองไม่เห็น การเรียกระบบ stat, access และ open ในเนมสเปซนี้จะไม่เห็นไฟล์ Magisk สามารถตรวจจับ Magisk ได้โดยตรวจสอบการมีอยู่ของ /proc/self/maps และค้นหาดัมพ์ magisk
ได้ หากแอปพลิเคชันไม่ตรวจสอบความสมบูรณ์ของโค้ดของมัน ผ่าน Frida สามารถสกัดกั้นเมธอดการตรวจสอบ Java และบังคับให้ส่งคืน false มาตรการตอบโต้คือการ implement ตรรกะสำคัญแบบเนทีฟด้วย C++ และตรวจสอบความสมบูรณ์ผ่านแฮชไฟล์ DEX หากไม่มีการทำให้สับสน Root Detection ใดๆ บน Java จะถูกเลี่ยงภายใน 5–10 นาที
SafetyNet (เลิกใช้แล้ว) และ Play Integrity API เป็นการตรวจสอบฝั่งเซิร์ฟเวอร์จาก Google ที่ยืนยันความสมบูรณ์ของอุปกรณ์ ซึ่งรวมถึงการตรวจสอบ bootloader ลายเซ็นระบบ และสถานะ root Play Integrity API เป็นตัวแทนที่แนะนำสำหรับ SafetyNet ซึ่งมีสามระดับ: BASIC, DEVICE และ STRONG Root Detection ฝั่งไคลเอ็นต์เสริมการรับรองฝั่งเซิร์ฟเวอร์
ติดตั้งแอปพลิเคชันบนอุปกรณ์ที่รูทจริง (เช่น Pixel ที่มี Magisk) ตรวจสอบว่าการบล็อกทำงานหรือไม่ จากนั้นลองซ่อน root ผ่าน Magisk Hide สำหรับแอปพลิเคชันของคุณและเริ่มการทดสอบใหม่ สำหรับการทดสอบเชิงลึก ให้ใช้ Frida เพื่อสกัดกั้นเมธอดเป้าหมายและตรวจสอบว่าการป้องกันแบบเนทีฟไม่สามารถเลี่ยงได้
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม