`
dawuafang
  • 浏览: 1112423 次
文章分类
社区版块
存档分类
最新评论

2、叉叉助手逆向分析(上)

 
阅读更多
描述:主要讲解如何有条例地逆向分析出软件的主要逻辑。
工具:APKIDE,JD-GUI
方法:顺藤摸瓜,smali代码主要看invoke关键函数调用,定位到相应的类中看代码。

使用APKIDE反编译xxzhushou_android.apk。

各个游戏的辅助只有通过叉叉助手里“我的游戏”启动游戏才会显示,如果直接从桌面启动游戏,辅助并不会启动。

搜索“启动游戏”字符串无果,猜测是图片显示,在com.xxAssistant\res\drawable目录下找到图片:
在APKIDE里搜索图片文件名:icon_mygame_startgame_pluginon,查找结果:
<publictype="drawable"name="icon_mygame_startgame_pluginon"id="0x7f0200a5"/>
继续搜索:0x7f0200a5,结果在com\xxAssistant\a\h.smali中。那么设置“启动游戏”控件相关的代码也就在此文件中,浏览上下文找到一处设置控件onClick事件处理过程的代码:
new-instance v2, Lcom/xxAssistant/a/j;invoke-direct {v2, p0, v3}, Lcom/xxAssistant/a/j;-><init>(Lcom/xxAssistant/a/h;Lcom/xxAssistant/a/k;)V invoke-virtual {v1, v2}, Landroid/widget/LinearLayout;->setOnClickListener(Landroid/view/View$OnClickListener;)V
其中的“new-instance v2, Lcom/xxAssistant/a/j;”相当于new了一个OnClickListener,因此打开com/xxAssistant/a/j分析:
.classLcom/xxAssistant/a/j; .super Ljava/lang/Object; # interfaces.implements Landroid/view/View$OnClickListener;#instancefields.fieldfinal synthetica:Lcom/xxAssistant/a/h;.fieldprivatefinal syntheticb:Lcom/xxAssistant/a/k; # direct methods.methodconstructor <init>(Lcom/xxAssistant/a/h;Lcom/xxAssistant/a/k;)V.locals0iput-objectp1, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; iput-objectp2, p0, Lcom/xxAssistant/a/j;->b:Lcom/xxAssistant/a/k; invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void.endmethod#virtualmethods.methodpubliconClick(Landroid/view/View;)V.locals6const/4v5,0x1iget-objectv0, p0, Lcom/xxAssistant/a/j;->b:Lcom/xxAssistant/a/k; iget-objectv0, v0, Lcom/xxAssistant/a/k;->d:Lcom/xxAssistant/Widget/MyTextView; invoke-virtual{v0}, Lcom/xxAssistant/Widget/MyTextView;->getPackageName()Ljava/lang/String; move-result-objectv1 iget-objectv0, p0, Lcom/xxAssistant/a/j;->b:Lcom/xxAssistant/a/k; iget-objectv0, v0, Lcom/xxAssistant/a/k;->d:Lcom/xxAssistant/Widget/MyTextView; invoke-virtual{v0}, Lcom/xxAssistant/Widget/MyTextView;->getAppName()Ljava/lang/String; move-result-objectv0 iget-objectv2, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v2}, Lcom/xxAssistant/a/h;->b(Lcom/xxAssistant/a/h;)Lcom/xxAssistant/d/d; move-result-objectv2 invoke-virtual{v2, v1}, Lcom/xxAssistant/d/d;->b(Ljava/lang/String;)I move-result v2 iget-objectv3, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v3}, Lcom/xxAssistant/a/h;->c(Lcom/xxAssistant/a/h;)Lcom/xxAssistant/d/e; move-result-objectv3 invoke-virtual{v3, v2}, Lcom/xxAssistant/d/e;->a(I)Lcom/xxAssistant/g/f; move-result-objectv3 if-eqz v3, :cond_4 invoke-virtual{v3}, Lcom/xxAssistant/g/f;->a()I move-result v3 if-ne v3, v5, :cond_3 iget-objectv3, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v3}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv3 const-stringv4,"game_start_have_plugin"invoke-static {v3, v4, v0}, Lcom/b/a/a;->b(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V iget-objectv3, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v3}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv3 const/16v4,0x3f4invoke-static {v3, v4, v0}, Lcom/xxAssistant/Utils/i;->a(Landroid/content/Context;ILjava/lang/String;)V const/4v0,0x0sget-objectv3, Lcom/xxAssistant/View/xxApplication;->b:Landroid/content/SharedPreferences; const-stringv4,"auto_clean_memory"invoke-interface {v3, v4, v5}, Landroid/content/SharedPreferences;->getBoolean(Ljava/lang/String;Z)Z move-result v3 if-eqz v3, :cond_0 iget-objectv0, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v0}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv0 invoke-static {v0}, Lcom/xxAssistant/Utils/w;->a(Landroid/content/Context;)Landroid/widget/Toast; move-result-objectv0 :cond_0 iget-objectv3, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v3}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv3 invoke-static {v3, v2, v1}, Lcom/xxAssistant/Utils/ag;->a(Landroid/content/Context;ILjava/lang/String;)V if-eqz v0, :cond_1 invoke-virtual{v0}, Landroid/widget/Toast;->show()V :cond_1 sget-boolean v0, Lcom/xxAssistant/Utils/r;->a:Z if-eqz v0, :cond_2 iget-objectv0, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v0}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv0 invoke-static {v1, v0}, Lcom/xxAssistant/Utils/w;->a(Ljava/lang/String;Landroid/content/Context;)Ljava/lang/Boolean; :goto_0 return-void:cond_2 iget-objectv0, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; iget-objectv0, v0, Lcom/xxAssistant/a/h;->a:Landroid/os/Handler; const/16v2,0x8invoke-virtual{v0, v2}, Landroid/os/Handler;->sendEmptyMessage(I)Z iget-objectv0, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v0}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv0 invoke-static {v1, v0}, Lcom/xxAssistant/Utils/w;->a(Ljava/lang/String;Landroid/content/Context;)Ljava/lang/Boolean; goto :goto_0 :cond_3 iget-objectv2, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v2}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv2 const-stringv3,"game_start_have_plugin"invoke-static {v2, v3, v0}, Lcom/b/a/a;->b(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V iget-objectv2, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v2}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv2 const/16v3,0x3f7invoke-static {v2, v3, v0}, Lcom/xxAssistant/Utils/i;->a(Landroid/content/Context;ILjava/lang/String;)V iget-objectv0, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v0, v1}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;Ljava/lang/String;)V goto :goto_0 :cond_4 iget-objectv2, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v2}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv2 const-stringv3,"game_start_no_plugin"invoke-static {v2, v3, v0}, Lcom/b/a/a;->b(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V iget-objectv2, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v2}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;)Landroid/content/Context; move-result-objectv2 const/16v3,0x3f5invoke-static {v2, v3, v0}, Lcom/xxAssistant/Utils/i;->a(Landroid/content/Context;ILjava/lang/String;)V iget-objectv0, p0, Lcom/xxAssistant/a/j;->a:Lcom/xxAssistant/a/h; invoke-static {v0, v1}, Lcom/xxAssistant/a/h;->a(Lcom/xxAssistant/a/h;Ljava/lang/String;)V goto :goto_0 .endmethod
从开头可以看出这确实是一个OnClickListener,重点分析onClick处理过程。
先看这几处字符串:
auto_clean_memory
game_start_have_plugin
game_start_no_plugin
其中auto_clean_memory是在启动游戏前进行一次内存清理以为游戏腾出更多内存。
game_start_have_plugin和game_start_no_plugin分别对应“辅助开启”和没有辅助的游戏,如图:
继续分析几个重点的函数调用:
getPackageName
getAppName
invoke-static {v2}, Lcom/xxAssistant/a/h;->b(Lcom/xxAssistant/a/h;)Lcom/xxAssistant/d/d;
经分析com/xxAssistant/d/d是数据库查询相关,这里把操作的数据库提取出来查看:

shell@android:/data/data/com.xxAssistant # ls
ls
app_plugin
cache
databases
files
lib
shared_prefs
utility.plist
xx-filter
shell@android:/data/data/com.xxAssistant # cd databases
cd databases
shell@android:/data/data/com.xxAssistant/databases # ls
ls
XXAssistantDB.db
XXAssistantDB.db-journal
webview.db
webview.db-journal
webviewCookiesChromium.db
webviewCookiesChromium.db-journal
webviewCookiesChromiumPrivate.db

adb pull /data/data/com.xxAssistant/databases/XXAssistantDB.db e:\

显示Permission denied,修改权限:chmod 755 XXAssistantDB.db
再次pull出来:
大致看出数据库结果,这里不是重点不再继续深入分析,继续回到a/j,后续的几个关键调用:
com/xxAssistant/Utils/i;
Lcom/xxAssistant/Utils/w;->a(Landroid/content/Context;)Landroid/widget/Toast;
Lcom/xxAssistant/Utils/ag;->a(Landroid/content/Context;ILjava/lang/String;)V
sget-boolean v0, Lcom/xxAssistant/Utils/r;->a:Z

其中com.xxAssistant.Utils.i代码出现字符串“http://api.xxzhushou.cn/xxdatareport.php”,猜测是统计使用信息相关的,无重要代码没有继续分析。
com/xxAssistant/Utils/w有一个生成吐司的函数,和一个启动游戏的函数(使用jd-ui打开):
packagecom.xxAssistant.Utils;importandroid.content.ComponentName;importandroid.content.Context;importandroid.content.Intent;importandroid.content.pm.ActivityInfo;importandroid.content.pm.PackageInfo;importandroid.content.pm.PackageManager;importandroid.content.pm.PackageManager.NameNotFoundException;importandroid.content.pm.ResolveInfo;importandroid.content.res.Resources;importandroid.os.Handler;importandroid.text.TextUtils.TruncateAt;importandroid.view.Display;importandroid.widget.ImageView;importandroid.widget.LinearLayout;importandroid.widget.LinearLayout.LayoutParams;importandroid.widget.TextView;importandroid.widget.Toast;importcom.d.b.bx;importcom.xxAssistant.View.MainActivity;importcom.xxAssistant.Widget.AlwaysMarqueeTextView;importcom.xxAssistant.c.b;importcom.xxAssistant.c.f;importjava.util.Iterator;importjava.util.List;publicclassw {privatestaticContext a;privatestaticLinearLayout b;privatestaticTextView c;privatestaticTextView d;privatestaticString e = " 叉叉助手:";privatestaticString f;privatestaticToast g;privatestaticHandler h =newx();publicstaticToast a(Context paramContext) { a = paramContext; h localh =newh(a);longl1 = localh.a(); localh.b();longl2 = localh.a(); g =newToast(a); g = Toast.makeText(a,null, 1); g.setGravity(48, 0, 0); LinearLayout localLinearLayout1 = (LinearLayout)g.getView(); c =newTextView(a); b.a = 1 + b.a; c.setTextSize(14.0F); c.setGravity(16); c.setTextColor(a.getResources().getColor(2131099705)); d =newAlwaysMarqueeTextView(a);inti = (int)(l2 - l1); (10 + (int)((l2 - l1) % 10L));if(i <= 0);for(intj = 10; ; j = i) {if((b.c == f.c) && (b.b.size() != 0) && (b.a < b.b.size())) { c.setText(" " + ((bx)b.b.get(b.a)).i() + ":"); d.setSingleLine(); d.setEllipsize(TextUtils.TruncateAt.MARQUEE); d.setText(((bx)b.b.get(b.a)).g()); d.setPadding(0, 0, 0, 0);if(b.a < 30) b.a = 1 + b.a;newz().start(); }while(true) { d.setTextSize(14.0F); d.setGravity(16); d.setTextColor(-1); LinearLayout.LayoutParams localLayoutParams1 =newLinearLayout.LayoutParams(-2, -2); LinearLayout.LayoutParams localLayoutParams2 =newLinearLayout.LayoutParams(-2, -2); LinearLayout.LayoutParams localLayoutParams3 =newLinearLayout.LayoutParams((int)(MainActivity.p.getWidth() - 80.0F * MainActivity.q), (int)(40.0F * MainActivity.q)); LinearLayout.LayoutParams localLayoutParams4 =newLinearLayout.LayoutParams((int)(35.0F * MainActivity.q), (int)(35.0F * MainActivity.q)); LinearLayout localLinearLayout2 =newLinearLayout(a); b =newLinearLayout(a); localLinearLayout2.setOrientation(0); b.setOrientation(0); b.addView(c, localLayoutParams2); b.addView(d, localLayoutParams1); localLinearLayout2.addView(b, localLayoutParams1); localLinearLayout2.setGravity(16); localLinearLayout2.setBackgroundResource(2130837522); ImageView localImageView =newImageView(a); localImageView.setBackgroundResource(2130837650); localLinearLayout1.addView(localImageView, localLayoutParams4); localLinearLayout1.addView(localLinearLayout2, localLayoutParams3); localLinearLayout1.setOrientation(0); localLinearLayout1.setBackgroundResource(2131099661); f = "成功清理了," + j + "M内存";returng; c.setText(e); d.setText("成功清理了," + j + "M内存"); } } }publicstaticBoolean a(String paramString, Context paramContext) { PackageManager localPackageManager = paramContext.getPackageManager();try{ PackageInfo localPackageInfo = localPackageManager.getPackageInfo(paramString, 0); Intent localIntent1 =newIntent("android.intent.action.MAIN",null); localIntent1.addCategory("android.intent.category.LAUNCHER"); localIntent1.setPackage(localPackageInfo.packageName); List localList = localPackageManager.queryIntentActivities(localIntent1, 0);if(localList.iterator().hasNext()) { ResolveInfo localResolveInfo = (ResolveInfo)localList.iterator().next(); String str1 = localResolveInfo.activityInfo.packageName; String str2 = localResolveInfo.activityInfo.name; Intent localIntent2 =newIntent("android.intent.action.MAIN"); localIntent2.addFlags(268435456); localIntent2.addCategory("android.intent.category.LAUNCHER"); localIntent2.setComponent(newComponentName(str1, str2)); paramContext.startActivity(localIntent2);returnBoolean.valueOf(true); } }catch(PackageManager.NameNotFoundException localNameNotFoundException) {while(true) localNameNotFoundException.printStackTrace(); }returnBoolean.valueOf(false); } }
第一个返回吐司的a函数不用分析,第二个a函数是启动游戏APP的,启动方式为常规方式。

继续分析com/xxAssistant/Utils/ag:
packagecom.xxAssistant.Utils;importandroid.content.Context;importandroid.widget.Toast;importjava.io.File;publicclassag {publicstaticvoida(Context paramContext,intparamInt, String paramString) {ah localah =newah(paramContext);try{ File localFile =newFile(paramContext.getCacheDir(), "");if(!localFile.exists()) localFile.mkdir();newFile(paramContext.getCacheDir(), "/plist.xx");if(Utility.doSymlinkAndChmode(newFile("/data/data/com.xxAssistant/app_plugin/" + paramInt + "/" + paramString + ".xxplist").getAbsolutePath(), paramContext.getCacheDir().toString() + "/plist.xx")) {localah.start();return; } Toast.makeText(paramContext, "文件部署不成功,请重新启动!", 0).show();return; }catch(Exception localException) { localException.printStackTrace(); } } }
new了一个ah并启动,可以猜出应该是个Thread,这个我们稍后细分析,中间调用了个doSymlinkAndChmode,用来将插件的配置文件重定向到一个公共的配置文件路径。
上面可以看出插件的配置文件是保存在"/data/data/com.xxAssistant/app_plugin/"目录下的,我们后面具体分析插件的时候便从这里入手
Utility.doSymlinkAndChmode是在Utility中的一个native函数:
public static native boolean doSymlinkAndChmode(String paramString1, String paramString2);Utility
IDA打开libutility.so,参考字符串doSymlinkAndChmode,找到如下代码:
可以看出有许多类似函数名的字符串,以及函数类型的字符串,可以猜测这里是一个“函数表”,通常在NDK开发时会在JNI_OnLoad函数中注册本地函数,注册函数如下所示:
/** * Table of methods associated with a single class.*/staticJNINativeMethod gMethods[] = { {"load","(Landroid/app/Application;Ljava/lang/String;)V", (void*)load}, {"run","(Landroid/app/Application;Ljava/lang/String;)V", (void*)run}, };/** Register native methods for all classes we know about.*/staticintregisterNativeMethods(JNIEnv* env) {intnError =0; jclass clazz = NULL; clazz = env->FindClass(JNIREG_CLASS);if(clazz == NULL) { LOGE("clazz is null");returnJNI_FALSE; } nError = env->RegisterNatives(clazz, gMethods,sizeof(gMethods) /sizeof(gMethods[0]) );if( nError <0) { LOGE("RegisterNatives error: %d num: %d",nError,sizeof(gMethods) /sizeof(gMethods[0]) );returnJNI_FALSE; }returnJNI_TRUE; }
因此从上表可以找到doSymlinkAndChmode对应的函数主体是sub_1C7C(这里截取一段):

主要调用了symlink和chmod,将插件的配置文件重定向到一个公共的配置文件路径。

继续分析ah:
packagecom.xxAssistant.Utils;importandroid.content.Context;importjava.io.File;classahextendsThread { ah(Context paramContext) { }publicvoidrun() {super.run();try{ Thread.sleep(3000L); Utility.doRemoveFile(this.a.getCacheDir().toString() + "/plist.xx");return; }catch(InterruptedException localInterruptedException) { localInterruptedException.printStackTrace(); } } }
猜测目的:点击“启动游戏”时如果此游戏有插件,则将插件配置文件重定向到一个公共的配置文件路径,游戏启动时底层监控到后读取此公共配置文件,由于做了重定向实际上就是读取当前游戏插件的配置文件,这里的等待三秒钟基本上配置文件也已经使用完毕,然后删除之。

最后是a/j代码中的“sget-boolean v0, Lcom/xxAssistant/Utils/r;->a:Z”,这是获取一个布尔变量的值,打开Utils/r查看其代码:
packagecom.xxAssistant.Utils;importandroid.annotation.SuppressLint;importandroid.content.Context;importandroid.content.Intent;importandroid.content.pm.ApplicationInfo;importandroid.os.Handler;importcom.xxAssistant.Service.GhostService; @SuppressLint({"SdCardPath"})publicclassr{publicstaticbooleana =false;publicstaticbooleanb =true;publicstaticvoida(Context paramContext, Handler paramHandler) { s locals =news(paramHandler, paramContext,newIntent(paramContext, GhostService.class));try{ String str1 = v.c(paramContext) + "/injectso"; StringBuilder localStringBuilder1 =newStringBuilder(); localStringBuilder1.append(str1); StringBuilder localStringBuilder2 =newStringBuilder(" "); Object[] arrayOfObject =newObject[1]; arrayOfObject[0] = Integer.valueOf(Utility.getppid()); localStringBuilder1.append(String.format("%d", arrayOfObject)); localStringBuilder1.append(" " + "/data/data/com.xxAssistant/lib/libxxghost.so"); localStringBuilder1.append("loadResAndPlugin"); localStringBuilder1.append(" " + paramContext.getApplicationInfo().sourceDir); String str2 = localStringBuilder1.toString();locals.start();if((Utility.a(str2)) || (Utility.isInjected())) {paramHandler.sendEmptyMessage(78); a =true; b =false;return; } paramHandler.sendEmptyMessage(79); a =false; b =false;return; }catch(Exception localException) { localException.printStackTrace(); } } }
其中a/j里面使用的a是r类中的一个静态布尔变量,是在一个静态的a函数中被赋值的,重点就是这个a函数。其中v.c返回路径:/data/data/com.xxAssistant/cache/injecttest:
packagecom.xxAssistant.Utils;importandroid.annotation.SuppressLint;importandroid.content.Context;importandroid.content.res.AssetManager;importandroid.content.res.Resources;importandroid.util.Log;importjava.io.File;importjava.io.FileOutputStream;importjava.io.InputStream;publicclassv {staticString a = "injecttest/";staticString[] b = { "injectso" };staticString[] c = { "libxxghost.so" };staticFile d =null;staticFile a(File paramFile, Context paramContext) {if(paramFile.exists()) paramFile.delete(); paramFile.mkdirs(); paramFile.setReadable(true); paramFile.setWritable(true); paramFile.setExecutable(true);returnparamFile; } @SuppressLint({"NewApi"})staticBoolean a(File paramFile, String paramString, Context paramContext) {byte[] arrayOfByte =newbyte[4096]; File localFile =newFile(paramFile.getAbsolutePath(), paramString);if(localFile.exists()) localFile.delete();try{ InputStream localInputStream = paramContext.getResources().getAssets().open(paramString); FileOutputStream localFileOutputStream =newFileOutputStream(localFile);while(true) {inti = localInputStream.read(arrayOfByte);if(i == -1) { localFileOutputStream.flush(); localFileOutputStream.close(); localFile.setReadable(true); localFile.setWritable(true); localFile.setExecutable(true);returnBoolean.valueOf(true); } localFileOutputStream.write(arrayOfByte, 0, i); } }catch(Exception localException) { localException.printStackTrace(); }returnBoolean.valueOf(false); }publicstaticvoida(Context paramContext) { Log.d("NativeFileInstaller", "### install begin!"); d = a(b(paramContext), paramContext);inti = 0;intj = b.length;intk = 0;if(i >= j);while(true) {if(k >= c.length) {return;if(!a(d, b[i], paramContext).booleanValue()) Log.e("NativeFileInstaller", "Orz exe: " + b[i]); i++;break; }if(!a(d, c[k], paramContext).booleanValue()) Log.e("NativeFileInstaller", "Orz so: " + c[k]); k++; } }publicstaticFile b(Context paramContext) {returnnewFile(paramContext.getCacheDir(), a); }public static String c(Context paramContext) { return b(paramContext).getAbsolutePath(); }}
再连接上/injectso就是:/data/data/com.xxAssistant/cache/injecttest/injectso,后面格式化注入命令,也就是把/data/data/com.xxAssistant/lib/libxxghost.so注入进父进程并调用函数:loadResAndPlugin。由于叉叉助手里面也有Log输出,截获的log信息为:
/data/data/com.xxAssistant/cache/injecttest/injectso 165 /data/data/com.xxAssistant/lib/libxxghost.so loadResAndPlugin /data/app/com.xxAssistant-1.apk

判断是否注入成功Utility.isInjected():

如果注入成功则发送消息:paramHandler.sendEmptyMessage(78);
转换为十六进制:78 = 0x4E,在APKIDE里搜索0x4E,找到一处代码:
:sswitch_data_0 .sparse-switch0xa-> :sswitch_00xb-> :sswitch_10xc-> :sswitch_20xd-> :sswitch_30x15-> :sswitch_40x4d-> :sswitch_50x4e-> :sswitch_60x4f-> :sswitch_70x50-> :sswitch_80x51-> :sswitch_9 .end sparse-switch.end method
是在view/an.smali中,再在jd-gui中打开view/an的代码:
packagecom.xxAssistant.View;importandroid.app.LocalActivityManager;importandroid.content.Intent;importandroid.content.SharedPreferences;importandroid.content.SharedPreferences.Editor;importandroid.content.res.Resources;importandroid.os.Handler;importandroid.os.Message;importandroid.view.Window;importandroid.view.animation.AlphaAnimation;importandroid.view.animation.Animation;importandroid.view.animation.TranslateAnimation;importandroid.widget.FrameLayout;importandroid.widget.ImageView;importandroid.widget.RelativeLayout;importandroid.widget.TextView;importandroid.widget.Toast;importcom.b.a.a;importcom.xxAssistant.Utils.i;importcom.xxAssistant.h.b;importjava.util.HashMap;classanextendsHandler { an(MainActivity paramMainActivity) { }publicvoidhandleMessage(Message paramMessage) {switch(paramMessage.what) {default:case10:case11:case12:case13:do{do{do{doreturn;while(this.a.j == 0);this.a.l.setSelected(true); MainActivity.a(this.a).setSelected(true);this.a.c.setText(this.a.getResources().getString(2131230722)); MainActivity.m.setSelected(false); MainActivity.n.setSelected(false);this.a.o.setSelected(false); MainActivity.b(this.a).setSelected(false); MainActivity.c(this.a).setSelected(false); MainActivity.d(this.a).setSelected(false);this.a.b.removeAllViews();this.a.b.addView(MainActivity.e(this.a), MainActivity.f(this.a));this.a.e.setBackgroundResource(2130837691);this.a.j = 0;return; }while(this.a.j == 1);if((PluginActivity.k) && (PluginActivity.m == 1)) { TranslateAnimation localTranslateAnimation =newTranslateAnimation(PluginActivity.l, PluginActivity.l + PluginActivity.n, 0.0F, 0.0F); AlphaAnimation localAlphaAnimation4 =newAlphaAnimation(0.0F, 1.0F); localAlphaAnimation4.setDuration(0L); localTranslateAnimation.setFillAfter(true); localTranslateAnimation.setDuration(0L); PluginActivity.j.startAnimation(localAlphaAnimation4); PluginActivity.j.startAnimation(localTranslateAnimation); } MainActivity.m.setSelected(true); MainActivity.b(this.a).setSelected(true);this.a.c.setText(this.a.getResources().getString(2131230723));this.a.l.setSelected(false); MainActivity.n.setSelected(false);this.a.o.setSelected(false); MainActivity.a(this.a).setSelected(false); MainActivity.c(this.a).setSelected(false); MainActivity.d(this.a).setSelected(false);this.a.b.removeAllViews();if(MainActivity.g(this.a) ==null) MainActivity.a(this.a,this.a.getLocalActivityManager().startActivity("AllPluginActivity", MainActivity.h(this.a)).getDecorView());this.a.b.addView(MainActivity.g(this.a), MainActivity.f(this.a));this.a.e.setBackgroundResource(2130837676);this.a.j = 1;return; }while(this.a.j == 2); MainActivity.n.setSelected(true);this.a.c.setText(this.a.getResources().getString(2131230724)); MainActivity.m.setSelected(false); MainActivity.b(this.a).setSelected(true);this.a.l.setSelected(false);this.a.o.setSelected(false); MainActivity.a(this.a).setSelected(false); MainActivity.b(this.a).setSelected(false); MainActivity.d(this.a).setSelected(false);if(MainActivity.i(this.a) ==null) MainActivity.b(this.a,this.a.getLocalActivityManager().startActivity("QuanActivity", MainActivity.j(this.a)).getDecorView());this.a.b.removeAllViews();this.a.b.addView(MainActivity.i(this.a), MainActivity.f(this.a));this.a.e.setBackgroundResource(2130837676);this.a.j = 2;return; }while(this.a.j == 3); MainActivity.f.setVisibility(8); MainActivity.k(this.a).edit().putInt(MainActivity.A, MainActivity.A).commit();this.a.o.setSelected(true); MainActivity.d(this.a).setSelected(true);this.a.c.setText(this.a.getResources().getString(2131230725)); MainActivity.m.setSelected(false); MainActivity.n.setSelected(false);this.a.l.setSelected(false); MainActivity.a(this.a).setSelected(false); MainActivity.c(this.a).setSelected(false); MainActivity.b(this.a).setSelected(false);if(MainActivity.l(this.a) ==null) MainActivity.c(this.a,this.a.getLocalActivityManager().startActivity("MoreActivity", MainActivity.m(this.a)).getDecorView());this.a.b.removeAllViews();this.a.b.addView(MainActivity.l(this.a), MainActivity.f(this.a));this.a.e.setBackgroundResource(2130837676);if(b.m.size() != 0)if(MoreActivity.c !=null) MoreActivity.c.setVisibility(0);while(true) {this.a.j = 3;return;if(MoreActivity.c !=null) MoreActivity.c.setVisibility(4); }case21:if(this.a.j != 0) { Intent localIntent1 =newIntent(); a.a(this.a, "client_share"); i.a(this.a, 1008); localIntent1.setAction("android.intent.action.SEND"); localIntent1.setType("text/plain"); localIntent1.putExtra("android.intent.extra.SUBJECT",this.a.getResources().getString(2131230800)); localIntent1.putExtra("android.intent.extra.TEXT",this.a.getResources().getString(2131230801)); Intent localIntent2 = Intent.createChooser(localIntent1,this.a.getResources().getString(2131230800));this.a.startActivity(localIntent2);return; } Intent localIntent3 =newIntent(this.a, ToolActivity.class);this.a.startActivity(localIntent3);return;case77: AlphaAnimation localAlphaAnimation3 =newAlphaAnimation(0.0F, 1.0F); localAlphaAnimation3.setDuration(500L);this.a.g.setVisibility(0); localAlphaAnimation3.setAnimationListener(newao(this));this.a.g.startAnimation(localAlphaAnimation3);return;case78: AlphaAnimation localAlphaAnimation2 =newAlphaAnimation(1.0F, 0.0F);this.a.g.setText(this.a.getResources().getString(2131230761)); localAlphaAnimation2.setDuration(2000L); localAlphaAnimation2.setAnimationListener(newap(this));this.a.g.startAnimation(localAlphaAnimation2);return;case79: AlphaAnimation localAlphaAnimation1 =newAlphaAnimation(1.0F, 0.0F);this.a.g.setText(this.a.getResources().getString(2131230762)); localAlphaAnimation1.setDuration(1000L); localAlphaAnimation1.setAnimationListener(newaq(this));this.a.g.startAnimation(localAlphaAnimation1);return;case80: MainActivity.f.setVisibility(0);return;case81: } Toast.makeText(this.a, MainActivity.x, 1).show(); } }
这里是所有自定义消息的处理代码,其他的先不分析,找到78消息:
case78: AlphaAnimation localAlphaAnimation2 =newAlphaAnimation(1.0F, 0.0F);this.a.g.setText(this.a.getResources().getString(2131230761)); localAlphaAnimation2.setDuration(2000L); localAlphaAnimation2.setAnimationListener(newap(this));this.a.g.startAnimation(localAlphaAnimation2);return;
这里使用了一个id为2131230761的资源,转为十六进制是0x7F080029,在APKIDE中查找0x7F080029,搜索结果:
<publictype="string"name="inject_success"id="0x7f080029"/>
继续搜索inject_success,搜索结果:
<stringname="inject_success">获取root成功</string>
综上,r的静态a函数其实是一段负责注入的代码,“启动游戏”只是使用了是否注入成功的布尔值,并不曾调用这个静态的a函数,下面分析a是在何时被调用的。
思路:com.xxAssistant.Utils包下的r/a调用在smali语法下应该为:com/xxAssistant/Utils/r;->a,也就是说在smali代码中应该至少有一处invoke调用,格式为:com/xxAssistant/Utils/r;->a,
因此在APKIDE中搜索com/xxAssistant/Utils/r;->a,搜索结果:
这两处调用均在一个函数中执行,jd-gui打开com.xxAssistant.View.as.class:
packagecom.xxAssistant.View;importandroid.content.Intent;importandroid.content.SharedPreferences;importandroid.content.SharedPreferences.Editor;importcom.xxAssistant.DialogView.RebootWindowActivity;importcom.xxAssistant.Utils.Utility;importcom.xxAssistant.Utils.k;importcom.xxAssistant.Utils.q;importcom.xxAssistant.Utils.r;importcom.xxAssistant.Utils.v;importcom.xxAssistant.f.s;classasextendsThread { as(MainActivity paramMainActivity) { }publicvoidrun() { Utility.a("chmod 777 /data/data/com.xxAssistant/lib/*\n"); q.a(this.a);if(this.a.w.compareTo(this.a.v) != 0) { s.a(MainActivity.n(this.a)); k.a(); k.b();this.a.B.edit().putString("version",this.a.v).commit();if(Utility.isInjectFrameChanged("2.0")) { Intent localIntent =newIntent(this.a, RebootWindowActivity.class);this.a.startActivity(localIntent);if(Utility.doGetAndroidLoaderVersion().compareTo("2.0") < 0) MainActivity.o(this.a);return; } v.a(this.a);r.a(this.a,this.a.C);return; } v.a(this.a); r.a(this.a,this.a.C); } }
也就是说注入代码是在as线程中调用的,下面查找使用as的代码,同上述方法在APKIDE中搜索:com/xxAssistant/View/as; 搜索结果:
忽略com/xxAssistant/View/as.smali(本身),只剩下com\xxAssistant\View\MainActivity.smali
com\xxAssistant\View\MainActivity.smali中有引用,是在构造函数中:
.methodpublicconstructor <init>()V .locals 2 ……new-instance v0, Lcom/xxAssistant/View/as; invoke-direct {v0, p0}, Lcom/xxAssistant/View/as;-><init>(Lcom/xxAssistant/View/MainActivity;)V iput-object v0, p0, Lcom/xxAssistant/View/MainActivity;->F:Ljava/lang/Thread;return-void.end method
可以看出在MainActivity构造函数中new了一个as并赋给成员变量F,后面查找F的引用,相同方法在APKIDE中搜索:com/xxAssistant/View/MainActivity;->F
(这一次搜索勾选上匹配大小写),搜索结果:
其中第一个就是构造函数中的引用代码,这里查看第二个搜索结果,是在MainActivity的onCreate函数中启动此线程函数:
.methodprotectedonCreate(Landroid/os/Bundle;)V .locals 8 …… iget-object v0, p0, Lcom/xxAssistant/View/MainActivity;->F:Ljava/lang/Thread; invoke-virtual {v0}, Ljava/lang/Thread;->start()V invoke-direct {p0}, Lcom/xxAssistant/View/MainActivity;->g()V invoke-direct {p0}, Lcom/xxAssistant/View/MainActivity;->a()Vreturn-void……goto:goto_0 .end method

综上:在叉叉助手的MainActivity的构造函数中声明一个as线程,并在onCreate函数中启动as线程函数,as线程调用r.a进行注入。游戏启动时只负责按照常规方式启动游戏,猜测注入的so库对于APK启动有监控,监控规则则是通过重定向的公共配置文件来读取,下面着重分析下插件的配置文件,见下篇。

版权声明:本文为博主原创文章,未经博主允许不得转载。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics