现在的位置: 首页 > 综合 > 正文

手机支付宝密码存储机制分析

2013年11月15日 ⁄ 综合 ⁄ 共 18750字 ⁄ 字号 评论关闭

作者:非虫

随着电子商务在国内的迅猛发展,网上购物也成为了时下流行的消费方式。就我个人来说,每年在淘宝上也会购物上百起。这足不出户的购物方式的确给我们的生活带来了不少实惠与方便,但同时,购物安全也成为了广大网购消费者担心的一个问题。每年在新闻中爆光的网银被盗、被骗的事件也屡见不鲜。

长期使用网络客户端软件的朋友都有一个习惯,为了避免每次使用时输入帐号名和密码,都习惯使用软件的自动保存密码功能来记住登录密码,这样下次直接点击登录按钮就可以登录软件了(有些软件直接跳过了登录确认的界面),这样,一个安全问题就出来了,软件为我们保存的密码是明文存储的吗?如果加密了,加密强度怎样?外部用户可以直接破解吗?试想一下,像支付宝这类敏感的网银软件,如果本地存储的密码被人直接破解,那后果是很难预料的!而随着我对支付宝程序的逆向分析,也证明了这个安全问题确实存在。在此申明:以下文章涉及的代码与分析内容仅供安卓系统安全学习交流,任何个人或组织不得使用文中提到的技术做违法犯罪活动,否则由此引发的任何后果与法律责任本人概不负责。

测试环境

一台安装有支付宝的安卓手机,并且能获得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;

抱歉!评论已关闭.