一般情况下,Android手机用户在拿到新机后,可能会做以下三件事。
一、水货用户立马重新刷机,然后对手机进行ROOT,国行用户思考再三后也ROOT掉手机(Android手机软件运行在普通用户权限,程序对系统本身没有控制权。而所谓“ROOT掉手机”就是通过一些手段让Android手机可拥有ROOT权限,之后软件通过“ROOT权限”可以随意修改、访问手机中所有资源)
二、将手机ROM中自带的广告软件、无用软件精减掉来对手机进行优化
今天的测试在手机必须拥有ROOT权限下才能完成,这在现实大多数环境下是成立的。
程序分析
今天分析之前,先说说Dalvik反汇编代码的阅读。有朋友对我直接读Small文件的反编译代码这种方式有些不赞同,认为用JD-GUI工具来阅读DEX转的JAR包更方便,我个人认为,Dalvik 反汇编代码的阅读是必须要掌握的,因为DEX2JAR不是万能的,很多时候DEX2JAR就会失败无法生成JAR文件,使用JD-GUI读JAVA代码固然是好,但从基础上掌握反汇编的阅读也是很重要的。
.method protected onCreate(Landroid/os/Bundle;)V
.locals 4
.parameter
.prologue
.line 41
invoke-super {p0, p1},Lcom/tencent/mobileqq/app/BaseActivity;->onCreate(Landroid/os/Bundle;)V
#这里的父类也是TX自己编写的
.line 42
sput-object p0,Lcom/tencent/mobileqq/activity/SplashActivity;->instance:Lcom/tencent/mobileqq/activity/SplashActivity;
.line 43
invoke-virtual {p0},Lcom/tencent/mobileqq/activity/SplashActivity;->getIntent()Landroid/content/Intent;
.line 44
const/4 v0, 0x1
invoke-virtual {p0, v0},Lcom/tencent/mobileqq/activity/SplashActivity;->requestWindowFeature(I)Z
.line 45
invoke-virtual {p0},Lcom/tencent/mobileqq/activity/SplashActivity;->getWindow()Landroid/view/Window;
move-result-object v0
const/16 v1, 0x400
invoke-virtual {v0, v1},Landroid/view/Window;->addFlags(I)V
.line 46
const v0, 0x7f03007f
invoke-virtual {p0, v0},Lcom/tencent/mobileqq/activity/SplashActivity;->setContentView(I)V
#设置要显示View
.line 47
new-instance v0,Landroid/os/Handler;#??new一个Handler??
invoke-direct {v0},Landroid/os/Handler;-><init>()V
iput-object v0, p0,Lcom/tencent/mobileqq/activity/SplashActivity;->a:Landroid/os/Handler;#保存Handler
.line 52
iget-object v0, p0,Lcom/tencent/mobileqq/activity/SplashActivity;->a:Landroid/os/Handler;
iget-object v1, p0,Lcom/tencent/mobileqq/activity/SplashActivity;->a:Ljava/lang/Runnable;
const-wide/16 v2, 0x7d0 #延迟的时间间隔
invoke-virtual {v0, v1,v2, v3}, Landroid/os/Handler;->postDelayed(Ljava/lang/Runnable;J)Z
#??延迟启动一个线程进行登陆操作??
.line 54
invoke-virtual {p0},Lcom/tencent/mobileqq/activity/SplashActivity;->getApplication()Landroid/app/Application;#获取Application
move-result-object p0
.line 55
.line 56
move-result-object v0#调用a()方法并比较结果,用户是否获取成功
if-eqz v0, :cond_0
move-result-object v0
invoke-virtual {v0},Lcom/tencent/qphone/base/remote/SimpleAccount;->getSid()Ljava/lang/String;
move-result-object v0#getSid()获取SID自动登陆字符串
if-eqz v0, :cond_0
.line 58
move-result-object v0
invoke-virtual {v0},Lcom/tencent/qphone/base/remote/SimpleAccount;->getUin()Ljava/lang/String;
move-result-object v0
invoke-static {v0},Lcom/tencent/mobileqq/log/ReportLog;->setUserUin(Ljava/lang/String;)V
.line 59
move-result-object v0
invoke-virtual {v0},Lcom/tencent/qphone/base/remote/SimpleAccount;->getSid()Ljava/lang/String;
move-result-object v0
invoke-virtual {v0},Ljava/lang/String;->getBytes()[B
move-result-object v0
invoke-static {v0},Lcom/tencent/mobileqq/log/ReportLog;->setSig([B)V
#ReportLog记录结果,到这里用户就可以算是自动登陆了
.line 65
:cond_0
invoke-static {},Lcom/tencent/qphone/base/util/LoginHelper;->getCommonConfig()Ljava/lang/String;
move-result-object v0 #听名字是获取通用配置,跟踪发现是JNI调用获取信息,这里可以不跟
.line 66
invoke-virtual {v0, v1},Ljava/lang/String;->indexOf(Ljava/lang/String;)I
move-result v1
.line 67
invoke-virtual {v0, v2},Ljava/lang/String;->indexOf(Ljava/lang/String;)I
move-result v2
.line 69
if-ltz v1, :cond_1
if-le v2, v1, :cond_1
.line 70
invoke-virtual {v3},Ljava/lang/String;->length()I
move-result v3
add-int/2addr v1, v3
invoke-virtual {v0, v1,v2}, Ljava/lang/String;->substring(II)Ljava/lang/String;
move-result-object v0
invoke-virtual {v0},Ljava/lang/String;->trim()Ljava/lang/String;
move-result-object v0
.line 71
sput-object v0,Lcom/tencent/mobileqq/log/ReportLog;->URL_LOG_UPLOAD:Ljava/lang/String;
.line 72
move-result-object v0
invoke-static {v0, p0},Lcom/tencent/mobileqq/log/ReportLog;->upload(Lcom/tencent/mobileqq/utils/httputils/HttpCommunicator;Landroid/content/Context;)V
.line 74
:cond_1
const/4 v0, 0x0
const-string v1,"SplashActivity onCreate()"
invoke-static {v0, v1},Lcom/tencent/mobileqq/log/ReportLog;->appendLog(Ljava/lang/String;Ljava/lang/String;)V
.line 81
return-void
.end method
在OnCreate()方法中,创建了一个Handler对象,Handler中加入了一个新线程,其实Handler与主线程使用了同一线程,所以在这里主线程就转去执行它的Run()方法了,新线程的实现代码在“ly.smali”文件中,打开这个文件看Run()方法:
.method public final run()V
.locals 5
.prologue
const-wide/16 v3, 0x0
.line 107
sget-object v0,Lcom/tencent/mobileqq/activity/NotificationActivity;->instance:Lcom/tencent/mobileqq/activity/NotificationActivity;
iget-object v0, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity; #获取SplashActivity对象
move-result-object v0
if-nez v0, :cond_2
.line 108
iget-object v0, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity;
const/4 v2, 0x0
move-result-object v0
.line 110
const-string v1,"firstTimeRun"
invoke-interface {v0, v1,v3, v4}, Landroid/content/SharedPreferences;->getLong(Ljava/lang/String;J)J
move-result-wide v1 #获取首次启动时间
.line 112
cmp-long v1, v1, v3 #判断配置文件中的启动时间是否为空
if-nez v1, :cond_0
.line 113
invoke-interface {v0},Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor;#准备写入数据
move-result-object v0
.line 114
const-string v1,"firstTimeRun"
const-wide/16 v2, 0x1
invoke-interface {v0, v1,v2, v3},Landroid/content/SharedPreferences$Editor;->putLong(Ljava/lang/String;J)Landroid/content/SharedPreferences$Editor;
#写入首次启动时间
.line 115
invoke-interface {v0}, Landroid/content/SharedPreferences$Editor;->commit()Z#提交修改
.line 116
iget-object v0, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity;
invoke-static {v0},Lcom/tencent/mobileqq/activity/SplashActivity;->access$100(Lcom/tencent/mobileqq/activity/SplashActivity;)V
.line 118
:cond_0
iget-object v0, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity;
invoke-virtual {v0},Lcom/tencent/mobileqq/activity/SplashActivity;->finish()V #结束闪屏
.line 119
new-instance v0, Landroid/content/Intent;
iget-object v1, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity;
const-class v2,Lcom/tencent/mobileqq/activity/HomeActivity;
invoke-direct {v0, v1,v2}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
#构造一个HomeActivity对象
.line 120
iget-object v1, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity;
invoke-virtual {v1},Lcom/tencent/mobileqq/activity/SplashActivity;->getIntent()Landroid/content/Intent;
move-result-object v1
const-string v2,"selfuin"
invoke-virtual {v1, v2},Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
.line 121
invoke-static {v1},Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z
move-result v1
if-nez v1, :cond_1
.line 123
iget-object v1, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity;
invoke-virtual {v1},Lcom/tencent/mobileqq/activity/SplashActivity;->getIntent()Landroid/content/Intent;
move-result-object v1
invoke-virtual {v0, v1},Landroid/content/Intent;->putExtras(Landroid/content/Intent;)Landroid/content/Intent;
.line 125
:cond_1
iget-object v1, p0,Lly;->a:Lcom/tencent/mobileqq/activity/SplashActivity;
invoke-virtual {v1, v0},Lcom/tencent/mobileqq/activity/SplashActivity;->startActivity(Landroid/content/Intent;)V#启动HomeActivity
.line 128
:cond_2
return-void
.end method
.line 166
:cond_a
new-instance v0,Landroid/content/Intent;
const-string v1,"com.tencent.mobileqq.action.LOGIN"
invoke-direct {v0, v1},Landroid/content/Intent;-><init>(Ljava/lang/String;)V
const/high16 v1, 0x4
invoke-virtual {v0, v1},Landroid/content/Intent;->addFlags(I)Landroid/content/Intent;
move-result-object v0
const/16 v1, 0x3e8
invoke-virtual {p0, v0,v1}, Lcom/tencent/mobileqq/activity/HomeActivity;->startActivityForResult(Landroid/content/Intent;I)V
goto :goto_3
在“AndroidMenifest.xml”文件中可以发现 "com.tencent.mobileqq.action.LOGIN"对应的就是LoginActivity,打开“LoginActivity.smali”文件,直接看登陆按钮监听器代码吧,同样,代码老长的,只上关键部分:
.method public onClick(Landroid/content/DialogInterface;I)V
.locals 5
.parameter
.parameter
.prologue
const/4 v1, -0x1
.line 811
iget v0, p0,Lcom/tencent/mobileqq/activity/LoginActivity;->a:I
if-eq v0, v1, :cond_3
…………
.line 836
:cond_2
invoke-virtual {v0},Lhf;->notifyDataSetChanged()V
.line 837
iget-object v0, p0,Lcom/tencent/mobileqq/activity/LoginActivity;->e:Landroid/widget/CheckBox;
invoke-virtual {v0},Landroid/widget/CheckBox;->isChecked()Z
move-result v0
if-eqz v0, :cond_3
.line 838
.line 839
invoke-virtual {v0, v2},Lcom/tencent/mobileqq/persistence/EntityManagerFactory;->build(Ljava/lang/String;)Landroid/database/sqlite/SQLiteOpenHelper;
move-result-object v1
invoke-virtual {v1},Landroid/database/sqlite/SQLiteOpenHelper;->getWritableDatabase()Landroid/database/sqlite/SQLiteDatabase;
move-result-object v1
.line 840
const-string v2,"select name from sqlite_master where type=\"table\" and namelike \"mr_%\""
const/4 v3, 0x0
invoke-virtual {v1, v2,v3}, Landroid/database/sqlite/SQLiteDatabase;->rawQuery(Ljava/lang/String;[Ljava/lang/String;)Landroid/database/Cursor;
move-result-object v2
.line 844
:goto_0
invoke-interface {v2},Landroid/database/Cursor;->moveToNext()Z
move-result v3
if-eqz v3, :cond_4
.line 845
const/4 v3, 0x0
invoke-interface {v2, v3},Landroid/database/Cursor;->getString(I)Ljava/lang/String;
move-result-object v3
.line 846
invoke-static {v3},Lcom/tencent/mobileqq/persistence/TableBuilder;->dropSQLStatement(Ljava/lang/String;)Ljava/lang/String;
move-result-object v3
invoke-virtual {v1, v3},Landroid/database/sqlite/SQLiteDatabase;->execSQL(Ljava/lang/String;)V
goto :goto_0
:catch_0
move-exception v0
.line 856
:cond_3
:goto_1
return-void
.line 848
:cond_4
new-instance v2,Lcom/tencent/mobileqq/data/RecentUser;
invoke-direct {v2},Lcom/tencent/mobileqq/data/RecentUser;-><init>()V
invoke-virtual {v2},Lcom/tencent/mobileqq/data/RecentUser;->getTableName()Ljava/lang/String;
move-result-object v2
invoke-static {v2},Lcom/tencent/mobileqq/persistence/TableBuilder;->dropSQLStatement(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2
invoke-virtual {v1, v2},Landroid/database/sqlite/SQLiteDatabase;->execSQL(Ljava/lang/String;)V
.line 850
invoke-virtual {v0},Lcom/tencent/mobileqq/persistence/EntityManagerFactory;->close()V
:try_end_0
.catchLjava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
goto :goto_1
.end method
这段代码注释我就不写了,大家自己看看也能明白,它在执行一条“select name from sqlite_master where type=\"table\" andname like \"mr_%\"”SQL语句,作用就是查询表中所有以“mr_”打头的表名,这在Sqlite命令行下输入“.tables mr_%”功能是一样的。看来,TX保存信息是使用的Sqlite.。
分析到这里,有必要对Android的存储机制先有些简单的了解。在Android中,信息的存储有以下几种,第一类存储为普通的文件存储,通过JAVA提供的IO库对文件读取与写入,这与其它的语言或平台是一样的。显然,由于它直接将数据暴露给用户,这种存储机制的安全性是最低的。第二类是Android中提供的一个私有数据存储类“SharedPerferences”,它提供了简单的键值对来对数据进行的存储,上次分析的360手机卫士就是用这种方法保存数据,这个类存储的数据只能被软件自身访问(ROOT过的手机除外),对数据安全性本身进行了加强。第三类是SQLite数据库存储,有过开发经验的朋友可能都认识它,它是一个轻量级的关系型数据库,现在各大平台上应用都很广泛了,在Android系统中,它作为了一个标准数据库的存在。第四类是ContentProvider方式存储,在第二类与第三类数据在存储的数据都是程序私有的,然而新的问题是两个或多个程序有时候可能需要进行数据交换,如同一个公司产品的无缝结合等,解决这个问题就要靠ContentProvider了,一个ContentProvider接口实现了一套标准的方法接口,应用程序可以通过实现ContentProvider接口将自己的数据暴露出去,这样就做到了选择性的数据开放。第五类就是网络存储了,也就是通过网络来实现对数据的存储与获取,此类存储一般作为软件的附加功能的多。