应用如下,
1、点击执行功能,弹出弹窗;
2、弹窗中点击取消退出应用;点击确定后跳到注册页面。
3、注册页面完整注册后,弹窗提示,之后退出app
一、分析apk
1、class MyApp
加载了一个本地库,有三个native方法,initSN()、saveSN(String)、work()。在onCreate()中执行了initSN()方法。
public class MyApp extends Application
{
public static int m = 0;
static{
System.loadLibrary("juan"); }
public native void initSN();
public void onCreate(){
initSN();
super.onCreate();}
public native void saveSN(String paramString);
public native void work();
}
2、class MainActivity
主要看onCreate()方法,启动后读取MyApp的成员m,并把值赋给int i。然后根据i的值,定义不同的string。
private static String workString;
public void work(String paramString){
workString = paramString;
}
public void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130903040);
((MyApp)getApplication());
int i = MyApp.m;
String str;
if (i == 0) {
str = "-未注册";
}
for (;;) # 这里的反编译是有问题的,通过上一篇章,我们知道这块是一个循环,不影响代码的阅读
{
setTitle("NDK保护与重启验证演示程序" + str);
this.btn1 = ((Button)findViewById(2131165184));
this.btn1.setOnClickListener(new MainActivity.1(this));
return;
if (i == 1) {
str = "-正式版";
} else if (i == 2) {
str = "-专业版";
} else if (i == 3) {
str = "-企业版";
} else if (i == 4) {
str = "-专供版";
} else {
str = "-未知版";
}
}
}
3、按钮的OnClickListener
跟进button的OnClickListener事件,先判断了MyApp的m成员是否为0,如果为0就走注册的逻辑。不为0,调用native的work()方法,然后弹对应的Toast提示--MainActivity.access$0(),其实就是private static String workString的值。而workString的值又是从work(String)方法而来。
class MainActivity$1
implements View.OnClickListener
{
MainActivity$1(MainActivity paramMainActivity) {}
public void onClick(View paramView)
{
((MyApp)this.this$0.getApplication());
if (MyApp.m == 0)
{
this.this$0.doRegister();
return;
}
((MyApp)this.this$0.getApplication()).work();
Toast.makeText(this.this$0.getApplicationContext(), MainActivity.access$0(), 0).show();
}
}
二、尝试修改app
1、在MyApp中,重新给成员m赋值
修改方法就不介绍了,直接在smali添加两行,将自定义的值赋给m。
const /4 v0, 0x3; #企业版
sput v0, Lcom/droider/ndkapp/MyApp;m:I
打包进行验证,发现点击执行功能后,还是弹出了需要注册的弹窗,所以直接赋值的路不通了。还是看onClick方法,我们之前分析到了要调用native的work方法,所以就得深入libjuan.so进行查看了。
三、分析so文件
我用的是mac,工具IDA,因为是初次使用,所以也记录下初始化过程。
1、导入jni.h
至于为什要要导入,是因为IDA不能识别JNI结构体,影响反汇编代码的阅读。
File -- Liad file -- Parse C header file,选择jni.h文件。这里我用了Android sdk的NDK目录下的jni文件:
/android-sdk-macosx/ndk-bundle/platforms/android-24/arch-arm/usr/include/jni.h
按照介绍,注释掉开头的 #include <sys/cdefs.h>,#include <stdarg.h>。将#define JNIEXPORT attribute ((visibility ("default")))注释掉,改成#define JNIEXPORT
导入完成后会有成功提示,如果报错,就安装报错的提示修改。注意copy文件,不要影响原ndk的jni文件。
2、structure中添加JNINativeInterface
structures选项卡汇总,将JNINativeInterface和JNIInvokeInterface添加进去。
添加成功后,回到主页面,在0x240处点击右键,可以看到已经能够解析出JNINativeInterface.GetStaticFiledID函数了。
3、搜索函数
app中有3个native方法,initSN()、saveSN(String)、work()。在左侧的function搜索函数名,发现并没有搜到有用的内容,所以到JNI_OnLoad中一探究竟。
在.text:000013AC LDR PC, [R12,#JNINativeInterface.RegisterNatives]这行,明显的看到了RegisterNatives,其实就是把本地函数和java类方法关联起来。
而RegisterNatives函数需要传进来class(native methods),此处传的是_data_start首地址。跟进看看发现数据如下,看到了熟悉的方法initSn、saveSn、work。所以得出结论:
n1对应着initSn()方法
n2对应着saveSn(String)方法
n3对应着work()方法
4、分析函数
前面的分析中,我们点击执行功能会调用work方法,所以先看n3。细节的地方还不太懂,能看出大概的意思。先执行n1(initSN)方法,然后getValue取到值,再对值分别做比较1、2、3、4,跳转各个分支,并且在最后都调用了callWork方法。
调用了com/droider/ndkapp/MainActivity类的work(String)方法,work(String)方法是给String workString赋值的,也就是toast的string文案。
回到n1(initSN)函数
先是打开(fopen)sd卡中的reg.dat文件,经过一些判断没问题后读到内存中(fread)。对比几个字符串是否相等后,跳转不同分支,但最后也都执行setValue方法。
setValue函数,其实就是给MyApp的m成员赋值的。
n2(saveSn)函数:
通过观察,写一个reg.dat文件,使用md5加密。
5、修改
n3函数主要是拿到initSN后的m值,根据对应的m值给出toast文案
n2函数就是写数据reg.data
n1函数主要是读数据,然后经过判断给MyApp的m成员赋值
所以直接修改n1的赋值相关逻辑是比较的好的方法。这里修改的思路是直接学习来的,仍以判断条件处入手。比如我们认定3号了(企业版),就得让setValue到“3号位置”。这里学习到的技巧就是直接修改字节码,让CMP比较结果为真即可。
...........
.text:000015DC ADD R1, PC, R1 ; "b2db1185c9e5b88d9b70d7b3278a4947"
.text:000015E0 BL strcmp
.text:000015E4 CMP R0, #0
.text:000015E8 BEQ loc_163C
...........
查看字节码,该条指令字节码为00 00 50 E3,将0x15E7修改成E1,这样相当于CMP R0,R0,也就是比较结果为真。修改完成后重新打包安装。
6、成功
由于sd中已有reg.data文件,所以点击执行后直接弹toast提示,这样标明我们的破解成功了。
四、题外话
saveSn的时候采用的是MD5加密算法,开发者提供了4个加密的字符串12345678、2345678、32345678、42345678。用MD5验证下,分别对应着25d55ad283aa400af464c76d713c07ad、08e0750210f66396eb83957973705aad、b2db1185c9e5b88d9b70d7b3278a4947、18e56d777d194c4d589046d62801501c。
也就说4个注册码分别为上面的字符串
APK连接:
链接: https://pan.baidu.com/s/1i5CHQvV 密码: umqp