Simplify는 앱의 동작을 이해하기 위해 가상으로 앱을 실행한 다음 동일하게 동작하지만 사람이 더 쉽게 이해할 수 있도록 코드를 최적화하려고 시도합니다. 각 최적화 유형은 간단하고 일반적이므로 어떤 특정 난독화 유형이 사용되는지는 중요하지 않습니다.
왼쪽 코드는 난독화된 앱을 역컴파일한 것이고, 오른쪽 코드는 난독해제된 코드입니다.
프로젝트에는 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
빌드하려면 JDK(Java Development Kit 8)가 설치되어 있어야 합니다.
이 프로젝트에는 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를 확인하세요.
단순화가 실패하면 다음 권장 사항을 순서대로 시도해 보십시오.
-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 등)를 포함해 주세요 .
작업이 문자열, 숫자 또는 부울과 같은 상수로 변환될 수 있는 유형의 값을 배치하는 경우 이 최적화는 해당 작업을 상수로 대체합니다. 예를 들어:
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()
에 대한 호출을 반영되지 않은 실제 메서드 호출로 대체할 수 있습니다. 반영된 필드 및 클래스 조회에도 동일하게 적용됩니다.
특정 카테고리에 딱 들어맞지 않는 모든 것에는 엿보기 최적화가 있습니다. 여기에는 쓸모 없는 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 파일에서 확인할 수 있습니다.