
JSON नियमों के माध्यम से अनुकूलन योग्य Java बाइटकोड विश्लेषक
Java बाइटकोड विश्लेषक जो JSON नियमों के माध्यम से अनुकूलन योग्य है। यह एक कमांड-लाइन उपकरण है जो एक या अधिक Jar या War फ़ाइलों वाले पथ को प्राप्त करता है, प्रदान किए गए नियमों का उपयोग करके उनका विश्लेषण करता है और परिणामों के साथ 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 होआप Java कोड में संरचना देखने के लिए net.nandgr.cba.custom.model.Rules.java भी देख सकते हैं।
निर्देशिका examples में पहले से कई नियम मौजूद हैं। वैसे भी, नीचे प्रत्येक नियम के लिए उदाहरण सूचीबद्ध हैं।
यदि हमें कस्टम डिसीरियलाइज़ेशन वाली कक्षाओं को खोजने की आवश्यकता है, तो हम इसे काफी आसानी से कर सकते हैं। एक क्लास 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.rule.methods विधियों की एक सरणी है, इसलिए इस तरह एक नियम लिखा जा सकता है:
{
"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 को false पर सेट किया गया था ताकि एक ही नियम के लिए दो बार रिपोर्ट करने से बचा जा सके। हम दूसरी विधि का उपयोग केवल एक शर्त के रूप में कर रहे हैं, लेकिन केवल readObject विधियों की रिपोर्ट करना इस नियम के उद्देश्य के लिए पर्याप्त होना चाहिए।
यदि कोई गुण परिभाषित नहीं है, तो यह हमेशा true के रूप में मेल खाएगा। उदाहरण के लिए, यह नियम सभी विधि परिभाषाएँ लौटाएगा:
{
"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() आह्वान पाएगी यदि आह्वान 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", ...].
एनोटेशन भी समर्थित हैं। एक नियम में कई एनोटेशन गुण परिभाषित किए जा सकते हैं (क्लास एनोटेशन ढूँढना), विधियों या चर (पैरामीटर या स्थानीय चर) में। यदि विश्लेषित क्लास में ये सभी पाए जाते हैं, तो इसकी रिपोर्ट की जाएगी।
उदाहरण के लिए, यदि हम Spring एंडपॉइंट ढूँढना चाहते हैं, तो हम 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"
}
}]
}]
}
यहाँ, हमारे पास दो नियम हैं ("Custom deserialization" और "Method invocation by reflection")। उन्हें संसाधित किया जाएगा जैसे कि आप उन्हें दो अलग-अलग निष्पादनों में करते हैं। और प्रति नियम एक रिपोर्ट उत्पन्न की जाएगी। यदि नियमों का एक ही नाम है, तो वे एक ही फ़ाइल में रिपोर्ट किए जाएंगे।
प्रोजेक्ट को डाउनलोड और बनाया जा सकता है ताकि Java कोड में अधिक जटिल कस्टम नियम जोड़े जा सकें जो JSON प्रारूप द्वारा कवर नहीं किए गए हैं। पैकेज net.nandgr.cba.visitor.checks के अंतर्गत पहले से तीन उदाहरण हैं। वे हैं CustomDeserializationCheck, DeserializationCheck और InvokeMethodCheck। आप net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor का विस्तार करके अपने स्वयं के नियम बना सकते हैं।
जैसा कि ऊपर उल्लेख किया गया है, रिपोर्ट डिफ़ॉल्ट रूप से report फ़ोल्डर के अंतर्गत बनाई जाती हैं। प्रत्येक नियम की एक अलग फ़ाइल होगी जब तक कि उनका एक ही नाम न हो।
यदि रिपोर्ट बहुत बड़ी है, तो आप इसे -i,--items-report <maxItems> पैरामीटर का उपयोग करके विभाजित कर सकते हैं, प्रत्येक में निर्दिष्ट तर्क या उससे कम होगा (यदि यह अंतिम है)।
प्रत्येक रिपोर्ट की गई वस्तु, उस जार को निर्दिष्ट करती है जहाँ वह पाई गई है, वर्ग का नाम और विधि का नाम (यदि प्रासंगिक है)। यह त्वरित दृश्य जांच की सुविधा के लिए वर्ग का डीकंपाइल्ड संस्करण भी दिखाता है।
एक नियम के लिए java.io.File इंस्टेंशिएशन खोजने के लिए आइटम कैसे दिखाए जाते हैं इसका उदाहरण:

सुरक्षा बग की खोज करते समय कॉल ग्राफ़ होना बहुत उपयोगी होता है। फिलहाल, report निर्देशिका के अंतर्गत एक सरल DOT संगत फ़ाइल बनाई जाती है।
ग्राफ़ में वे सभी संभावित प्रवाह शामिल हैं जहाँ से पाए गए मुद्दों को आमंत्रित किया जा सकता है। उदाहरण के लिए, यदि डिसीरियलाइज़ेशन खोजने के लिए एक नियम का उपयोग किया जाता है,
तो डिसीरियलाइज़ेशन को कॉल करने वाली विधि की ओर ले जाने वाले सभी संभावित पथों वाला एक ग्राफ़ उत्पन्न किया जाएगा।
फ़ाइल 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 सिस्टम PATH में पाया जाता है। यदि नहीं, तो DOT को Debian आधारित सिस्टम में sudo apt-get install graphviz का उपयोग करके स्थापित किया जा सकता है।
यह एक SVG फ़ाइल बनाएगा जिसका नाम call-graph.svg है, जिसे inkscape या बस firefox जैसे प्रोग्राम का उपयोग करके PNG में बदला या देखा जा सकता है।
उपरोक्त फ़ाइल call-graph.dot का एक बहुत ही सरल उदाहरण होगा:

कुछ सीमाएँ हैं, उदाहरण के लिए, यदि खोजा गया आइटम java.lang.Runnable.run() या समान विधि में है, तो यह नहीं पाएगा कि थ्रेड कहाँ से निष्पादित किया गया है।
साथ ही, ग्राफ़ StackOverflowError से बचने के लिए चक्रों को साफ कर रहा है, यह थोड़ा रूढ़िवादी तरीके से बनाया गया है ताकि बड़ी निर्देशिका के विश्लेषण के दौरान सिस्टम की मेमोरी समाप्त न हो।
भविष्य के संस्करणों में और विकल्प जोड़े जाएंगे।
java -jar cba-cli-<version>.jar -a /path/with/jars -f /path/with/json/file/rules.json
कस्टम जावा नियमों का उपयोग करने के लिए, क्लास नामों को -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 को पहले JAR में बदलना होगा।
d2j-dex2jar.sh -f -o app_to_analyze.jar app_to_analyze.apk-a पैरामीटर के रूप में परिवर्तित जार फ़ाइल वाली निर्देशिका पास करें।पहले से ही bin निर्देशिका में एक निष्पादन योग्य jar फ़ाइल मौजूद है: 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 फ़ोल्डर के अंतर्गत दो jars उत्पन्न होंगे। cba-cli-<version>.jar में सभी निर्भरताएँ हैं और यह निष्पादन योग्य है। इसे java -jar cba-cli-<version>.jar का उपयोग करके चलाया जा सकता है।