应广大网友的要求,笔者近期对乐安全的软件锁进行了分析,下面简单就一篇博文向大家介绍一下乐安全软件锁的实现原理。测试的乐安全版本为V4.2.4。
先说下结论,如果有兴趣的同学可以继续往下看分析。乐安全在本地进程得到了AMS的Bp,直接调用其getTasks方法,每隔一秒检测一下当前topActivity是否是需要被锁定的,如果是的话,就弹出一个解锁界面盖住本该启动的APP。
下面进行分析
首先要爆料的是,乐安全做的实在是有点不小心,打了些对于破解有用的log出来,降低了破解的难度。比如下面的:
这是在触发软件锁的时候打出的log,给了我们启发,通过反编译APK文件后,我们很快定位到了相关的代码。
while (AppCheck.isRun)
try
{
Thread.sleep(1000L);
List localList1 = AppCheck.a(AppCheck.this).getTasks(1, 0, null);
if (localList1.size() > 0)
{
AppCheck.a(AppCheck.this, ((ActivityManager.RunningTaskInfo)localList1.get(0)).topActivity.getPackageName());
if (!AppCheck.c(AppCheck.this).equals(AppCheck.b(AppCheck.this)))
{
Log.i("AppCheck", "old" + AppCheck.c(AppCheck.this) + "------------new:" + AppCheck.b(AppCheck.this));
String[] arrayOfString3 = new String[3];
arrayOfString3[0] = "runningTask";
arrayOfString3[1] = AppCheck.b(AppCheck.this);
arrayOfString3[2] = AppCheck.c(AppCheck.this);
a(arrayOfString3);
AppCheck.b(AppCheck.this, AppCheck.b(AppCheck.this));
}
}
可以看到,乐安全在这里做了一个循环操作,首先sleep一秒,然后直接调用getTasks()方法得到一个List。我们先看下AppCheck.a(AppCheck.this)返回的是什么。由于jd反编译的不全面,下面要po上一个smali的代码片段。
.method static synthetic a(Lcom/lenovo/safecenter/utils/AppCheck;)Landroid/app/IActivityManager;
.locals 1
.parameter "x0"
.prologue
.line 21
iget-object v0, p0, Lcom/lenovo/safecenter/utils/AppCheck;->a:Landroid/app/IActivityManager;
return-object v0
.end method
这里可以看到a(AppCheck.this)返回的是一个IActivityManager的对象。这里可能大家会有一个疑问,IActivityManager是hide的,为什么可以在这里被调用。@安卓安全小分队的大牛讲师认为乐安全很有可能自己编译了一套framework.jar使得乐安全在编译阶段顺利通过,并且在手机上也能正常运行,关于这一部分的机理,如果大家觉得有必要,我们可以在以后的时间作为一个专题来讲解。
在这里笔者对之前的猜测做下更正,乐安全并不是通过注入方式来调用AMS的一个方法,而是简单的在本地得到了AMS的Bp,也就是这个IActivityManager的对象。下面再po一个smali的代码段来说明这个IActivityManager是如何得到的。
.line 38
invoke-static {}, Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager;
move-result-object v0
iput-object v0, p0, Lcom/lenovo/safecenter/utils/AppCheck;->a:Landroid/app/IActivityManager;
通过直接调用ActivityManagerNative的getDefault静态方法就获得了这个IActivityManager对象,很简单。
好,回到前面的分析阶段。我们已经知道了,乐安全通过调用AMS的getTasks方法得到了tasks的list,到了第一if分支(参考前面贴出的代码)。
首先检查的是新启动的activity是否与前一个activity属于同一包。说白了就是如果用户已经在使用敏感APP了,乐安全就不要再去搞软件锁了,不然用户启动一个activity就有一锁出来不的把人烦死。
但是如果不属于同一个包,就进入了这个分支,先打了个被我们抓住尾巴的log,然后把old的包名和new的报名放进数组交给了a()方法。我们看看a()里都做了什么
private void a(Object[] paramArrayOfObject)
{
if (paramArrayOfObject[0].equals("runningTask"));
for (String str = "com.lenovo.safecenter.activityswitch"; ; str = "com.lenovo.safecenter.activityswitch2")
{
Log.i("AppCheck", paramArrayOfObject[0] + "------------action:" + str + " " + paramArrayOfObject[1] + " " + paramArrayOfObject[2]);
Intent localIntent = new Intent(str);
localIntent.putExtra("newPkg", (String)paramArrayOfObject[1]);
localIntent.putExtra("oldPkg", (String)paramArrayOfObject[2]);
AppCheck.g(AppCheck.this).sendBroadcast(localIntent);
Settings.System.putString(AppCheck.g(AppCheck.this).getContentResolver(), "safe_input_method", "com.android.inputmethod.latin/.LatinIME");
Log.i("AppCheck", paramArrayOfObject[0] + "------------end");
return;
}
}
很简单,把old package和new package用广播给发出去了。我们到接收端看一下如何处理。
乐安全的assets包里有一个LenovoSafeBox429.apk,打开后在里面发现一个MonitorAppService的类,他在onCreate的时候注册了一个BroadcastReceiver,接收名为com.lenovo.safecenter.activityswitch的action如下。
public void onCreate()
{
this.getBroadcast = false;
this.mContext = this;
this.pattern = Pattern.compile("(.*)(\\={1})(.*)(//*)");
this.runtime = Runtime.getRuntime();
this.filter = new IntentFilter();
this.filter.addAction("android.intent.action.SCREEN_OFF");
this.filter.addAction("android.intent.action.SCREEN_ON");
this.filter.addAction("com.lenovo.safecenter.activityswitch");
this.filter.addAction("com.lenovo.safebox.lockscreen");
this.lastPkg = "";
registerReceiver(this.receiver, this.filter);
if (canGetPassword())
{
if (!this.watchAppOpen)
watchAppStart();
if (!this.getBroadcast)
{
this.mTimer = new Timer(true);
restartTimer();
}
}
在receiver的onReceive函数里,这个receiver做了下面的事情。我们捡主要的说
label498: if ((MonitorAppService.this.lastPkg == null) || (MonitorAppService.this.lastPkg.equals("com.lenovo.safebox")) || (MonitorAppService.this.lastPkg.equals(str)) || (!MonitorAppService.this.checkList.contains(str)) || (MonitorAppService.isLocked))
continue;
Intent localIntent = new Intent(MonitorAppService.this.mContext, LockScreenActivity.class);
localIntent.putExtra("pkg", str);
localIntent.addFlags(268435456);
if (MonitorAppService.DEBUG)
Log.i("MonitorAppService", "Lock in Broadcast TopTask :" + str);
MonitorAppService.this.mContext.startActivity(localIntent);
关键的位置就是到一个list里查看当前启动的package是不是我要锁住的,如果是锁住的,那就直接启动一个activity盖在上面,就是我们看到的效果了。
PS:有的网友提到乐安全阻止了程序的正常启动,相关进程不会被启动。但是通过我们的观察,进程是已经被启动了的,如下图。
需要关注的是,AMS首先启动了被锁软件。Start了它的activity以及process(最开始的两条log),然后才会执行我们之前分析的部分,然后LockScreenActivity才被启动。这个时候可以通过ps命令查看当前进程。我们发现了被锁软件:
新浪微博@安卓安全小分队