أداة قائمة على Frida لتتبع استخدام JNI API في تطبيقات Android.
غالبًا ما تستخدم المكتبات الأصلية الموجودة في تطبيقات Android واجهة برمجة تطبيقات JNI لاستخدام وقت تشغيل Android. يمكن أن يكون تتبع تلك المكالمات من خلال الهندسة العكسية اليدوية عملية بطيئة ومؤلمة. يعمل jnitrace كأداة تتبع تحليل ديناميكية مماثلة لـ Frida-Trace أو Strace ولكن لـ JNI.
أسهل طريقة للجري مع jnitrace هي التثبيت باستخدام PIP:
pip install jnitrace
بعد تثبيت PIP ، من السهل تشغيل jnitrace :
jnitrace -l libnative-lib.so com.example.myapplication
يتطلب jnitrace ما لا يقل عن معلمتين لتشغيل أثر:
-l libnative-lib.so -lib.so -يستخدم لتحديد المكتبات لتتبع. يمكن استخدام هذه الوسيطة عدة مرات أو * يمكن استخدامها لتتبع جميع المكتبات. على سبيل المثال ، -l libnative-lib.so -l libanother-lib.so أو -l * .com.example.myapplication - هي حزمة Android لتتبع. يجب تثبيت هذه الحزمة بالفعل على الجهاز.الوسيطات الاختيارية مدرجة أدناه:
-R <host>:<port> - يستخدم لتحديد موقع الشبكة لخادم Frida عن بُعد. إذا كان A: غير محدد ، يتم استخدام المضيف المحلي: 27042 بواسطة Deault.-m <spawn|attach> - يستخدم لتحديد آلية إرفاق FRIDA للاستخدام. يمكن إما تفرخ أو إرفاق. تفرخ هو الخيار الافتراضي والموصى به.-b <fuzzy|accurate|none> - يستخدم للتحكم في إخراج الخلفية. بشكل افتراضي ، ستقوم jnitrace بتشغيل Backtracer في الوضع accurate . يمكن تغيير هذا الخيار إلى وضع fuzzy أو استخدامه لإيقاف Backtrace باستخدام خيار none . انظر مستندات Frida للحصول على شرح حول الاختلافات.-i <regex> - يستخدم لتحديد أسماء الأسلوب التي يجب تتبعها. يمكن أن يكون هذا مفيدًا لتقليل الضوضاء في تطبيقات JNI الكبيرة بشكل خاص. يمكن توفير الخيار عدة مرات. على سبيل المثال ، -i Get -i RegisterNatives ستشمل فقط أساليب JNI التي تحتوي على GET أو التسجيلات باسمهم.-e <regex> - يستخدم لتحديد أسماء الأسلوب التي يجب تجاهلها في التتبع. يمكن أن يكون هذا مفيدًا لتقليل الضوضاء في تطبيقات JNI الكبيرة بشكل خاص. يمكن توفير الخيار عدة مرات. على سبيل المثال ، -e ^Find -e GetEnv سوف يستبعد من النتائج جميع أسماء طريقة JNI التي تبدأ في العثور أو تحتوي على getenv.-I <string> - يستخدم لتحديد الصادرات من مكتبة يجب تتبعها. هذا مفيد للمكتبات حيث تريد فقط تتبع عدد صغير من الطرق. الوظائف التي تعتبرها Jnitrace تصديرها هي أي وظائف قابلة للاتصال مباشرة من جانب Java ، على هذا النحو ، والتي تتضمن طرقًا ملزمة باستخدام سجلات التسجيلات. يمكن توفير الخيار عدة مرات. على سبيل المثال ، يمكن استخدام -I stringFromJNI -I nativeMethod([B)V لتضمين تصدير من المكتبة تسمى Java_com_nativetest_MainActivity_stringFromJNI وطريقة ملزمة باستخدام أسماء التسجيل مع توقيع nativeMethod([B)V .-E <string> يستخدم لتحديد الصادرات من مكتبة لا ينبغي تتبعها. هذا مفيد للمكتبات حيث لديك مجموعة من المكالمات الأصلية المزدحمة التي تريد تجاهلها. الوظائف التي تعتبرها Jnitrace تصديرها هي أي وظائف قابلة للاتصال مباشرة من جانب Java ، على هذا النحو ، والتي تتضمن طرقًا ملزمة باستخدام سجلات التسجيلات. يمكن توفير الخيار عدة مرات. على سبيل المثال ، سوف يستبعد -E JNI_OnLoad -E nativeMethod من تتبع استدعاء وظيفة JNI_OnLoad وأي طرق تحمل اسم nativeMethod .-o path/output.json - يتم استخدامه لتحديد مسار الإخراج حيث سيقوم jnitrace بتخزين جميع البيانات تتبع. يتم تخزين المعلومات بتنسيق JSON للسماح لاحقًا بعد المعالجة لبيانات التتبع.-p path/to/script.js - يتم استخدام المسار المقدم لتحميل برنامج نصي frida في العملية الهدف قبل تحميل البرنامج النصي jnitrace . يمكن استخدام هذا لهزيمة رمز مكافحة الإغراء أو مكافحة الأداء قبل بدء jnitrace .-a path/to/script.js - يتم استخدام المسار المقدم لتحميل البرنامج النصي frida في العملية المستهدفة بعد تحميل jnitrace .--hide-data تستخدم لتقليل كمية الإخراج المعروضة في وحدة التحكم. سيخفي هذا الخيار بيانات إضافية يتم عرضها على أنها hexdumps أو كإلغاء عمليات إلحاق السلسلة.--ignore-env سيؤدي استخدام هذا الخيار إلى إخفاء جميع المكالمات التي يقوم بها التطبيق باستخدام jnienv struth.--ignore-vm سيؤدي استخدام هذا الخيار إلى إخفاء جميع المكالمات التي يقوم بها التطبيق باستخدام javavm struct.--aux <name=(string|bool|int)value> -المستخدمة لتمرير المعلمات المخصصة عند التفريخ تطبيق. على سبيل المثال --aux='uid=(int)10' سوف يولد التطبيق للمستخدم 10 بدلاً من المستخدم الافتراضي 0.ملحوظة
تذكر أن Frida-Server يجب أن يعمل قبل تشغيل jnitrace . إذا تم اتباع الإرشادات الافتراضية لتثبيت Frida ، فسيبدأ الأمر التالي خادمًا جاهزًا لـ jnitrace :
adb shell /data/local/tmp/frida-server
المحرك الذي يعمل على تشغيل Jnitrace متاح كمشروع منفصل. يتيح لك هذا المشروع استيراد Jnitrace لتتبع مكالمات API الفردية JNI ، بطريقة مألوفة في استخدام Interceptor Frida لتوصيل الوظائف والعناوين.
import { JNIInterceptor } from "jnitrace-engine" ;
JNIInterceptor . attach ( "FindClass" , {
onEnter ( args ) {
console . log ( "FindClass method called" ) ;
this . className = Memory . readCString ( args [ 1 ] ) ;
} ,
onLeave ( retval ) {
console . log ( "tLoading Class:" , this . className ) ;
console . log ( "tClass ID:" , retval . get ( ) ) ;
}
} ) ;مزيد من المعلومات: https://github.com/Chame1eon/Jnitrace-Engine
يتطلب بناء jnitrace من المصدر تثبيت node أولاً. بعد تثبيت node ، يجب تشغيل الأوامر التالية:
npm installnpm run watch سيتم frida-compile npm run watch في الخلفية لتجميع المصدر إلى ملف الإخراج ، build/jnitrace.js . jnitrace.py يحمل من build/jnitrace.js افتراضيا ، لذلك لا يلزم تغييرات أخرى لتشغيل التحديثات.
مثل Frida-Trace ، يتم تلوين الإخراج بناءً على مؤشر ترابط استدعاء API.
مباشرة أسفل معرف مؤشر الترابط في الشاشة هو اسم طريقة JNI API. تتطابق أسماء الطريقة مع تلك التي شوهدت في ملف jni.h
تحتوي الخطوط اللاحقة على قائمة بالوسائط المشار إليها بواسطة A |- . بعد الشخصيات |- هي نوع الوسيطة تليها قيمة الوسيطة. بالنسبة إلى JMethods و JFields و JClasses ، سيتم عرض نوع Java في أقواس مجعد. يعتمد هذا على jnitrace بعد أن رأى الطريقة الأصلية أو الحقل أو البحث عن الفصل. بالنسبة لأي طرق تمرير المخازن المؤقتة ، ستقوم jnitrace باستخراج المخازن المؤقتة من الوسيطات وعرضها على أنها عبارة عن hexdump أسفل قيمة الوسيطة.
يتم عرض قيم الإرجاع في أسفل القائمة كـ |= ولن تكون موجودة لطرق الفراغ.
إذا تم تمكين Backtrace ، فسيتم عرض Frida Backtrace أسفل استدعاء الطريقة. يرجى أن تكون على علم ، وفقًا لمستندات Frida ، فإن Backtrace الغامض ليس دقيقًا دائمًا وقد يوفر Backtrace الدقيق نتائج محدودة.
كان الهدف من هذا المشروع هو إنشاء أداة يمكن أن تتبع مكالمات JNI API بكفاءة لمعظم تطبيقات Android.
لسوء الحظ ، فإن أبسط نهج في الارتباط بجميع مؤشرات الوظائف في بنية JniNV يؤدي إلى زيادة تحميل التطبيق. إنه يسبب تصادمًا بناءً على العدد الهائل من مكالمات الوظائف التي تقوم بها المكتبات الأخرى غير ذات الصلة باستخدام نفس الوظائف في libart.so .
للتعامل مع حاجز الأداء هذا ، يقوم jnitrace بإنشاء ظل Jnienv يمكن أن يوفره للمكتبات التي تريد تتبعها. يحتوي هذا JniNV على سلسلة من الترامبولين للوظائف التي ترتد مكالمات JNI API من خلال بعض Frida NativeCallbacks المخصصة لتتبع مدخلات وإخراج تلك الوظائف.
تقوم FRIDA API العامة بعمل رائع في توفير منصة لبناء تلك الترامبينات ذات الوظائف بأقل جهد. ومع ذلك ، فإن هذا النهج البسيط لا يعمل لجميع jnienv API. المشكلة الرئيسية في تتبع جميع الأساليب هي استخدام وسيطات variadic في API. لا يمكن إنشاء NativeCallback لهذه الوظائف في وقت مبكر ، لأنه غير معروف مسبقًا جميع المجموعات المختلفة من أساليب Java التي سيتم استدعاؤها.
يتمثل الحل في مراقبة عملية المكالمات إلى GetMethodID أو GetStaticMethodID ، وتستخدم للبحث عن معرفات الطريقة من وقت التشغيل. بمجرد أن يرى jnitrace عملية بحث jmethodID ، فإنه يحتوي على رسم خرائط معرف للمعرف إلى توقيع الطريقة. في وقت لاحق ، عندما يتم إجراء استدعاء طريقة Jni Java ، يتم استخدام NativeCallback الأولي لاستخراج معرف الطريقة في المكالمة. ثم يتم تحليل توقيع الطريقة لاستخراج وسيطات الطريقة. بمجرد أن يستخرج jnitrace الوسائط في الطريقة ، يمكنه إنشاء NativeCallback ديناميكيًا لهذه الطريقة. يتم إرجاع أن NativeCallback الجديد ويتعامل القليل من رمز shellcode الخاص بعمارة مع إنشاء المكدس والسجلات للسماح لهذه المكالمة بالتشغيل بنجاح. يتم تخزين المؤتمر الوطنية NativeCallbacks لطرق محددة للسماح لاستدعاء رد الاتصال بشكل أكثر كفاءة إذا كانت هناك طريقة إذا تم استدعاؤها عدة مرات.
المكان الآخر الذي لا يكفيه NativeCallback البسيط لاستخراج الوسائط من مكالمة الطريقة ، هو للمكالمات باستخدام مؤشر VA_ARGS كوسيطة نهائية. في هذه الحالة ، يستخدم jnitrace بعض التعليمات البرمجية لاستخراج الوسائط من المؤشر المقدم. مرة أخرى هذا هو الهندسة المعمارية.
يتم إرسال جميع البيانات التي يتم تتبعها في هذه المكالمات الوظائف إلى تطبيق وحدة التحكم في Python التي تتنسيق وتعرضها للمستخدم.
تم إجراء معظم اختبارات هذه الأداة على محاكي Android X86_64 الذي يعمل Marshmallow. أي مشكلات من العوامل التي يتم تشغيلها على جهاز آخر ، يرجى تقديم مشكلة ، ولكن أيضًا ، إذا أمكن ، يوصى بمحاولة التشغيل على محاكي مماثل.
لأي مشكلات من ذوي الخبرة في تشغيل jnitrace ، يرجى إنشاء مشكلة على Github. يرجى تضمين المعلومات التالية في القضية المودعة: