查看原文
其他

App隐藏大师绕过密码与多开分析

挤蹭菌衣 看雪学院 2021-03-07
本文为看雪论坛优秀文章
看雪论坛作者ID:挤蹭菌衣



准备工作jadx反编译另存为gradle导入as,打开monitor方便方法回溯,启动frIDA-server以便hook。

菜鸟最近看到一个App隐藏大师,可以隐藏App和相册图片等,隐身模式是一个绿色的计算器,输入正确的密码后会跳转到隐藏App的页面,感觉很神奇,想分析一下密码认证功能实现的函数和App的创建实现。

首先分析跳转界面,输入正确6位密码后可以直接跳转,否则调用计算功能进行计算,看起来就像个真实的计算器,哈哈,猜测有一个跳转函数,当输入数字为6位时,调用认证函数进行认证,然后通过新建Activity跳转到主界面。

然后,我就走到了岔路,我不断的搜索intent相关,想找到切换 Activity 的地方,找了好几个地方都不对,最终我只能进行方法回溯,寻找关键函数,一步一步追溯源头。


一、追溯密码认证函数


很明显,当text改变时就会进行判断,所以必定有一个对于text监听的afterTextChanged的调用,直接搜索,定位到com.prism.hider.vault.calculator.Calculator$1.afterTextChanged。

public void afterTextChanged(Editable editable) { if (Calculator.this.f6590w && editable != null && C3154h.m10228a(editable.length()) && PinCodeCertifier.m10216a((Context) Calculator.this).mo23413a(editable.toString())) { Calculator.this.m10160b(); } Calculator.this.m10155a(CalculatorState.INPUT); Calculator.this.f6588u.mo23401a((CharSequence) editable, (C3137a) Calculator.this);} public boolean mo23413a(String str) {//这个判断密码,f6652c就是我们设的密码 return this.f6652c != null && this.f6652c.equals(EncodeUtils.m4661a(str)); }

这句话就是判断密码调用的地方,如果忘记密码,直接hook这个函数,修改返回值,就可以绕过密码验证了。当然如果有人安装这个app也可以通过hook做坏事,嘿嘿。

Calculator.this.f6590w && editable != null// 判断有没有输入
&& C3154h.m10228a(editable.length())//判断输入数字长度
&& PinCodeCertifier.m10216a((Context) Calculator.this).mo23413a(editable.toString())// mo23413a 函数判断密码是否正确


二、追溯界面切换原理


下面定位 m10160b和它的下层函数m10167e, 分析它是如何通过密码实现界面切换的。

com.prism.hider.vault.calculator.Calculator.b()V = m10160b public void m10160b() { if (getIntent() == null || getIntent().getExtras() == null || !getIntent().getExtras().getBoolean(f6568a)) { m10167e(); return; } Log.d(f6570j, getIntent().toString()); finish();} private void m10167e() { String str = f6570j; StringBuilder sb = new StringBuilder(); sb.append("GlobalVaultListener :"); sb.append(GlobalVaultListener.f6648a); Log.d(str, sb.toString()); if (GlobalVaultListener.f6648a != null) { GlobalVaultListener.f6648a.mo23188c(this);//mo23188c是一个接口实现,好像跟intent无关 } else { Log.d(f6570j, "GlobalVaultListener Not Found"); } this.f6592y = false; finish();}

看了这2个函数,我有点慌,怎么会没有intent切换activity呢,那他是怎么实现界面切换的呢,我当时在这卡了半天,没有找到通过intent启动SplashActivity的地方,又去查了activity堆栈,还是没搞明白。

难道这个 SplashActivity 主界面在计算器之前已经启动了,如果判断密码正确,通过finish()退出计算器来到 SplashActivity 吗,那样的话,hook调mo23413a函数直接返回true是通过退出进入 SplashActivity 了。

带着疑问,我再次方法回溯,果然,在系统函数performCreate里,同时调用了Calculator和SplashActivity的oncreate方法,也就是说,没有输入密码的时候 SplashActivity已经启动了,会玩,里面的app其实根本就没有加密,密码只是让你绕过了计算器而已。




三、追溯App多开原理


后面我又简单跟了一下,它安装app的方法跟一般的双开应用一样,修改了包名,安装的第一个app的界面是Com.prism.gaia.client.stub.GuestActivityStub$Guest0,后面分别是1234等等。

创建多开应用,并且用方法回溯追踪,从onclick追溯到com.prism.hider.b.l.a(Landroid/view/View;Lcom/android/launcher3/AppInfo;Lcom/android/launcher3/Launcher;Landroid/content/DialogInterface;)V = m9886a这个关键函数。

private void m9886a(View view, AppInfo appInfo, Launcher launcher, DialogInterface dialogInterface) { AppInfo appInfo2 = appInfo; Launcher launcher2 = launcher; Workspace workspace = launcher.getWorkspace(); int[] iArr = new int[2]; long[] jArr = {-1}; Iterator it = workspace.getScreenOrder().iterator(); while (true) { if (!it.hasNext()) { break; } long longValue = ((Long) it.next()).longValue(); String str = f6338a; StringBuilder sb = new StringBuilder(); sb.append("screenid:"); sb.append(longValue); Log.d(str, sb.toString()); if (launcher2.getCellLayout((long) -100, longValue).findCellForSpan(iArr, 1, 1)) { jArr[0] = longValue; break; } } if (jArr[0] < 0) { workspace.addExtraEmptyScreen(); jArr[0] = workspace.commitExtraEmptyScreen(); } long j = (long) -100; CellLayout cellLayout = launcher2.getCellLayout(j, jArr[0]); String str2 = f6338a; StringBuilder sb2 = new StringBuilder(); sb2.append("targetScreen:"); sb2.append(jArr[0]); sb2.append(" targetx:"); sb2.append(iArr[0]); sb2.append(" targety:"); sb2.append(iArr[1]); Log.d(str2, sb2.toString()); ShortcutInfo makeShortcut = new PromisedGuestAppInfo(appInfo2).makeShortcut();//这里应该就是通过appInfo2创建新的app与shortcut图标 View createShortcut = launcher2.createShortcut(cellLayout, makeShortcut);//把创建的shortcut图标放到launcher2,也就是自己的launch里 launcher.getModelWriter().addOrMoveItemInDatabase(makeShortcut, j, jArr[0], iArr[0], iArr[1]); makeShortcut.container = j; makeShortcut.cellX = iArr[0]; makeShortcut.cellY = iArr[1]; workspace.addInScreen(createShortcut, makeShortcut); GuestAppStateExtension.m9874a().mo23238b().mo23268a(appInfo2); dialogInterface.dismiss(); workspace.post(new Runnable(jArr) { private final /* synthetic */ long[] f$1; { this.f$1 = r2; } public final void run() { Workspace.this.moveToScreen(this.f$1[0]); } });}

容易看出new PromisedGuestAppInfo(appInfo2).makeShortcut()这句应用双开前的appinfo创建新的 PromisedGuestAppInfo 并创建 Shortcut图标,launcher2.createShortcut(cellLayout, makeShortcut)这句把创建的shortcut图标放到launcher2,也就是自己的launch里。

这里由于点击了好几个按钮,lamda函数套了几层,贴一下堆栈。

com.prism.hider.b.l.a(Native Method)com.prism.hider.b.l.a(ItemClickHandlerExtensionImpl.java:205)com.prism.hider.b.l.lambda$1v3PIwaoDBWd6OM-CRLs-RNzmUQ(Unknown Source:0)com.prism.hider.b.-$$Lambda$l$1v3PIwaoDBWd6OM-CRLs-RNzmUQ.onClick(Unknown Source:10)com.prism.hider.ui.a.a(AppOperateDialog.java:93)com.prism.hider.ui.a.lambda$OTuIeTAl3vf4RmjQk_3g0AXegkk(Unknown Source:0)com.prism.hider.ui.-$$Lambda$a$OTuIeTAl3vf4RmjQk_3g0AXegkk.onClick(Unknown Source:2)android.view.View.performClick(View.java:6294)android.view.View$PerformClick.run(View.java:24770)android.os.Handler.handleCallback(Handler.java:790)android.os.Handler.dispatchMessage(Handler.java:99)android.os.Looper.loop(Looper.java:164)android.app.ActivityThread.main(ActivityThread.java:6494)java.lang.reflect.Method.invoke(Native Method)com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:440)com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

我们再看一下他的构造函数,很明显就是改了包名和组件名实现多开,就是在之前附加一个gaia_guest_字符串,然后把组件添加进去,结束。

public PromisedGuestAppInfo(AppInfo appInfo) { super(appInfo); this.packageName = ExtensionPkgUtils.m9976a(appInfo.packageName);// m9976a修改包名和组件名 this.componentName = new ComponentName(ExtensionPkgUtils.m9976a(this.componentName.getPackageName()), this.componentName.getClassName()); this.intent = new Intent("android.intent.action.MAIN").addCategory("android.intent.category.LAUNCHER").setComponent(this.componentName).setFlags(270532608);} private static final String f6410b = "gaia_guest_."; public static String m9976a(String str) { StringBuilder sb = new StringBuilder(); sb.append(f6410b);// f6410b = "gaia_guest_."; sb.append(str); return sb.toString();}

总结一下,经过这次逆向,学习了这个软件的思路:

1. App入口同时oncreate 2个activity,计算器或者其他干扰界面放在真正的界面前面,输入密码错误就执行干扰界面app功能,输入密码正确就finish干扰界面,这样真正的界面就显示出来。比如说,这里用计算器做干扰界面,你可以用bmi计算器或者单位换算器做干扰界面,这样认识这个软件的人也判断不出来了哈哈。

2. 多开软件原理,复制安装原App,然后把包名和组件名都改了,系统就会认为这是2个软件了,然后把图标放到真正界面的launcher,实现隐藏。




- End -




看雪ID:挤蹭菌衣

https://bbs.pediy.com/user-877073.htm 

*本文由看雪论坛 挤蹭菌衣 原创,转载请注明来自看雪社区。




推荐文章++++

VC黑防日记:DLL隐藏和逆向

VC黑防日记:DLL隐藏和逆向(续)

**游戏逆向分析笔记

对宝马车载apps协议的逆向分析研究

x86_64架构下的函数调用及栈帧原理



好书推荐







公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



“阅读原文”一起来充电吧!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存