OLLVM控制流程平坦化
简介:https://github.com/obfuscator-llvm/obfuscator/wiki/Control-Flow-Flattening
特征:常量很多,用来根据常量跳转到正确的位置。
分析控制流程平坦化,主要是分析交叉引用,关注两种数据,一种从输入参数开始分析交叉引用,一种是从输出结果分析交叉引用。
把能使用输入参数的地址都Hook一下。
Java反编译
package com.example.hellojni;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.example.hellojni_sign2.R;
import org.apache.commons.lang3.RandomStringUtils;
public class HelloJni extends AppCompatActivity {
TextView tv;
public native String sign1(String str);
public native String sign2(String str, String str2);
public native String stringFromJNI();
/* access modifiers changed from: protected */
@Override // android.support.v7.app.AppCompatActivity, android.support.v4.app.SupportActivity, android.support.v4.app.FragmentActivity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_hello_jni);
this.tv = (TextView) findViewById(R.id.hello_textview);
this.tv.setText(stringFromJNI());
((Button) findViewById(R.id.button_sign2)).setOnClickListener(new View.OnClickListener() {
/* class com.example.hellojni.HelloJni.AnonymousClass1 */
public void onClick(View view) {
HelloJni.this.tv.setText(HelloJni.this.sign2(RandomStringUtils.randomAlphabetic(15), RandomStringUtils.randomAlphabetic(16)));
}
});
}
static {
System.loadLibrary("hello-jni");
}
}
分析SO
找到Native函数sign2:
加载Android ARM库
打开sign2,并重命名第一个参数为env
,类型为JNIEnv*
。第3个、第4个参数对应Java层的str1和str2,所以也重命名为str1和str2。前两个是编译时自带的。
从输入str1开始分析,交叉引用:
重命名v5为_str1, ReleaseStringUTFChars
右键Force Call Type,查看参数:
GetStringUTFChars函数原型:
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
返回char*
其中v10是字符长度。
查看sub_13558:
查看v28交叉引用:
把v3赋值给了v28.
接下来要Hook偏移量为13558的函数:
说明a2是字符串的长度 * 2
。
然后查一下sub_13558((unsigned __int64 *)&v45, (__int64)lp_str1, v10);
中v45的交叉引用:
查看函数sub_12D70:
Esc返回到sub_12D70,查看参数v46:
Hook找到的这几个未知函数:
function hex_dump(p) {
try {
return hexdump(p) + "\r\n";
} catch (error) {
return ptr(p) + "\r\n";
}
}
function hook_native_addr(addr, idb_addr) {
var base_hello_jni = Module.findBaseAddress("libhello-jni.so");
Interceptor.attach(addr, {
onEnter: function (args) {
this.arg0 = args[0];
this.arg1 = args[1];
this.arg2 = args[2];
this.arg3 = args[3];
// 调用地址(LR寄存器)
this.lr = this.context.lr;
}, onLeave: function (retval) {
console.log("ptr:" + ptr(addr) + "idb_addr:" + ptr(idb_addr) + " LR:" + ptr(this.lr).sub(base_hello_jni) + " \r\n",
"this.arg0:\r\n", hex_dump(this.arg0),
"this.arg1:\r\n", hex_dump(this.arg1),
"this.arg2:\r\n", hex_dump(this.arg2),
"this.arg3:\r\n", hex_dump(this.arg3),
"retval:\r\n", hex_dump(retval));
}
})
}
function hook_native() {
// 基地址
var base_hello_jni = Module.findBaseAddress("libhello-jni.so");
// 加上偏移,获取到虚拟地址
// console.log(hexdump(base_hello_jni.add(0x3E070)));
var sub_13558 = base_hello_jni.add(0x13558);
// Interceptor.attach(sub_13558, {
// onEnter: function (args) {
// this.arg0 = args[0];
// this.arg1 = args[1];
// this.arg2 = args[2];
// }, onLeave: function (retval) {
// console.log("sub_13558:", hexdump(this.arg0), "\r\n",
// hexdump(this.arg1, { length: parseInt(this.arg2) }));
// }
// })
// hook_native_addr(base_hello_jni.add(0x12D70), 0x12D70);
// hook_native_addr(base_hello_jni.add(0x13558), 0x13558);
// hook_native_addr(base_hello_jni.add(0x162B8), 0x162B8);
// hook_native_addr(base_hello_jni.add(0x130F0), 0x130F0);
hook_native_addr(base_hello_jni.add(0x15F1C), 0x15F1C);
// hook_native_addr(base_hello_jni.add(0x154D4), 0x154D4);
// hook_native_addr(base_hello_jni.add(0x158AC), 0x158AC);
Interceptor.attach(base_hello_jni.add(0x154D4), {
onEnter: function (args) {
this.arg0 = args[0];
this.arg1 = args[1];
this.arg2 = args[2];
}, onLeave: function (retval) {
console.log("0x154D4:",
hexdump(this.arg1, { length: parseInt(this.arg2) }));
}
})
}
function hook_java() {
Java.perform(function () {
// 首先把随机数固定一下
var HelloJni = Java.use("com.example.hellojni.HelloJni");
HelloJni.sign2.implementation = function (str, str2) {
var result = this.sign2("0123456789abcde", "fedcba9876543210");
console.log("Java sign2 result:", result);
return result;
}
})
}
function main() {
hook_native();
hook_java();
}
setImmediate(main);
通过分析输出结果,可知sub_15F1C(v33, v34, &v51);
是关键函数。
查看sub_130F0函数:
查看sub_162B8:
打开aAbcdefghijklmn:
找到Base64编码函数特征。
调用aAbcdefghijklmn的函数即Base编码函数:
查看交叉引用:
然后查看sub_15F1C:
改名:
进去sub_15F1C查看:
查看sub_154D4、sub_158AC函数。
找到md5特征:https://github.com/Zunawe/md5-c/blob/main/md5.c https://code.woboq.org/linux/linux/crypto/md5.c.html
重命名md5_transform:
md5_update调用的md5_transform:
md5特征:
找到md5函数: