محلل Java Bytecode قابل للتخصيص عبر قواعد JSON. إنها أداة سطر الأوامر التي تتلقى مسارًا يحتوي على واحد أو أكثر من ملفات جرة أو حرب ، وتحليلها باستخدام القواعد المقدمة وإنشاء تقارير HTML مع النتائج.
usage: java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE
-a,--analyze <pathToAnalyze> Path of the directory to run the
analysis.
-c,--checks <checks...> Space separated list of custom checks
that are going to be run in the analysis.
-f,--custom-file <customFile> Specify a file in JSON format to run
custom rules. Read more in
https://github.com/fergarrui/custom-bytecode-analyzer.
-h,--help Print this message.
-i,--items-report <maxItems> Max number of items per report. If the
number of issues found exceeds this
value, the report will be split into
different files. Useful if expecting too
many issues in the report. Default: 50.
-o,--output <outputDir> Directory to save the report. Warning -
if there are already saved reports in
this directory they will be overwritten.
Default is "report".
-v,--verbose-debug Increase verbosity to debug mode.
-vv,--verbose-trace Increase verbosity to trace mode - makes it slower, use it only when you need.
يمكن تحديد ملف القواعد باستخدام وسيطة -f,--custom-file . الملف بتنسيق JSON ولديه الهيكل التالي:
final يمكنك أيضًا التحقق من net.nandgr.cba.custom.model.Rules.java لرؤية الهيكل في كود Java.
هناك بالفعل العديد من القواعد بموجب أمثلة الدليل. على أي حال ، فيما يلي أمثلة مدرجة لكل قاعدة.
إذا كنا بحاجة إلى العثور على فصول مع تفضيل مخصص ، فيمكننا القيام بذلك بسهولة تامة. يحدد الفئة إلغاء التخلص المخصص عن طريق تطبيق private void readObject(ObjectInputStream in) . لذلك نحن بحاجة فقط إلى العثور على جميع الفئات حيث يتم تعريف هذه الطريقة. سيكون كافيا فقط لتحديد قاعدة على النحو التالي:
{
"rules" : [{
"name" : " Custom deserialization " ,
"methods" : [{
"name" : " readObject " ,
"visibility" : " private " ,
"parameters" : [{
"type" : " java.io.ObjectInputStream "
}]
}]
}]
} وسوف الإبلاغ عن طرق برؤية private ، readObject كاسم ومعلمة من النوع java.io.ObjectOutputStream . المعلمات عبارة عن صفيف ، إذا تم تحديد أكثر من واحد ، يجب أن تتطابق جميعها. نظرًا لأن لدينا قاعدة واحدة فقط ، سيتم إنشاء تقرير يسمى: custom-deserialization-0.html.
في هذه الحالة ، يجب تحديد قاعدة واحدة مع طريقتين. نفس الشيء في المثال السابق لإلغاء التسلسل ، وواحد جديد لمطابقة private void writeObject(ObjectOutputStream out) . كما هو موضح في بنية JSON أعلاه ، قواعد الممتلكات.
{
"rules" : [{
"name" : " Custom serialization and deserialization " ,
"methods" : [{
"name" : " readObject " ,
"visibility" : " private " ,
"parameters" : [{
"type" : " java.io.ObjectInputStream "
}]
},{
"name" : " writeObject " ,
"report" : " false " ,
"visibility" : " private " ,
"parameters" : [{
"type" : " java.io.ObjectOutputStream "
}]
}]
}]
} تم تعيين report الملكية على خطأ لتجنب الإبلاغ مرتين لنفس القاعدة. نحن نستخدم الطريقة الثانية كشرط ، لكن الإبلاغ عن طرق readObject فقط يجب أن تكون كافية لغرض هذه القاعدة.
إذا لم يتم تعريف خاصية ، فسيتطابق مع ذلك دائمًا. على سبيل المثال ، ستعيد هذه القاعدة جميع تعريفات الأساليب:
{
"rules" : [{
"name" : " Method definitions " ,
"methods" : [{
}]
}]
}يمكن العثور على استدعاءات الطريقة. سيكون JSON في هذه الحالة:
{
"rules" : [{
"name" : " String equals " ,
"invocations" : [{
"owner" : " java.lang.String " ,
"method" : {
"name" : " equals "
}
}]
}]
} يحدد owner العقار الفئة التي تحتوي على الطريقة.
مثال آخر على طريقة الاحتجاج أكثر فائدة قليلاً من السابق:
{
"rules" : [{
"name" : " Method invocation by reflection " ,
"invocations" : [{
"owner" : " java.lang.reflect.Method " ,
"method" : {
"name" : " invoke "
}
}]
}]
} إنه نفسه من أي طريقة استدعاء ، ولكن يجب أن يكون اسم الطريقة في هذه الحالة <init> .
{
"rules" : [{
"name" : " String instantiation " ,
"invocations" : [{
"owner" : " java.lang.String " ,
"method" : {
"name" : " <init> "
}
}]
}]
}ستجد هذه القاعدة حوادث:
[...]
String s = new String ( "foo" );
[...] في هذا المثال ، نريد أن نجد استخدامات إزالة التهوية (وليس الفصول التي تحدد سلوكيات التسلسل كما في الأمثلة السابقة). يحدث إزالة التأهيل عند استدعاء ObjectInputStream.readObject() . على سبيل المثال في مقتطف الرمز هذا:
ObjectInputStream in = new ObjectInputStream ( fileInputStream );
Object o = in . readObject (); لذلك نحن بحاجة إلى العثور على دعوات الأسلوب من ObjectInputStream المسماة readObject . لكنه سيجد الكثير من الإيجابيات الخاطئة في سياق البحث ، لأنه عندما يحدد الفئة الهفضات المخصصة ، فإنها تقوم بإجراء استدعاء لهذه الطريقة داخل طريقة private void readObject(ObjectInputStream in) ، وهذا من شأنه تلوث التقرير أكثر من اللازم. إذا أردنا استبعاد هذه الحالات والاحتفاظ بفرقة حقيقية فقط ، فيمكن استخدام خاصية notFrom :
{
"rules" : [{
"name" : " Deserialization usage " ,
"invocations" : [{
"owner" : " java.io.ObjectInputStream " ,
"method" : {
"name" : " readObject "
},
"notFrom" : {
"name" : " readObject " ,
"visibility" : " private "
}
}]
}]
} سيجد هذا الملف java.io.ObjectInputStream.readObject() Invocations إذا لم يتم الاحتجاج داخل طريقة private void readObject(ObjectInputStream in) .
لن يتم الإبلاغ عن فئة تم تجميعها باستخدام هذا الرمز:
private void readObject ( ObjectInputStream in ) throws IOException , ClassNotFoundException {
Object o = in . readObject ();
}ولكن سيتم الإبلاغ عن هذا:
public Object deserializeObject ( ObjectInputStream in ) throws IOException , ClassNotFoundException {
Object o = in . readObject ();
return o ;
} يمكن تعيين خاصية from دعوات بنفس الطريقة بالضبط من notFrom ، ولكن النتيجة ستكون عكس ذلك: ستتطابق فقط إذا تم إجراء الاحتجاج من الطريقة المحددة.
يمكن استخدام الطبقة superClass في هذه الحالة. إذا أردنا العثور على جميع الفئات التي تمتد javax.servlet.http.HttpServlet ، يمكن أن تكون القاعدة:
{
"rules" : [{
"name" : " Java servlets " ,
"superClass" : " javax.servlet.http.HttpServlet "
}]
}
يمكن كتابة قاعدة للعثور على فئات تنفذ مجموعة من الواجهات. إذا تم تعريف أكثر من واجهة واحدة في القاعدة ، فيجب على الفصل تنفيذها جميعًا ليتم الإبلاغ عنها. إذا أردنا العثور على فصول تنفذ javax.net.ssl.X509TrustManager ، ستكون القاعدة هي:
{
"rules" : [{
"name" : " X509TrustManager implementations " ,
"interfaces" : [ " javax.net.ssl.X509TrustManager " ]
}]
} يرجى ملاحظة أن interfaces عبارة عن صفيف ، لذا تأكد من إضافة الأوتار بين الأقواس المربعة ، على سبيل المثال: ["interface1", "interface2", ...] .
كما تدعم التعليقات التوضيحية. يمكن تعريف خصائص التعليقات التوضيحية المتعددة في قاعدة (العثور على تعليقات التعليقات التوضيحية) ، في الأساليب O متغيرات (المعلمات أو المتغيرات المحلية). إذا تم العثور على جميعهم في الفصل الذي تم تحليله ، فسيتم الإبلاغ عنه. على سبيل المثال ، إذا كنا نريد العثور على نقاط نهاية الربيع ، فسنبحث عن فصول أو أساليب مشروح مع org.springframework.web.bind.annotation.RequestMapping . لذلك ، يمكن أن تكون القاعدة:
{
"rules" : [{
"name" : " Spring endpoint - class annotation " ,
"annotations" : [{
"type" : " org.springframework.web.bind.annotation.RequestMapping "
}]
},
{
"name" : " Spring endpoint - method annotation " ,
"methods" : [{
"annotations" : [{
"type" : " org.springframework.web.bind.annotation.RequestMapping "
}]
}]
}]
} يمكن استخدام rule.fields الملكية. الحقول للعثور على حقول الفصل. إذا أردنا العثور على حقول سلسلة خاصة بأسماء كلمات المرور ، فيمكن استخدام قاعدة مثل هذه:
{
"rules" : [{
"name" : " Password fields " ,
"fields" : [
{
"visibility" : " private " ,
"type" : " java.lang.String " ,
"nameRegex" : " (password|pass|psswd|passwd) "
}
]
}]
} للعثور على المتغيرات ، يمكن استخدام rule.variables . ستقوم هذه الخاصية بالإبلاغ عن المتغيرات المحلية وطريقة وسيطات المتغيرات. إذا أردنا العثور على جميع متغيرات النوع javax.servlet.http.Part ، يمكن أن تكون القاعدة:
{
"rules" : [{
"name" : " Servlet upload file " ,
"methods" : [{
"variables" : [{
"type" : " javax.servlet.http.Part "
}]
}]
}]
}يمكن تحديد قواعد متعددة في نفس ملف JSON. سيتم معالجتها والإبلاغ عنها بشكل منفصل ولن تؤثر على بعضها البعض. يمكننا الجمع بين بعض قواعد الأمثلة السابقة:
{
"rules" : [{
"name" : " Custom deserialization " ,
"methods" : [{
"name" : " readObject " ,
"visibility" : " private " ,
"parameters" : [{
"type" : " java.io.ObjectInputStream "
}]
}]
},{
"name" : " Method invocation by reflection " ,
"invocations" : [{
"owner" : " java.lang.reflect.Method " ,
"method" : {
"name" : " invoke "
}
}]
}]
}هنا ، لدينا قاعدتان ("إلغاء التخلص المخصص" و "استدعاء الطريقة عن طريق الانعكاس"). سيتم معالجتها كما لو كنت تفعل ذلك في اثنين من عمليات الإعدام المنفصلة. وسيتم إنشاء تقرير لكل قاعدة. إذا كان للقواعد نفس الاسم ، فسيتم الإبلاغ عنها في نفس الملف.
يمكن تنزيل المشروع وصممه لإضافة قواعد مخصصة أكثر تعقيدًا في كود Java والتي لا يتم تغطيتها بتنسيق JSON. هناك بالفعل ثلاثة أمثلة ضمن حزمة net.nandgr.cba.visitor.checks . هذه هي CustomDeserializationCheck, DeserializationCheck and InvokeMethodCheck . يمكنك إنشاء قواعدك الخاصة عن طريق توسيع net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor .
كما ذكر أعلاه ، يتم إنشاء التقارير افتراضيًا بموجب مجلد report . سيكون لكل قاعدة ملف منفصل ما لم يكن لديهم نفس الاسم. إذا كان التقرير كبيرًا جدًا ، فيمكنك تقسيمه باستخدام المعلمة -i,--items-report <maxItems> ، سيحتفظ كل منهم بالسيجة المحددة أو أقل (إذا كانت الأخيرة). كل عنصر تم الإبلاغ عنه ، يحدد الجرة التي توجد فيها ، اسم الفصل واسم الطريقة (إذا كان ذا صلة). كما يوضح النسخة التي تم حلها من الفصل لتخفيف فحص مرئي سريع. مثال على كيفية عرض العناصر لقاعدة للعثور على java.io.File instantiations:

عند البحث عن أخطاء الأمان ، من المفيد جدًا الحصول على رسم بياني للاتصال. في الوقت الحالي ، يتم إنشاء ملف متوافق مع DOT بسيط ضمن دليل report . يحتوي الرسم البياني على جميع التدفقات المحتملة حيث يمكن استدعاء المشكلات التي تم العثور عليها. على سبيل المثال ، إذا تم استخدام قاعدة لإيجاد التسلسل ، فسيتم إنشاء رسم بياني يحتوي على جميع المسارات الممكنة التي تؤدي إلى الطريقة التي تستدعي تفضيل.
الملف هو call-graph.dot وسيبدو هكذا (هذا مثال بسيط للغاية):
graph callGraph {
"demo.callgraph.Class1:method1" -- "demo.callgraph.Class2:method2"
"demo.callgraph.Class3:method3" -- "demo.callgraph.Class2:method2"
}
لعرضه بطريقة مرئية ، يمكن استخدام DOT (أو أي برنامج متوافق). على سبيل المثال ، لتحويل الملف إلى svg :
dot -Tsvg call-graph.dot -o call-graph.svg
يتم ذلك تلقائيًا بشكل افتراضي إذا تم العثور على DOT في مسار النظام. إذا لم يكن الأمر كذلك ، يمكن تثبيت DOT في الأنظمة المستندة إلى Debian باستخدام sudo apt-get install graphviz .
سيقوم بإنشاء ملف SVG يسمى call-graph.svg يمكن تحويله إلى PNG أو تصوره باستخدام برامج مثل inkscape أو Just firefox .
مثال بسيط للغاية على ملف call-graph.dot أعلاه ، سيكون:

هناك بعض القيود ، على سبيل المثال ، إذا كان العنصر الذي تم تفتيشه في java.lang.Runnable.run() أو طريقة مماثلة ، فلن يجد من أين يتم تنفيذ مؤشر الترابط. أيضًا ، الرسم البياني هو دورات التنظيف لتجنب StackOverflowError s ، وهو مصنوع بطريقة محافظة حتى لا يتم تصريف ذاكرة النظام أثناء تحليل دليل كبير.
سيتم إضافة المزيد من الخيارات في الإصدارات المستقبلية.
java -jar cba-cli-<version>.jar -a /path/with/jars -f /path/with/json/file/rules.json
لاستخدام قواعد Java المخصصة ، يجب تحديد أسماء الفصول كوسائط لـ -c .
java -jar cba-cli-<version>.jar -a /path/with/jars -c DeserializationCheck
يقبل قائمة منفصلة عن المساحة ، لذلك يمكن تعريف قواعد مخصصة متعددة (كل قاعدة من القواعد ستنشئ تقريرًا منفصلًا):
java -jar cba-cli-<version>.jar -a /path/with/jars -c DeserializationCheck InvokeMethodCheck CustomDeserializationCheck YourCustomRule
java -jar cba-cli-<version>.jar -a /path/with/jars -f /path/with/json/file/rules.json -c YourCustomRule1 YourCustomRule2
للعثور على الأخطاء ، يمكن زيادة الفعل. مستوى التصحيح:
java -jar cba-cli-<version>.jar -a /path/with/jars -c YourCustomRule1 -v
مستوى التتبع:
java -jar cba-cli-<version>.jar -a /path/with/jars -c YourCustomRule1 -vv
في الوقت الحالي ، يجب تحويل APK إلى جرة أولاً ليتم تحليلها.
d2j-dex2jar.sh -f -o app_to_analyze.jar app_to_analyze.apk-a الدليل الذي يحتوي على ملف JAR المحول. يوجد بالفعل ملف جرة قابلة للتنفيذ ضمن دليل bin على: https://github.com/fergarrui/Custom-bytecode-analyzer/blob/master/bin/cba-cli-0.1-snapshot.jar. إذا كنت ترغب في إجراء تعديلات أو إضافة قواعد مخصصة ، فيمكن تصميم المشروع:
git clone https://github.com/fergarrui/custom-bytecode-analyzer.git
cd custom-bytecode-analyzer
mvn clean package
سيتم إنشاء اثنين من الجرار تحت المجلد target . يحتوي cba-cli-<version>.jar على جميع التبعيات وقابلة للتنفيذ. يمكن تشغيله باستخدام java -jar cba-cli-<version>.jar