新人刚接触android逆向,请多多包涵!今天分析了一款apk,apk作者利用异步回调机制来实现该apk的注册,该注册机制实现的思路是很新颖的,并且在分析的过程中还出现了点小惊喜。
下面我将以软件注册机制初探、注册机制系统分析和破解之法进行详细讲解。
注意:本文仅用于移动安全的研究,勿用于非法用途,由此带来的后果与本文作者无关。如果感觉好用,请支持正版,毕竟软件作者也是不容易的。
下面将以第一视角来讲解第一部分
-----------------------------------------------
一 软件注册机制初探
-----------------------------------------------
初次拿到该apk,安装。
如果点击设置皮肤,则会弹出需要激活对话框,提示需要50的积分。
点击关于对话框,其中会提示“免费版”和“移除广告”等,点击购买付费版,则会提示购买和授权等
看到这里,我想该软件的注册流程大家应该熟悉了吧。如果不清楚,可以看我上一篇文章《某APK注册机制分析与多思路破解方法》,在此就不啰嗦了。
进入正题......
看到这,破解的思路:如果搞到正式版本,那么相应的功能限制便会一一解除。
载入JEB,找到AboutActivity类(看名字就知道对应的正是关于对话框)中的oncreate()方法,如下:
[Java] 纯文本查看 复制代码 public void onCreate(Bundle arg11) {
this.requestWindowFeature(1);
super.onCreate(arg11);
this.setContentView(2130903040);
Log.d("tt", "onCreate");
this.a = s.a(((Activity)this));
this.e = new bf(((Activity)this));
this.a();
this.findViewById(2131361792).setOnClickListener(new g(this));
this.findViewById(2131361794).setOnClickListener(new i(this));
this.findViewById(2131361796).setOnClickListener(new j(this));
this.findViewById(2131361797).setOnClickListener(new k(this));
this.findViewById(2131361798).setOnClickListener(new l(this));
this.findViewById(2131361799).setOnClickListener(new m(this));
View v0 = this.findViewById(2131361802);
View v1 = this.findViewById(2131361803);
View v2 = this.findViewById(2131361804);
View v3 = this.findViewById(2131361805);
View v4 = this.findViewById(2131361807);
View v5 = this.findViewById(2131361808);
((ImageView)v0).setOnClickListener(new r(this, String.valueOf("http://pocket.ndtv.com.cn:1086/others/")
+ "BlessSmsFree.apk"));
((ImageView)v1).setOnClickListener(new r(this, String.valueOf("http://pocket.ndtv.com.cn:1086/others/")
+ "Foyin.apk"));
((ImageView)v2).setOnClickListener(new r(this, String.valueOf("http://pocket.ndtv.com.cn:1086/others/")
+ "Flashlight.apk"));
((ImageView)v3).setOnClickListener(new r(this, String.valueOf("http://pocket.ndtv.com.cn:1086/others/")
+ "KnotGuideFree.apk"));
((ImageView)v4).setOnClickListener(new r(this, String.valueOf("http://pocket.ndtv.com.cn:1086/others/")
+ "Geomancy.apk"));
((ImageView)v5).setOnClickListener(new p(this));
}
首先看最底下的6个点击事件注册函数,其中传入了BlessSmsFree.apk、Foyin.apk、Flashlight.apk、KnotGuideFree.apk、Geomancy.apk等,
再回头看上面第三张图,这几个点击事件正好对应图中最下面五款apk的安装。
因此,购买付费版的点击事件注册函数应该是
[Java] 纯文本查看 复制代码 this.findViewById(2131361799).setOnClickListener(new m(this));
接着来到class m(作者对类名做了混淆)类的点击事件函数:
[Java] 纯文本查看 复制代码 public void onClick(View arg6) {
DialogInterface$OnClickListener v4 = null;
if(AboutActivity.c(this.a).o()) {
new AlertDialog$Builder(this.a).setMessage("您已购买付费版,谢谢您的支持!").setPositiveButton("知道了", v4)
.show();
}
else {
new AlertDialog$Builder(this.a).setMessage("5元升级为永久无广告无限制的付费版,格机或刷机后均无需再次付费(点击“授权”即可)。目前可以通过支付宝在线支付。\n账号:[email]fengyun2108@163.com[/email]\n户名:谢文杰\n付款界面请按照以下信息填写:\n—————————————\n汇款金额:5元\n汇款说明:"
+ this.a.getSystemService("phone").getDeviceId() + "\n" + "—————————————" + "\n"
+ "付费成功后隔天点击“授权”即可升级为付费版。确定吗?").setNegativeButton("取消", v4).setNeutralButton("授权",
new n(this)).setPositiveButton("付款", new o(this)).show();
}
}
看到此处,我想应该就是找到关键点了吧。该函数的流程为:
点击购买付费版->首先检测是否已购买付费(是,则弹出提示购买成功的对话框)->否则会提示上面第四张图的内容
另外,如果点击第四张种的“授权”,则会提示图5的内容(其中会提示,授权失败,但是付款成功,请等待一段时间后,重试),提示的内容可能大家有个疑问,既然付款成功了,怎么还没授权呢。那是因为该apk在付款成功后,作者会在服务器端进行处理,然后才能返回是否可以授权,因此不管付费成功与否,都会这样提示,最后根据服务端传回的信息,判断是否真的可以授权。
那我们该怎么破解呢,当然是在最初判断那里做文章了,如下代码所示的位置。
[Java] 纯文本查看 复制代码 if(AboutActivity.c(this.a).o()) {
new AlertDialog$Builder(this.a).setMessage("您已购买付费版,谢谢您的支持!").setPositiveButton("知道了", v4)
.show();
}
来到0()方法,如下:
[Java] 纯文本查看 复制代码 public boolean o() {
return this.o;
}
其中返回的值为s类的 private boolean o; 一个布尔型。
如果使该值为true,那不就注册成功了吗
查看S类,可以看到在a(Activity arg14)和h(boolean arg1)中有赋值操作,分别如下:
[Java] 纯文本查看 复制代码 public static s a(Activity arg14) {
s v0 = new ce().a(arg14);
if(v0 == null) {
v0 = new s();
v0.a("0");
v0.b("0");
v0.g(true);
v0.h(false);
v0.c("");
}
......
return v0;
}
----------------------------
[Java] 纯文本查看 复制代码 public void h(boolean arg1) {
this.o = arg1;
}
可以看出,应该在public static s a(Activity arg14)函数中,在函数内部首先新建了一个ce类,并调用了其a()方法,如下:
[Java] 纯文本查看 复制代码 s v0 = new ce().a(arg14);
来到ce类的a()方法,如下:
[Java] 纯文本查看 复制代码 public s a(Activity arg3) {
s v0_2;
try {
FileInputStream v0_1 = arg3.openFileInput("config.xml");
this.a = new bi();
v0_2 = this.a.a(((InputStream)v0_1));
}
catch(Exception v0) {
Log.e("XML", v0.getMessage());
v0_2 = null;
}
return v0_2;
}
其中新建了bi类,并调用了该类的a()方法,来到bi类的a()方法,如下:
[Java] 纯文本查看 复制代码 public s a(InputStream arg6) {
XmlPullParser v2 = Xml.newPullParser();
v2.setInput(arg6, "UTF-8");
s v0 = null;
int v1 = v2.getEventType();
label_8:
if(v1 == 1) {
return v0;
}
switch(v1) {
case 0: {
goto label_14;
}
case 2: {
goto label_16;
}
}
goto label_12;
label_16:
if(v2.getName().equals("result")) {
v2.next();
v0.a(v2.getText());
goto label_12;
}
if(v2.getName().equals("activedSkins")) {
v2.next();
v0.b(v2.getText());
goto label_12;
}
if(v2.getName().equals("showWelcome")) {
v2.next();
v0.g(Boolean.parseBoolean(v2.getText()));
goto label_12;
}
if(v2.getName().equals("payVersion")) {
v2.next();
v0.h(Boolean.parseBoolean(v2.getText()));
goto label_12;
}
if(!v2.getName().equals("removeAdDate")) {
}
else {
v2.next();
v0.c(v2.getText());
goto label_12;
label_14:
v0 = new s();
}
label_12:
v1 = v2.next();
goto label_8;
return v0;
}
看到这,应该明白了吧,这一系列函数会读取config.xml文件,根据该文件中payVersion标签位置的值,设置s类的布尔型变量o,然后购买时,会根据该布尔值,判断是否支付过,最后在说明一点public static s a(Activity arg14)函数其实是在AboutActivity类的oncreate中调用的,如下:
[Java] 纯文本查看 复制代码 public void onCreate(Bundle arg11) {
this.requestWindowFeature(1);
super.onCreate(arg11);
this.setContentView(2130903040);
Log.d("tt", "onCreate");
this.a = s.a(((Activity)this));
整个流程如下:
到此,你是否认为只需要pull回来config.xml文件,然后修改payVersion标签为true,再push回去,就可以了。
那我们可以试试,如下:
文件在config.xml /data/data/xwj.calculator/files目录下
那pull回来看看内容,
修改为true后,push回去看看,如下:
然后,再点击“关于”看看
试试功能是否都能解禁,换个皮肤看看
到此,你是否觉得的已经成功破解了呢?
答案是否定的,因为作为文章来说,异步回调机制实现软件注册我还没有讲解,不可能这样就结束的。
重启软件看看有什么效果,
看到没“非法正式版用户”,关于中又显示为免费版了
原因是作者利用异步回调机制来实现软件的注册和非法用户检查的。
接下来,我们继续讲解------柳暗花明之注册机制系统分析
由于文章有点长,第二部分的内容和破解之道,我会放到下篇文章中讲解。
|