cp : https://segmentfault.com/a/1190000005128037
Strings are inevitable in the development process, but these strings may also be the key points of cracking, such as server addresses and error prompts for these sensitive string information. If these strings are hardcoded, they can be easily obtained by static analysis. A previous blog used the prompt string to reverse the analysis of Android programs as a breakthrough point
Normal way to define strings
Define a string in Java:
private String normalString(){ String str = "Hello world"; return str; }
Decompiled .smali code
.method private normalString()Ljava/lang/String; .registers 2 .prologue .line 16 const-string v0, "Hello world" .line 17 .local v0, "str":Ljava/lang/String; return-object v0 .end method
It can be seen that const-string
the keyword is followed by the defined string value, and it can even be extracted in batches using automated analysis tools.
solution
1. StringBuilder splicing
The StringBuilder class constructs the required string through the append method. This method can increase the difficulty of automatic analysis. If you want to obtain a complete string, you must perform corresponding lexical parsing.
String concatenation code in Java:
private String buildString(){ StringBuilder builder = new StringBuilder(); builder.append("Hello"); builder.append(" "); builder.append("world"); return builder.toString(); }
Decompiled .smali code:
.method private buildString()Ljava/lang/String; .registers 3 .prologue .line 21 new-instance v0, Ljava/lang/StringBuilder; invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V .line 22 .local v0, "builder":Ljava/lang/StringBuilder; const-string v1, "Hello" invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 23 const-string v1, " " invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 24 const-string v1, "world" invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 25 invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; move-result-object v1 return-object v1 .end method
It can be seen that the decompiled .smali code adds a certain degree of difficulty to cracking, and cannot be recognized at a glance.
2. Coding Obfuscation
Encoding confusion is to convert the string into a hexadecimal array or Unicode encoding when hard-coding, and convert it back to a string when using it. This method is more difficult to directly identify when decompiled into .smali code than StringBuilder method.
Java code:
private String encodeString(){ byte[] strBytes = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}; String str = new String(strBytes); return str; }
Decompile the .smali code:
.method private encodeString()Ljava/lang/String; .registers 4 .prologue .line 29 const/16 v2, 0xb new-array v1, v2, [B fill-array-data v1, :array_e .line 30 .local v1, "strBytes":[B new-instance v0, Ljava/lang/String; invoke-direct {v0, v1}, Ljava/lang/String;-><init>([B)V .line 31 .local v0, "str":Ljava/lang/String; return-object v0 .line 29 nop :array_e .array-data 1 0x48t 0x65t 0x6ct 0x6ct 0x6ft 0x20t 0x77t 0x6ft 0x72t 0x6ct 0x64t .end array-data .end method
It can be seen in the .smali code that all characters have been hidden.
3. Encryption processing
Encryption processing is to encrypt the string locally, then hardcode the ciphertext into it, and decrypt it at runtime.
Encryption steps:
-
String encryption
-
hard-coded into the program
-
compile and run
-
decrypt ciphertext
Of course, because Java code is relatively easy to decompile, and this method requires the decryption method to be placed locally in the APK, we can implement the decryption method through JNI to increase the difficulty of decompilation.
Summarize
Any kind of reinforcement method only increases the difficulty of anti-cracking, and cannot completely prevent Android programs from being cracked.