ลดความซับซ้อนของการดำเนินการแอปเสมือนจริงเพื่อทำความเข้าใจพฤติกรรมของมัน จากนั้นพยายามปรับโค้ดให้เหมาะสมเพื่อให้มีพฤติกรรมเหมือนกัน แต่มนุษย์จะเข้าใจได้ง่ายขึ้น การเพิ่มประสิทธิภาพแต่ละประเภทเป็นแบบเรียบง่ายและทั่วไป ดังนั้นจึงไม่สำคัญว่าจะใช้การสร้างความสับสนประเภทใดโดยเฉพาะ
โค้ดด้านซ้ายเป็นการถอดรหัสแอปที่สร้างความสับสน และโค้ดทางด้านขวาได้รับการถอดรหัสที่สร้างความสับสนแล้ว
โปรเจ็กต์มีสามส่วน ได้แก่ smalivm ลดความซับซ้อน และแอปสาธิต
if
หรือ switch
เงื่อนไขใดๆ ด้วยค่าที่ไม่รู้จัก ส่งผลให้ทั้งสองสาขาถูกรับไป usage: java -jar simplify.jar <input> [options]
deobfuscates a dalvik executable
-et,--exclude-types <pattern> Exclude classes and methods which include REGEX, eg: "com/android", applied after include-types
-h,--help Display this message
-ie,--ignore-errors Ignore errors while executing and optimizing methods. This may lead to unexpected behavior.
--include-support Attempt to execute and optimize classes in Android support library packages, default: false
-it,--include-types <pattern> Limit execution to classes and methods which include REGEX, eg: ";->targetMethod("
--max-address-visits <N> Give up executing a method after visiting the same address N times, limits loops, default: 10000
--max-call-depth <N> Do not call methods after reaching a call depth of N, limits recursion and long method chains, default: 50
--max-execution-time <N> Give up executing a method after N seconds, default: 300
--max-method-visits <N> Give up executing a method after executing N instructions in that method, default: 1000000
--max-passes <N> Do not run optimizers on a method more than N times, default: 100
-o,--output <file> Output simplified input to FILE
--output-api-level <LEVEL> Set output DEX API compatibility to LEVEL, default: 15
-q,--quiet Be quiet
--remove-weak Remove code even if there are weak side effects, default: true
-v,--verbose <LEVEL> Set verbosity to LEVEL, default: 0
อาคารจำเป็นต้องติดตั้ง Java Development Kit 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
เพื่อทำความเข้าใจว่าอะไรกำลังถูกถอดรหัสซอร์สโค้ดที่สร้างความสับสน โปรดดู README ของแอป Obfuscated
หากลดความซับซ้อนล้มเหลว ให้ลองทำตามคำแนะนำเหล่านี้ตามลำดับ:
-it
--max-address-visits
, --max-call-depth
และ --max-method-visits
ให้สูงขึ้น-v
หรือ -v 2
แล้วรายงานปัญหาเกี่ยวกับบันทึกและแฮชของ DEX หรือ APKหากสร้างบน Windows และการสร้างล้มเหลวโดยมีข้อผิดพลาดคล้ายกับ:
ไม่พบ tools.jar โปรดตรวจสอบว่า C:Program FilesJavajre1.8.0_151 มีการติดตั้ง JDK ที่ถูกต้อง
ซึ่งหมายความว่า Gradle ไม่สามารถค้นหาเส้นทาง JDK ที่เหมาะสมได้ ตรวจสอบให้แน่ใจว่าติดตั้ง JDK แล้ว ตั้งค่าตัวแปรสภาพแวดล้อม JAVA_HOME
เป็นพาธ JDK ของคุณ และตรวจสอบให้แน่ใจว่าได้ปิดและเปิดพรอมต์คำสั่งที่คุณใช้ในการสร้างอีกครั้ง
อย่าอาย. ฉันคิดว่าการดำเนินการเสมือนจริงและการลดความยุ่งเหยิงเป็นปัญหาที่น่าสนใจ ใครก็ตามที่สนใจจะเจ๋งโดยอัตโนมัติและยินดีสนับสนุน แม้ว่าจะเป็นเพียงการแก้ไขการพิมพ์ผิดก็ตาม อย่าลังเลที่จะถามคำถามในประเด็นต่างๆ และส่งคำขอดึง
โปรดใส่ลิงก์ไปยัง 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
เนื่องจากสตริงเป็นแบบ "คงที่" ดังนั้น 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 ไม่จำเป็นต้องฉลาด มันแค่ดำเนินการทุกอย่างอย่างโง่เขลาและถือว่ามีผลข้างเคียงหากไม่แน่ใจ ลองพิจารณาตัวอย่างจาก Constant Propagation:
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."
ฮัซซาห์!
ความท้าทายสำคัญประการหนึ่งกับการวิเคราะห์แบบคงที่ของ Java คือการสะท้อนกลับ เป็นไปไม่ได้ที่จะรู้ว่าข้อโต้แย้งมีไว้สำหรับวิธีการสะท้อนกลับโดยไม่ทำการวิเคราะห์กระแสข้อมูลอย่างระมัดระวัง มีวิธีที่ชาญฉลาดและชาญฉลาดในการทำเช่นนี้ แต่ 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()
ด้วยการเรียกใช้เมธอดที่ไม่สะท้อนจริงได้ เช่นเดียวกับการค้นหาฟิลด์ที่สะท้อนและคลาส
สำหรับทุกสิ่งที่ไม่เข้ากับหมวดหมู่ใดหมวดหมู่หนึ่งอย่างหมดจด เรามีการเพิ่มประสิทธิภาพช่องมอง ซึ่งรวมถึงการลบ ops check-cast
ที่ไร้ประโยชน์ออก แทนที่การเรียก Ljava/lang/String;-><init>
ด้วย const-string
และอื่นๆ
.method public static test1 () I
.locals 2
new-instance v0 , L java/lang/Integer ;
const/4 v1 , 0x1
invoke-direct { v0 , v1 }, L java/lang/Integer ; -> <init> ( I ) V
invoke-virtual { v0 }, L java/lang/Integer ; -> intValue () I
move-result v0
return v0
.end method
ทั้งหมดนี้ทำคือ v0 = 1
.method public static test1 () I
.locals 2
new-instance v0 , L java/lang/Integer ;
const/4 v1 , 0x1
invoke-direct { v0 , v1 }, L java/lang/Integer ; -> <init> ( I ) V
invoke-virtual { v0 }, L java/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-string
const-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