Encryption Analysis of a Car Network App Communication Protocol (Part 1)
1. Objectives
Boss: I just bought a new car recently. The app that came with it is quite interesting. Would you like to take a look at it?
Me: Oh my god, it’s packed, it’s quite interesting, let me try it.
v6.1.0
2. Steps
Packet capture
My packet capture environment is Mac 10.14.6 + httpToolKit, and I captured the packet smoothly in this step.
1:main
As you can see, both the http request and the return value are encrypted. This is our goal.requestandresponseThe origin of .
Shelling
Our first choice for shelling isBlackDex, easy to use and effective. However, it does not work well for this sample, only one dex is extracted, and it is likely to have failed.
Then go to the gourd baby bossFRIDA-DEXDump, This sample is very cunning. It crashes as soon as the frida spawn mode is run. It seems that the shell is relatively hard.
This is a tricky situation. Recall that we used Xcube to deal with packed applications before. Let’s try it again this time.
Xcube did not disappoint us and the injection was successful. But how to run FRIDA-DEXDump in Xcube mode?
I wanted to modify the code and merge it in. I accidentally discovered that Yang Shen also wrote DumpDex https://github.com/lasting-yang/frida_dump
Easily merged into js, now a successful dump is produced.
String Search
Direct Search"request" Not much results.
Obviously, thisjsonObject.addPropertyThe biggest suspect.
Click in and find the CheckCodeUtil class. Its checkcode and decheckcode are probably our targets this time.
1:utilcls
hook
var CheckCodeUtilCls = Java.use("com.bangcle.comapiprotect.CheckCodeUtil");
CheckCodeUtilCls.checkcode.implementation = function(a,b,c){
var rc = this.checkcode(a,b,c);
console.log(TAG + "checkcode >>> a = " + a);
console.log(TAG + "checkcode >>> b = " + b);
console.log(TAG + "checkcode >>> c = " + c);
console.log(TAG + "checkcode >>> rc = " + rc);
return rc;
}
CheckCodeUtilCls.decheckcode.implementation = function(a){
var rc = this.decheckcode(a);
console.log(TAG + "decheckcode >>> a = " + a);
console.log(TAG + "decheckcode >>> rc = " + rc);
return rc;
}
I ran it and it was strange, there was no output at all, this doesn’t make sense?
Look carefully, silly, there are two checkcode functions in this class, so we need to specify which one to hook. Because we ignored the error output when printing the output, we did not see the error.
Notice:If there is no output in the Xcube environment, it is likely that the script reported an error. In this case, do not filter and directly view all output logs to see the error.
Modify the code
CheckCodeUtilCls.checkcode.overload('java.lang.String','int','java.lang.String').implementation = function(a,b,c){
This time the results were printed out smoothly. But something strange happened again, the App crashed.
Rescue a crashed app
Why did it crash? Is there a bug in our printing data?
First comment out the code that prints the input parameters and results. Still crashes.
I commented out all the hook codes and it stopped crashing, but I can’t play without hooks?
The ultimate solution is to change your phone. Many times, it is enough to change your phone, maybe the phone is not suitable for the local environment.
The result was disappointing. I changed the phone and it still crashed.
There is no fluke mentality, which means that the App or shell has detected the Hook of key functions and will destroy it if it is found to be hooked.
The App or shell must be doing the detection at the Native layer. If we want to deal with it, we have to be at the same level as it.
Instead of hooking the functions of the Jave layer, directly hook the checkcode and decheckcode of the Native layer.
Hook Native
We found System.loadLibrary(“encrypt”); in the Java code of CheckCodeUtil, which means that the target we are dealing with is libencrypt.so.
IDA opened it, exported the table, and exposed two pieces of information.
- The address of the checkcode function is 0x24424, and the address of the decheckcode function is 0x2B1BC.
- These two functions most likely use the AES algorithm.
Keep Hooking
var targetSo = Module.findBaseAddress('libencrypt.so');
console.log(TAG +" ############# libencrypt.so: " +targetSo);
// 24424 2B1BC
Interceptor.attach(targetSo.add(0x24424 ),{
onEnter: function(args){
var strCls = Java.use('java.lang.String');
this.rBuf = ptr(this.context.x0);
console.log(TAG + " ======================================== ");
// console.log(TAG + "-------- checkcode x0 = " + ptr(this.context.x0) ) ;
var strA = Java.cast(this.context.x2, strCls);
console.log(TAG + "-------- checkcode a = " + strA);
console.log(TAG + "-------- checkcode b = " + ptr(this.context.x3));
var strC = Java.cast(this.context.x4, strCls);
console.log(TAG + "-------- checkcode b = " + strC);
},
onLeave: function(retval){
var strCls = Java.use('java.lang.String');
var strRc = Java.cast(retval, strCls);
console.log(TAG + "-------- checkcode rc = " + strRc);
}
});
Interceptor.attach(targetSo.add(0x2B1BC ),{
onEnter: function(args){
var strCls = Java.use('java.lang.String');
this.rBuf = ptr(this.context.x0);
console.log(TAG + " ======================================== ");
// console.log(TAG + "-------- checkcode x0 = " + ptr(this.context.x0) ) ;
var strA = Java.cast(this.context.x2, strCls);
console.log(TAG + "-------- decheckcode a = " + strA);
},
onLeave: function(retval){
var strCls = Java.use('java.lang.String');
var strRc = Java.cast(retval, strCls);
console.log(TAG + "-------- decheckcode rc = " + strRc);
}
});
Boss: isn’t the first parameter of the function X0? Why did you print the first parameter as X2?
Me: Boss, I asked you to approve more funds to buy books a long time ago, but you didn’t agree. Now you are showing your weakness. If you look up JNI programming, you will know that the first two parameters of Java calling C/C++ functions are fixed. The actual parameters passed in start from the third one.
Run again this time.
1:rc
The truth is out.
It’s already autumn in the capital, so it’s not time to order fresh beer, but it’s time to order Erguotou.
Conclusion
There are thousands of ways to unpack the shell, but the key is still Dump Dex.
Don’t be afraid of shelled apps, most likely all the clues will be revealed after unpacking. Unshelled apps are the real scary ones.
If the script does not have any output, it may not necessarily mean that the location is wrong, but it may also be that you did not see the error reported by the script.
The app crashed, and changing the phone was effective, although I was embarrassed this time.
For this sample, IDA also tells us that the function entry code in so is also extracted. To analyze this so, we probably have to dump so.