
एंड्रॉइड वर्चुअल मशीन और डीओबफसकेटर
Simplify वस्तुतः एक ऐप को निष्पादित करके उसके व्यवहार को समझता है और फिर कोड को अनुकूलित करने का प्रयास करता है ताकि वह समान व्यवहार करे लेकिन मनुष्य के लिए समझना आसान हो। प्रत्येक अनुकूलन प्रकार सरल और सामान्य है, इसलिए इससे कोई फर्क नहीं पड़ता कि किस विशेष प्रकार का अस्पष्टीकरण (obfuscation) उपयोग किया गया है।
बाईं ओर का कोड एक अस्पष्टीकृत ऐप का डीकंपाइलेशन है, और दाईं ओर का कोड डिओबफस्केट किया गया है।
परियोजना के तीन भाग हैं: smalivm, simplify, और डेमो ऐप।
if या switch सशर्त स्थिति दोनों शाखाओं को लेने का कारण बनती है।usage: java -jar simplify.jar <input> [options]
deobfuscates a dalvik executable
-et,--exclude-types <pattern> उन कक्षाओं और विधियों को बाहर करें जिनमें REGEX शामिल है, जैसे: "com/android", include-types के बाद लागू होता है
-h,--help यह संदेश प्रदर्शित करें
-ie,--ignore-errors विधियों को निष्पादित और अनुकूलित करते समय त्रुटियों को अनदेखा करें। इससे अप्रत्याशित व्यवहार हो सकता है।
--include-support Android समर्थन लाइब्रेरी पैकेजों में कक्षाओं को निष्पादित और अनुकूलित करने का प्रयास करें, डिफ़ॉल्ट: false
-it,--include-types <pattern> निष्पादन को उन कक्षाओं और विधियों तक सीमित करें जिनमें REGEX शामिल है, जैसे: ";->targetMethod\("
--max-address-visits <N> किसी विधि को निष्पादित करना छोड़ दें जब उसी पते पर N बार जाया जाए, लूप को सीमित करता है, डिफ़ॉल्ट: 10000
--max-call-depth <N> N की कॉल गहराई तक पहुँचने के बाद विधियों को कॉल न करें, पुनरावृत्ति और लंबी विधि श्रृंखलाओं को सीमित करता है, डिफ़ॉल्ट: 50
--max-execution-time <N> किसी विधि को N सेकंड के बाद निष्पादित करना छोड़ दें, डिफ़ॉल्ट: 300
--max-method-visits <N> किसी विधि में N निर्देश निष्पादित करने के बाद उसे निष्पादित करना छोड़ दें, डिफ़ॉल्ट: 1000000
--max-passes <N> किसी विधि पर अनुकूलक को N बार से अधिक न चलाएं, डिफ़ॉल्ट: 100
-o,--output <file> सरलीकृत आउटपुट को FILE में लिखें
--output-api-level <LEVEL> आउटपुट DEX API संगतता को LEVEL पर सेट करें, डिफ़ॉल्ट: 15
-q,--quiet शांत रहें
--remove-weak कमजोर दुष्प्रभाव होने पर भी कोड हटाएं, डिफ़ॉल्ट: true
-v,--verbose <LEVEL> वाचालता को LEVEL पर सेट करें, डिफ़ॉल्ट: 0
निर्माण के लिए जावा डेवलपमेंट किट 8 (JDK) स्थापित होना आवश्यक है।
क्योंकि इस परियोजना में Android फ्रेमवर्क के लिए सबमॉड्यूल शामिल हैं, --recursive के साथ क्लोन करें:
git clone --recursive https://github.com/CalebFenton/simplify.git
या किसी भी समय सबमॉड्यूल को अपडेट करें:
git submodule update --init --recursive
फिर, सभी निर्भरताओं वाला एक एकल jar बनाने के लिए:
./gradlew fatjar
Simplify jar simplify/build/libs/ में होगा। आप प्रदान किए गए अस्पष्टीकृत उदाहरण ऐप को सरल बनाकर इसके काम करने की जांच कर सकते हैं। इसे चलाने का तरीका यहां दिया गया है (आपको simplify.jar बदलने की आवश्यकता हो सकती है):
java -jar simplify/build/libs/simplify.jar -it "org/cf/obfuscated" -et "MainActivity" simplify/obfuscated-app.apk
यह समझने के लिए कि क्या डिओबफस्केट किया जा रहा है, Obfuscated App का README देखें।
यदि Simplify विफल हो जाता है, तो क्रम से इन सुझावों को आज़माएं:
-it विकल्प का उपयोग करें।--max-address-visits, --max-call-depth, और --max-method-visits का उपयोग करने का प्रयास करें।-v या -v 2 के साथ प्रयास करें और लॉग और DEX या APK के हैश के साथ समस्या की रिपोर्ट करें।यदि Windows पर निर्माण कर रहे हैं और निर्माण इस तरह की त्रुटि के साथ विफल होता है:
Could not find tools.jar. Please check that C:\Program Files\Java\jre1.8.0_151 contains a valid JDK installation.
इसका अर्थ है कि Gradle उचित JDK पथ खोजने में असमर्थ है। सुनिश्चित करें कि JDK स्थापित है, JAVA_HOME पर्यावरण चर को अपने JDK पथ पर सेट करें, और निर्माण के लिए उपयोग किए जाने वाले कमांड प्रॉम्प्ट को बंद करके फिर से खोलें।
संकोच न करें। मुझे वर्चुअल निष्पादन और डिओबफस्केशन आकर्षक समस्याएँ लगती हैं। जो भी रुचि रखता है वह स्वतः ही अच्छा है और योगदान का स्वागत है, भले ही वह केवल एक टाइपो सुधारने के लिए हो। प्रश्न पूछने के लिए issues में और पुल रिक्वेस्ट सबमिट करने में संकोच न करें।
कृपया APK या DEX का लिंक और आपके द्वारा उपयोग किया जा रहा पूर्ण कमांड शामिल करें। इससे आपकी समस्या को पुन: उत्पन्न करना (और इस प्रकार ठीक करना) बहुत आसान हो जाता है।
यदि आप नमूना साझा नहीं कर सकते, तो कृपया फ़ाइल हैश (SHA1, SHA256, आदि) शामिल करें।
यदि कोई op किसी प्रकार का मान रखता है जिसे स्थिरांक में बदला जा सकता है जैसे स्ट्रिंग, संख्या या बूलियन, तो यह अनुकूलन उस op को स्थिरांक से बदल देगा। उदाहरण के लिए:
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
# Decrypts to: "Tell me of your homeworld, Usul."
move-result v0
इस उदाहरण में, एक एन्क्रिप्टेड स्ट्रिंग को डिक्रिप्ट किया जाता है और v0 में रखा जाता है। चूंकि स्ट्रिंग "constantizable" हैं, move-result v0 को const-string से बदला जा सकता है:
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
const-string v0, "Tell me of your homeworld, Usul."
कोड मृत है यदि उसे हटाने से ऐप के व्यवहार में कोई बदलाव नहीं आ सकता। सबसे स्पष्ट मामला तब है जब कोड अप्राप्य है, जैसे if (false) { // dead })। यदि कोड प्राप्य है, तो उसे मृत माना जा सकता है यदि वह विधि के बाहर किसी भी स्थिति को प्रभावित नहीं करता, यानी उसका कोई दुष्प्रभाव नहीं है। उदाहरण के लिए, कोड विधि के लिए वापसी मान को प्रभावित नहीं कर सकता, किसी भी क्लास चर को नहीं बदल सकता, या कोई IO नहीं कर सकता। स्थैतिक विश्लेषण में यह निर्धारित करना कठिन है। सौभाग्य से, smalivm को स्मार्ट होने की आवश्यकता नहीं है। यह बस मूर्खतापूर्ण ढंग से वह सब कुछ निष्पादित करता है जो वह कर सकता है और यदि वह सुनिश्चित नहीं है तो दुष्प्रभाव मानता है। स्थिर प्रसार के उदाहरण पर विचार करें:
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
invoke-static {v0}, Lmy/string/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
const-string v0, "Tell me of your homeworld, Usul."
इस कोड में, invoke-static अब विधि के वापसी मान को प्रभावित नहीं करता और मान लें कि यह कुछ अजीब नहीं करता जैसे फ़ाइल सिस्टम या नेटवर्क सॉकेट पर बाइट्स लिखना, इसलिए इसका कोई दुष्प्रभाव नहीं है। इसे आसानी से हटाया जा सकता है।
const-string v0, "VGVsbCBtZSBvZiB5b3VyIGhvbWV3b3JsZCwgVXN1bC4="
const-string v0, "Tell me of your homeworld, Usul."
अंत में, पहला const-string एक रजिस्टर को एक मान निर्दिष्ट करता है, लेकिन वह मान कभी उपयोग नहीं किया जाता, यानी असाइनमेंट मृत है। इसे भी हटाया जा सकता है।
const-string v0, "Tell me of your homeworld, Usul."
वाह!
जावा के स्थैतिक विश्लेषण में एक प्रमुख चुनौती रिफ्लेक्शन है। यह जानना संभव नहीं है कि रिफ्लेक्शन विधियों के लिए तर्क क्या हैं, बिना सावधान डेटा प्रवाह विश्लेषण किए। ऐसा करने के स्मार्ट, चतुर तरीके हैं, लेकिन smalivm इसे केवल कोड निष्पादित करके करता है। जब यह एक परावर्तित विधि आह्वान पाता है जैसे:
invoke-virtual {v0, v1, v2}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
यह v0, v1, और v2 के मान जान सकता है। यदि यह सुनिश्चित है कि मान क्या हैं, तो यह Method.invoke() के कॉल को एक वास्तविक गैर-परावर्तित विधि आह्वान से बदल सकता है। यही परावर्तित फ़ील्ड और क्लास लुकअप पर भी लागू होता है।
जो कुछ भी किसी विशेष श्रेणी में साफ-साफ फिट नहीं होता, उसके लिए पीपहोल अनुकूलन हैं। इसमें बेकार check-cast ops को हटाना, Ljava/lang/String;-><init> कॉल को const-string से बदलना, और इसी तरह के अन्य कार्य शामिल हैं।
.method public static test1()I
.locals 2
new-instance v0, Ljava/lang/Integer;
const/4 v1, 0x1
invoke-direct {v0, v1}, Ljava/lang/Integer;-><init>(I)V
invoke-virtual {v0}, Ljava/lang/Integer;->intValue()I
move-result v0
return v0
.end method
यह सब बस v0 = 1 करता है।
.method public static test1()I
.locals 2
new-instance v0, Ljava/lang/Integer;
const/4 v1, 0x1
invoke-direct {v0, v1}, Ljava/lang/Integer;-><init>(I)V
invoke-virtual {v0}, Ljava/lang/Integer;->intValue()I
const/4 v0, 0x1
return v0
.end method
move-result v0 को const/4 v0, 0x1 से बदल दिया गया है। ऐसा इसलिए क्योंकि intValue()I के लिए केवल एक संभावित वापसी मान है और वापसी प्रकार को स्थिरांक बनाया जा सकता है। तर्क v0 और v1 असंदिग्ध हैं और बदलते नहीं हैं। यानी, intValue()I पर प्रत्येक संभावित निष्पादन पथ के लिए मानों की सर्वसम्मति है। अन्य प्रकार के मान जिन्हें स्थिरांक में बदला जा सकता है:
const/4, const/16, आदि।const-stringconst-class.method public static test1()I
.locals 2
const/4 v0, 0x1
return v0
.end method
क्योंकि उपरोक्त const/4 v0, 0x1 का कोड विधि के बाहर की स्थिति को प्रभावित नहीं करता (कोई दुष्प्रभाव नहीं), इसे व्यवहार बदले बिना हटाया जा सकता है। यदि कोई मेथड कॉल होता जिसने फ़ाइल सिस्टम या नेटवर्क पर कुछ लिखा होता, तो इसे नहीं हटाया जा सकता क्योंकि यह विधि के बाहर की स्थिति को प्रभावित करता है। या यदि test()I एक परिवर्तनीय तर्क लेता, जैसे LinkedList, तो उस तक पहुँचने वाले किसी भी निर्देश को मृत नहीं माना जा सकता।
मृत कोड के अन्य उदाहरण:
if (false) { dead_code(); }यह उपकरण एक दोहरे लाइसेंस के तहत उपलब्ध है: एक वाणिज्यिक लाइसेंस जो बंद स्रोत परियोजनाओं के लिए उपयुक्त है, और एक GPL लाइसेंस जिसका उपयोग ओपन सोर्स सॉफ्टवेयर में किया जा सकता है।
आपकी आवश्यकताओं के आधार पर, आपको उनमें से एक को चुनना होगा और उसकी नीतियों का पालन करना होगा। प्रत्येक लाइसेंस प्रकार के लिए नीतियों और समझौतों का विवरण LICENSE.COMMERCIAL और LICENSE.GPL फ़ाइलों में उपलब्ध है।