作者:非虫
随着电子商务在国内的迅猛发展,网上购物也成为了时下流行的消费方式。就我个人来说,每年在淘宝上也会购物上百起。这足不出户的购物方式的确给我们的生活带来了不少实惠与方便,但同时,购物安全也成为了广大网购消费者担心的一个问题。每年在新闻中爆光的网银被盗、被骗的事件也屡见不鲜。
长期使用网络客户端软件的朋友都有一个习惯,为了避免每次使用时输入帐号名和密码,都习惯使用软件的自动保存密码功能来记住登录密码,这样下次直接点击登录按钮就可以登录软件了(有些软件直接跳过了登录确认的界面),这样,一个安全问题就出来了,软件为我们保存的密码是明文存储的吗?如果加密了,加密强度怎样?外部用户可以直接破解吗?试想一下,像支付宝这类敏感的网银软件,如果本地存储的密码被人直接破解,那后果是很难预料的!而随着我对支付宝程序的逆向分析,也证明了这个安全问题确实存在。在此申明:以下文章涉及的代码与分析内容仅供安卓系统安全学习交流,任何个人或组织不得使用文中提到的技术做违法犯罪活动,否则由此引发的任何后果与法律责任本人概不负责。
测试环境
一台安装有支付宝的安卓手机,并且能获得ROOT权限。
支付宝的版本为3.4.0.0229。
程序运行后使用了自动保存密码功能。为了测试更详细,我分别保存了支付宝与淘宝的帐号密码。
程序分析
医生给病人看病步骤讲究的是望闻问切,通过查看病人的面貌体态来对病情做初步判断,我们今天的分析也一改以往的埋头分析,采用类似的方法,先看看软件运行后的“症状”。打开支付宝软件,点击右上角的登录按钮,分别使用淘宝与支付宝帐号登录,勾选上自动保存密码,如图1所示:
图1
退出软件,然后重新登录,发现软件的确记住了保存的密码,而且密码框显示“星星”的位数与我实际的密码位数一样,看到这里我立马来劲了!这说明密码肯定是保存在本地的某个文件中,而且程序启动时读取密码,然后设置到密码框中。
下面,请出DDMS,进入支付宝数据目录“/data/data/com.eg.android.AlipayGphone/”,里面的文件结构如图2所示:
图2
在DateBases目录里有个RecentDB文件,初步判断它是使用的Sqlite3保存的数据库,将该文件导出到D盘根目录,进行命令行,使用AndroidSDK里面的Sqlite3.exe打开该数据库并分别执行“.tables”、“.schema RecentTable2”、“select * from RecentTable2;”命令,执行后结果如图3所示:
图3
这不看不知道,一看真吓一跳,原来帐号名与密码直接保存在了这个数据库中!只是密码被加过密,看后面的“==”还以为是Base64,可测试后发现不是,退出支付宝程序将它卸载并重新安装。重新运行软件一次后退出,将刚才导出的“RecentDB”文件导入,再次运行支付宝后发现软件登录框中帐号名与密码都记住了!接下来为手机换一张电话号码卡,重新进入程序发现密码框为空了。同样,在其它安卓手机上安装支付宝后导入RecentDB文件,密码框也为空,看来,支付宝对使用者手机与电话号码有所判断。
使用ApkTool将支付宝APK安装文件解包,打开“AndroidManifest.xml”文件,将“android:name”一栏的android:debuggable="false"改成android:debuggable="true",然后重新编译签名并安装,打开DDMS,在LogCat中新建一栏,设置“Filter Name”与“By Application Name”为“com.eg.android.AlipayGphone”,如图4所示:
图4
启动程序,这时就可以在DDMS中查看支付宝的Log输出了,运行程序后点击登录按钮进入到登录界面,在界面随意处点击几下,发现拦截到的消息如图5所示:
图5
由Log输出信息得知程序所在的Activity为“com.alipay.android.client.Login”,在反编译出的Smali文件夹中找到“Login.smali”文件并查看OnCreate()方法。代码太长,只帖关键部分:
.method public onCreate(Landroid/os/Bundle;)V
.locals 6
const/4 v5, 0x1
const/4 v4, 0x0
invoke-super {p0, p1},Lcom/alipay/android/client/RootActivity;->onCreate(Landroid/os/Bundle;)V
invoke-static {p0},Lcom/alipay/android/appHall/h;->a(Landroid/app/Activity;)Z
new-instance v0,Lcom/alipay/platform/a/b;
............
const-string v1, "logintype"
invoke-virtual {v0, v1},Lcom/alipay/android/client/a/o;->a(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
const-string v1,"taobao" #判断登录类型
invoke-virtual {v0, v1},Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_3
iput-boolean v5, p0, Lcom/alipay/android/client/Login;->k:Z
iput-boolean v4, p0,Lcom/alipay/android/client/Login;->l:Z
iget-object v0, p0,Lcom/alipay/android/client/Login;->K:Landroid/widget/Button;
invoke-virtual {v0, v4},Landroid/widget/Button;->setSelected(Z)V
iget-object v0, p0,Lcom/alipay/android/client/Login;->L:Landroid/widget/Button;
invoke-virtual {v0, v5},Landroid/widget/Button;->setSelected(Z)V
iget-object v0, p0,Lcom/alipay/android/client/Login;->v:Landroid/widget/AutoCompleteTextView;
const v1, 0x7f0a0010
invoke-virtual {v0, v1},Landroid/widget/AutoCompleteTextView;->setHint(I)V #设置输入框的提示
:goto_0
invoke-direct {p0},Lcom/alipay/android/client/Login;->d()V #☻关键方法
iget-object v0, p0,Lcom/alipay/android/client/Login;->D:Landroid/widget/CheckBox;
new-instance v1,Lcom/alipay/android/client/cx;
invoke-direct {v1, p0},Lcom/alipay/android/client/cx;-><init>(Lcom/alipay/android/client/Login;)V
invoke-virtual {v0, v1},Landroid/widget/CheckBox;->setOnClickListener(Landroid/view/View$OnClickListener;)V
return-void
:cond_3
iput-boolean v4, p0,Lcom/alipay/android/client/Login;->k:Z
iput-boolean v5, p0,Lcom/alipay/android/client/Login;->l:Z
iget-object v0, p0,Lcom/alipay/android/client/Login;->K:Landroid/widget/Button;
invoke-virtual {v0, v5},Landroid/widget/Button;->setSelected(Z)V
iget-object v0, p0,Lcom/alipay/android/client/Login;->L:Landroid/widget/Button;
invoke-virtual {v0, v4},Landroid/widget/Button;->setSelected(Z)V
iget-object v0, p0,Lcom/alipay/android/client/Login;->v:Landroid/widget/AutoCompleteTextView;
const v1, 0x7f0a0011
invoke-virtual {v0, v1},Landroid/widget/AutoCompleteTextView;->setHint(I)V #设置输入框的提示
goto :goto_0
.end method
在OnCreate()方法中,设置了控件的显示、提示及监听器,而后判断登录类型并设置“支付宝会员”或“淘宝会员”按钮的选择状态,在这期间调用了d()方法,该方法过后,帐号名与密码就被显示了出来,d()方法代码如下:
.method private d()V
.locals 5
const/16 v2, 0x8 #设置为View.GONE
const/4 v4, 0x0 #设置不选中或View.VISIBLE
iget-boolean v0, p0,Lcom/alipay/android/client/Login;->k:Z
if-nez v0, :cond_5 #检查用户类型
const-string v0,"alipay"
iget-object v1, p0,Lcom/alipay/android/client/Login;->J:Landroid/widget/Button;
invoke-virtual {v1, v4},Landroid/widget/Button;->setVisibility(I)V #设置为View.VISIBLE
iget-object v1, p0,Lcom/alipay/android/client/Login;->H:Landroid/widget/TextView;
invoke-virtual {v1, v4},Landroid/widget/TextView;->setVisibility(I)V #设置为View.VISIBLE
iget-boolean v1, p0,Lcom/alipay/android/client/Login;->P:Z
if-nez v1, :cond_3
iget-object v1, p0,Lcom/alipay/android/client/Login;->d:Landroid/widget/RelativeLayout;
invoke-virtual {v1, v2},Landroid/widget/RelativeLayout;->setVisibility(I)V #设置为View.GONE
:goto_0
iget-object v1, p0,Lcom/alipay/android/client/Login;->j:Lcom/alipay/android/client/a/l; #获取j对象
#☻调用j.a(String)方法获得ho对象,传入的参数为代表用户类型的“alipay”或“taobao”☻
invoke-virtual {v1, v0},Lcom/alipay/android/client/a/l;->a(Ljava/lang/String;)Lcom/alipay/android/client/ho;
move-result-object v0
if-nez v0, :cond_0 #☻检查有没有“alipay”类型登录的用户☻,没有下面就new个空的ho对象
new-instance v0,Lcom/alipay/android/client/ho; #new一个ho对象
invoke-direct {v0},Lcom/alipay/android/client/ho;-><init>()V
:cond_0
iget-object v1, p0,Lcom/alipay/android/client/Login;->v:Landroid/widget/AutoCompleteTextView;
const-string v2,""
invoke-virtual {v1, v2},Landroid/widget/AutoCompleteTextView;->setText(Ljava/lang/CharSequence;)V
#将用户名输入框清空
iget-object v1, p0,Lcom/alipay/android/client/Login;->x:Landroid/widget/EditText;
const-string v2,""
#将密码输入框清空
invoke-virtual {v1, v2},Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V
iget-object v1, p0,Lcom/alipay/android/client/Login;->D:Landroid/widget/CheckBox;
invoke-virtual {v1, v4},Landroid/widget/CheckBox;->setChecked(Z)V #设置“记住登录密码”选择状态
iget-object v1, p0,Lcom/alipay/android/client/Login;->v:Landroid/widget/AutoCompleteTextView;
const/16 v2, 0x64
invoke-virtual {v1, v2},Landroid/widget/AutoCompleteTextView;->setThreshold(I)V
iget-object v1, p0,Lcom/alipay/android/client/Login;->v:Landroid/widget/AutoCompleteTextView;
iget-object v2, v0,Lcom/alipay/android/client/ho;->a:Ljava/lang/String; # ho.a 为用户名
# ☻将ho.a中的用户名设置到用户名输入框☻
invoke-virtual {v1, v2},Landroid/widget/AutoCompleteTextView;->setText(Ljava/lang/CharSequence;)V
iget-object v1, p0,Lcom/alipay/android/client/Login;->v:Landroid/widget/AutoCompleteTextView;
const/4 v2, 0x0
invoke-static {v1, v2},Lcom/alipay/android/client/a/o;->a(Landroid/widget/EditText;Landroid/text/method/PasswordTransformationMethod;)V
const-string v1,""
:try_start_0
iget-object v2, v0,Lcom/alipay/android/client/ho;->b:Ljava/lang/String; #ho.b为加密过的密码
const-string v3,""
invoke-virtual {v2, v3},Ljava/lang/String;->equals(Ljava/lang/Object;)Z #☻判断密码是否为空☻
move-result v2
if-nez v2, :cond_8
iget-object v0, v0,Lcom/alipay/android/client/ho;->b:Ljava/lang/String; #☻需要解密的密码☻
sget-object v1,Lcom/alipay/android/client/d/b;->I:Ljava/lang/String; #☻解密密钥☻
#调用com.google.zxing.c.a.b.b(String)方法解密ho.b中加密过的密码
invoke-static {v0, v1},Lcom/google/zxing/c/a/b;->b(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
if-eqz v0, :cond_1 #解密出的密码是否为空,为空就跳过设置密码框
iget-object v1, p0,Lcom/alipay/android/client/Login;->x:Landroid/widget/EditText; #密码框
invoke-virtual {v1, v0},Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V #☻设置密码☻
:cond_1
:goto_1
if-eqz v0, :cond_2
iget-object v1, p0,Lcom/alipay/android/client/Login;->D:Landroid/widget/CheckBox;
invoke-virtual {v0},Ljava/lang/String;->length()I
move-result v0
if-lez v0, :cond_9
const/4 v0, 0x1
:goto_2
invoke-virtual {v1, v0},Landroid/widget/CheckBox;->setChecked(Z)V #取消“记住登录密码”选中状态
:try_end_0
.catchLjava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
:cond_2
:goto_3
return-void #返回
:cond_3
iget-object v1, p0,Lcom/alipay/android/client/Login;->d:Landroid/widget/RelativeLayout;
invoke-virtual {v1, v4},Landroid/widget/RelativeLayout;->setVisibility(I)V #设置为View.VISIBLE
iget-object v1, p0,Lcom/alipay/android/client/Login;->h:Landroid/graphics/Bitmap;
if-eqz v1, :cond_4
iget-object v1, p0,Lcom/alipay/android/client/Login;->h:Landroid/graphics/Bitmap;
invoke-direct {p0, v1},Lcom/alipay/android/client/Login;->a(Landroid/graphics/Bitmap;)V
:cond_4
iget-object v1, p0,Lcom/alipay/android/client/Login;->e:Landroid/widget/EditText;
const-string v2,""
invoke-virtual {v1, v2},Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V
goto :goto_0
:cond_5
const-string v0,"taobao" #☻等待查询"taobao" 类型的用户登录记录☻
iget-object v1, p0,Lcom/alipay/android/client/Login;->J:Landroid/widget/Button;
invoke-virtual {v1, v2},Landroid/widget/Button;->setVisibility(I)V
iget-object v1, p0,Lcom/alipay/android/client/Login;->H:Landroid/widget/TextView;
invoke-virtual {v1, v2},Landroid/widget/TextView;->setVisibility(I)V
iget-boolean v1, p0,Lcom/alipay/android/client/Login;->M:Z
if-nez v1, :cond_6
iget-object v1, p0,Lcom/alipay/android/client/Login;->d:Landroid/widget/RelativeLayout;
invoke-virtual {v1, v2},Landroid/widget/RelativeLayout;->setVisibility(I)V
goto/16 :goto_0
:cond_6
iget-object v1, p0,Lcom/alipay/android/client/Login;->d:Landroid/widget/RelativeLayout;
invoke-virtual {v1, v4},Landroid/widget/RelativeLayout;->setVisibility(I)V #设置为View.VISIBLE
iget-object v1, p0,Lcom/alipay/android/client/Login;->g:Landroid/graphics/Bitmap;
if-eqz v1, :cond_7
iget-object v1, p0,Lcom/alipay/android/client/Login;->g:Landroid/graphics/Bitmap;
invoke-direct {p0, v1},Lcom/alipay/android/client/Login;->a(Landroid/graphics/Bitmap;)V
:cond_7
iget-object v1, p0,Lcom/alipay/android/client/Login;->e:Landroid/widget/EditText;
const-string v2,""
invoke-virtual {v1, v2},Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V #清空
goto/16 :goto_0
:cond_8 #跳到这里说明密码为空,则直接设置密码框内容为空
:try_start_1
iget-object v2, p0,Lcom/alipay/android/client/Login;->x:Landroid/widget/EditText; #密码框
iget-object v0, v0,Lcom/alipay/android/client/ho;->b:Ljava/lang/String; #密码
invoke-virtual {v2, v0},Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V#设置密码框
:try_end_1
.catchLjava/lang/Exception; {:try_start_1 .. :try_end_1} :catch_0
move-object v0, v1
goto :goto_1
:cond_9
move v0, v4
goto :goto_2
:catch_0
move-exception v0
invoke-virtual {v0},Ljava/lang/Exception;->printStackTrace()V
goto :goto_3
.end method
在整个Login类中,有三个变量是需要注意的,它们分别是“EditText e”、“AutoCompleteTextView v”、“EditText x”。因为只有它们才可能是用户名或密码输入框,经过分析发现“AutoCompleteTextView v”为用户名输入框,而“EditText x”为密码输入框。在d()方法中,代码首先设置用户名输入框内容为ho.a,如果ho.b不为空就先其解密,然后设置到密码框,这样,用户名与密码就设置好了,由于我们的主题是分析密码存储机制,所以,其它代码就不着重分析了。
为了验证上面的分析,这里使用一个小技巧来查看上面ho.a与ho.b以及解密后的值,很多人可能立即想到了使用Toast弹出信息提示,不过个人觉得使用LogCat输出显示更方便,一方面是加入代码量少,使用的寄存器少,另一方面是输出的结果可以随时查看。在d()方法中加入两处Log.v的代码,修改后的代码如图6所示:
图6
在插入代码时需要注意不要随意使用寄存器,而破坏了原程序的状态。接下来保存“Login.Smali”文件后对整个APK进行重新编译与签名,再次安装后导入上面保存的RecentDB文件,启动程序进入登录界面,会发现LogCat会显示如图7所示的信息:
图7
这个时候神奇的发现,被加密的密码、密钥以及解密后的密码都输出到了LogCat中!ho对象何时获取的密码信息?而密钥又是如何生成的?这重重的疑问更增加了我的好奇心!我们这个时候可以采取顺藤摸瓜的方式来追根溯源了。在d()方法中有如下一段代码:
iget-object v1, p0,Lcom/alipay/android/client/Login;->j:Lcom/alipay/android/client/a/l;
invoke-virtual {v1, v0},Lcom/alipay/android/client/a/l;->a(Ljava/lang/String;)Lcom/alipay/android/client/ho;
move-result-object v0
if-nez v0, :cond_0
new-instance v0, Lcom/alipay/android/client/ho; #如果j.a()返回为0,就new一个ho对象
ho对象是通过this.j.a()方法生成的,j是一个l对象,代码位于“com\alipay\android\client\a\l.smali”文件中,找到相应的l.a(String)方法,代码如下:
.method public final a(Ljava/lang/String;)Lcom/alipay/android/client/ho;
.locals 12
const/4 v10, 0x3
const/4 v7, 0x2
const/4 v8, 0x1
const/4 v9, 0x0
const/4 v3, 0x0
if-nez p1, :cond_0 #p1为String类型的参数,不为空就跳走,为空下面就查询所有用户登录的记录
iget-object v0, p0,Lcom/alipay/android/client/a/l;->a:Landroid/database/sqlite/SQLiteDatabase;
const-string v1,"RecentTable2" #需要查询的表
const/4 v2, 0x6
new-array v2, v2,[Ljava/lang/String;
const-string v4,"ID" #ID
aput-object v4, v2, v9
const-string v4,"NAME" #用户名
aput-object v4, v2, v8
const-string v4,"PASSWORD" #加密过的密码
aput-object v4, v2, v7
const-string v4,"TYPE" #用户类型
aput-object v4, v2, v10
const/4 v4, 0x4
const-string v5,"LOGINTIME" #最后登录的时间
aput-object v5, v2, v4
const/4 v4, 0x5
const-string v5, "USERID"
aput-object v5, v2, v4
const-string v7,"LOGINTIME desc" #构造SQL语句
move-object v4, v3
move-object v5, v3
move-object v6, v3
invoke-virtual/range {v0.. v7}, Landroid/database/sqlite/SQLiteDatabase;->query(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
move-result-object v0 #☻执行查询并返回结果☻
:goto_0
invoke-interface {v0},Landroid/database/Cursor;->moveToFirst()Z #转到第一条记录
move-result v1
if-eqz v1, :cond_2 #如果记录为空就跳走关闭数据库并返回
new-instance v1,Lcom/alipay/android/client/ho; #☻new一个ho对象☻
invoke-direct {v1},Lcom/alipay/android/client/ho;-><init>()V
const-string v2,"NAME"
invoke-interface {v0, v2},Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v2
invoke-interface {v0, v2},Landroid/database/Cursor;->getString(I)Ljava/lang/String; #☻查询结果的用户名
move-result-object v2
iput-object v2, v1,Lcom/alipay/android/client/ho;->a:Ljava/lang/String; #☻用户名赋值给ho.a☻
const-string v2,"PASSWORD"
invoke-interface {v0, v2},Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v2
invoke-interface {v0, v2},Landroid/database/Cursor;->getString(I)Ljava/lang/String;#☻查询结果的密码
move-result-object v2
iput-object v2, v1,Lcom/alipay/android/client/ho;->b:Ljava/lang/String;#☻密码赋值给ho.b☻
if-nez p1, :cond_1
const-string v2,"TYPE"
invoke-interface {v0, v2},Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v2
invoke-interface {v0, v2},Landroid/database/Cursor;->getString(I)Ljava/lang/String;#查询结果的用户类型
move-result-object v2
iput-object v2, v1,Lcom/alipay/android/client/ho;->c:Ljava/lang/String;☻用户类型赋值给ho.c☻
:goto_1
const-string v2,"USERID"
invoke-interface {v0, v2},Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v2
invoke-interface {v0, v2},Landroid/database/Cursor;->getString(I)Ljava/lang/String;
move-result-object v2
iput-object v2, v1,Lcom/alipay/android/client/ho;->e:Ljava/lang/String;☻用户ID赋值给ho.e☻
:goto_2
invoke-interface {v0},Landroid/database/Cursor;->close()V #关闭Cursor
return-object v1 #返回ho对象
:cond_0 #☻跳到这里查询特定“TYPE”的用户登录记录☻
iget-object v4, p0,Lcom/alipay/android/client/a/l;->a:Landroid/database/sqlite/SQLiteDatabase;
const-string v5,"RecentTable2" #要查询的数据表
const/4 v0, 0x5
new-array v6, v0,[Ljava/lang/String;
const-string v0,"ID"
aput-object v0, v6, v9
const-string v0,"NAME"
aput-object v0, v6, v8
const-string v0,"PASSWORD"
aput-object v0, v6, v7
const-string v0,"LOGINTIME"
aput-object v0, v6, v10
const/4 v0, 0x4
const-string v1,"USERID"
aput-object v1, v6, v0
const-string v7,"TYPE = ?"
new-array v8, v8,[Ljava/lang/String;
aput-object p1, v8, v9
const-string v11, "LOGINTIMEdesc"
#上面在构造SQL语句,整个语句类似于:
#select ID, NAME, PASSWORD, LOGINTIME, USERID from RecentTable2
# where TYPE="alipay" order by LOGINTIME desc;
move-object v9, v3
move-object v10, v3
invoke-virtual/range {v4.. v11},Landroid/database/sqlite/SQLiteDatabase;->query(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;#执行SQL查询语句
move-result-object v0
goto :goto_0 #跳转去赋值
:cond_1
iput-object p1, v1,Lcom/alipay/android/client/ho;->c:Ljava/lang/String; #保存用户类型到ho.c
goto :goto_1
:cond_2
move-object v1, v3
goto :goto_2
.end method
这段代码我注释的很清楚,而且功能也很简单,就是查询SQL语句,然后对ho对象的相应字段赋值。
用户名与加密密码的获取弄清楚后,来看看密钥是如何生成的。从上面密码解密部分的分析得知它是通过“com.alipay.android.client.d.b”对象的I成员传递进来的,而它是在哪里被赋的值呢?经过分析,发现是在Login类的“b(com.alipay.platform.core.b)”方法中调用了“com.alipay.android.client.a.m.b(Context)”方法,而后者又调用了“com.alipay.android.client.a.o”类的“a(Context)”方法,“a(Context)”方法代码如下:
.method public static a(Landroid/content/Context;)Ljava/lang/String;
.locals 3
invoke-static {p0},Lcom/alipay/android/client/a/j;->a(Landroid/content/Context;)Lcom/alipay/android/client/a/j;
move-result-object v0
invoke-virtual {v0},Lcom/alipay/android/client/a/j;->e()Ljava/lang/String;
move-result-object v0
const/4 v1, 0x0
const/16 v2, 0x8
invoke-virtual {v0, v1,v2}, Ljava/lang/String;->substring(II)Ljava/lang/String;
move-result-object v0
return-object v0
.end method
转换成JAVA代码只只执行如下一行:
return com.alipay.android.client.a.j.a(Context).e().substring(0, 8);
取e()方法返回字符串的前8位,“e()”方法代码如下:
.method public final e()Ljava/lang/String;
.locals 2
iget-object v0, p0,Lcom/alipay/android/client/a/j;->b:Ljava/lang/String; #j.b是否为空字符串
if-nez v0, :cond_0
iget-object v0, p0,Lcom/alipay/android/client/a/j;->a:Ljava/lang/String;#j.a是否为空字符串
if-nez v0, :cond_0
const-string v0,"000000000000000" #调用b()方法构造一个全0字符串
invoke-direct {p0, v0},Lcom/alipay/android/client/a/j;->b(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
:goto_0
const-string v1,"[[a-z][A-Z][0-9]]{15}\\|[[a-z][A-Z][0-9]]{15}"
invoke-virtual {v0, v1},Ljava/lang/String;->matches(Ljava/lang/String;)Z # #字符串是否适合要求
move-result v1
if-eqz v1, :cond_3 #字符串构造失败跳走
:goto_1
return-object v0 #返回
:cond_0
iget-object v0, p0,Lcom/alipay/android/client/a/j;->b:Ljava/lang/String; #取j.b字符串
if-nez v0, :cond_1 #不为空就跳走
new-instance v0,Ljava/lang/StringBuilder;
invoke-direct {v0},Ljava/lang/StringBuilder;-><init>()V
iget-object v1, p0,Lcom/alipay/android/client/a/j;->a:Ljava/lang/String; #取j.a字符串
invoke-virtual {v0, v1},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v0
const-string v1,"|" #添加‘|’
invoke-virtual {v0, v1},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v0
const-string v1,"000000000000000" #添加全0
invoke-virtual {v0, v1},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v0
invoke-virtual {v0},Ljava/lang/StringBuilder;->toString()Ljava/lang/String; #转换为字符串
move-result-object v0
goto :goto_0 #返回
:cond_1
iget-object v0, p0,Lcom/alipay/android/client/a/j;->a:Ljava/lang/String; #取j.a字符串
if-nez v0, :cond_2
iget-object v0, p0,Lcom/alipay/android/client/a/j;->b:Ljava/lang/String; #取j.b字符串
invoke-direct {p0, v0},Lcom/alipay/android/client/a/j;->b(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
goto :goto_0
:cond_2
new-instance v0, Ljava/lang/StringBuilder;
invoke-direct {v0},Ljava/lang/StringBuilder;-><init>()V
iget-object v1, p0,Lcom/alipay/android/client/a/j;->a:Ljava/lang/String;
invoke-virtual {v0, v1},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v0
const-string v1,"|"
invoke-virtual {v0, v1},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;