• 拿到这个APK之后,没啥好说的,安装后发现要输入一个什么密码才能通过验证,拖到JEB里看看。
  • 看到MainActivity里有几个关键的函数,分别是bytesToAliSmsCode(String, [B)StringgetPwdFromPic()StringgetTableFromPic()StringaliCodeToBytes(String, String)[B。具体做什么用的等下再分析。
  • 找到onCreate函数,发现设置了一个监听器。
    00000048 invoke-virtual Button->setOnClickListener(View$OnClickListener)V, v0, v2
  • 于是我们继续去找onClick函数,应该在MAinActivity$1这个类中,
.method public onClick(View)V
          .registers 11
          .param p1, "v"
          .prologue
00000000  iget-object             v6, p0, MainActivity$1->val$edit:EditText
00000004  invoke-virtual          EditText->getText()Editable, v6
0000000A  move-result-object      v6
0000000C  invoke-interface        Editable->toString()String, v6
00000012  move-result-object      v3
          .local v3, password:Ljava/lang/String;
00000014  iget-object             v6, p0, MainActivity$1->this$0:MainActivity
00000018  invoke-virtual          MainActivity->getTableFromPic()String, v6
0000001E  move-result-object      v5
          .local v5, table:Ljava/lang/String;
00000020  iget-object             v6, p0, MainActivity$1->this$0:MainActivity
00000024  invoke-virtual          MainActivity->getPwdFromPic()String, v6
0000002A  move-result-object      v4
          .local v4, pw:Ljava/lang/String;
0000002C  const-string            v6, "lil"
00000030  new-instance            v7, StringBuilder
00000034  const-string            v8, "table:"
00000038  invoke-direct           StringBuilder-><init>(String)V, v7, v8
0000003E  invoke-virtual          StringBuilder->append(String)StringBuilder, v7, v5
00000044  move-result-object      v7
00000046  invoke-virtual          StringBuilder->toString()String, v7
0000004C  move-result-object      v7
0000004E  invoke-static           Log->i(String, String)I, v6, v7
00000054  const-string            v6, "lil"
00000058  new-instance            v7, StringBuilder
0000005C  const-string            v8, "pw:"
00000060  invoke-direct           StringBuilder-><init>(String)V, v7, v8
00000066  invoke-virtual          StringBuilder->append(String)StringBuilder, v7, v4
0000006C  move-result-object      v7
0000006E  invoke-virtual          StringBuilder->toString()String, v7
00000074  move-result-object      v7
00000076  invoke-static           Log->i(String, String)I, v6, v7
0000007C  const-string            v2, ""
:80
          .local v2, enPassword:Ljava/lang/String;
00000080  const-string            v6, "utf-8"
00000084  invoke-virtual          String->getBytes(String)[B, v3, v6
0000008A  move-result-object      v6
0000008C  invoke-static           MainActivity->access$0(String, [B)String, v5, v6
00000092  move-result-object      v2
00000094  const-string            v6, "lil"
00000098  new-instance            v7, StringBuilder
0000009C  const-string            v8, "enPassword:"
000000A0  invoke-direct           StringBuilder-><init>(String)V, v7, v8
000000A6  invoke-virtual          StringBuilder->append(String)StringBuilder, v7, v2
000000AC  move-result-object      v7
000000AE  invoke-virtual          StringBuilder->toString()String, v7
000000B4  move-result-object      v7
000000B6  invoke-static           Log->i(String, String)I, v6, v7
:BC
000000BC  if-eqz                  v4, :F2
:C0
000000C0  const-string            v6, ""
000000C4  invoke-virtual          String->equals(Object)Z, v4, v6
000000CA  move-result             v6
000000CC  if-nez                  v6, :F2
:D0
000000D0  invoke-virtual          String->equals(Object)Z, v4, v2
000000D6  move-result             v6
000000D8  if-eqz                  v6, :F2
:DC
000000DC  iget-object             v6, p0, MainActivity$1->this$0:MainActivity
000000E0  invoke-static           MainActivity->access$1(MainActivity)V, v6
:E6
000000E6  return-void
:E8
000000E8  move-exception          v1
          .local v1, e:Ljava/io/UnsupportedEncodingException;
000000EA  invoke-virtual          UnsupportedEncodingException->printStackTrace()V, v1
000000F0  goto                    :BC
:F2
000000F2  new-instance            v0, AlertDialog$Builder
000000F6  iget-object             v6, p0, MainActivity$1->this$0:MainActivity
000000FA  invoke-direct           AlertDialog$Builder-><init>(Context)V, v0, v6
          .local v0, builder:Landroid/app/AlertDialog$Builder;
00000100  const                   v6, 0x7F0A0011  # R.string.dialog_error_tips
00000106  invoke-virtual          AlertDialog$Builder->setMessage(I)AlertDialog$Builder, v0, v6
0000010C  const                   v6, 0x7F0A0010  # R.string.dialog_title
00000112  invoke-virtual          AlertDialog$Builder->setTitle(I)AlertDialog$Builder, v0, v6
00000118  const                   v6, 0x7F0A0013  # R.string.dialog_ok
0000011E  new-instance            v7, MainActivity$1$1
00000122  invoke-direct           MainActivity$1$1-><init>(MainActivity$1)V, v7, p0
00000128  invoke-virtual          AlertDialog$Builder->setPositiveButton(I, DialogInterface$OnClickListener)AlertDialog$Builder, v0, v6, v7
0000012E  invoke-virtual          AlertDialog$Builder->show()AlertDialog, v0
00000134  goto                    :E6
          .catch UnsupportedEncodingException {:80 .. :BC} :E8
.end method

整个流程还是比较清晰的,先获取EditText中的内容,然后调用getTableFromPicgetPwdFromPicbytesToAliSmsCode,最后将enPasswd和getPwdFromPic得到的数据进行比较,如果一致则通过。搞成JAVA代码大概是下面这个样子。

public void onClick(View v) {
    String InputString = this.val$edit.getText().toString();
    String pic89473 = MainActivity.this.getTableFromPic();
    String pic91265 = MainActivity.this.getPwdFromPic();
    Log.i("lil", "table:" + pic89473);
    Log.i("lil", "pw:" + pic91265);
    try {
        String enPassword = MainActivity.bytesToAliSmsCode(pic89473, InputString.getBytes("utf-8"));
        Log.i("lil", "enPassword:" + enPassword);
    }
    catch(UnsupportedEncodingException v1) {
        v1.printStackTrace();
    }

    if(pic91265 == null || (pic91265.equals("")) || !pic91265.equals(enPassword)) {
        AlertDialog$Builder v0 = new AlertDialog$Builder(MainActivity.this);
        v0.setMessage(2131361809);
        v0.setTitle(2131361808);
        v0.setPositiveButton(2131361811, new DialogInterface$OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        v0.show();
    }
    else {
        MainActivity.this.showDialog();
    }
}

接下来去分析getTableFromPicgetPwdFromPic函数。
关键部分如下

inputStream = this.getResources().getAssets().open("logo.png");
int picSize = inputStream.available();
byte[] buffer = new byte[picSize];
inputStream.read(buffer, 0, picSize);
byte[] temp = new byte[768];
System.arraycopy(buffer, 89473, temp, 0, 768);  // 从第89473偏移量开始复制768个字节
returnResult = new String(temp, "utf-8");
if(inputStream == null) {
    return returnResult;
}

其实就是读取了logo.png这张图片,从89473位置开始读取了768个字节。getPwdFromPic函数类似。
bytesToAliSmsCode更简单,主要就是

for(v0 = 0; v0 < data.length; ++v0) {
    v1.append(table.charAt(data[v0] & 255));
}

最后返回v1

  • 注意到代码中有Log.i,果断祭出ddms,看看log,先随便输入点什么,比如123456
02-18 11:05:41.434: I/lil(25142): table:一乙二十丁厂七卜人入八九几儿了力乃刀又三于干亏士工土才寸下大丈与万上小口巾山千乞川亿个勺久凡及夕丸么广亡门义之尸弓己已子卫也女飞刃习叉马乡丰王井开夫天无元专云扎艺木五支厅不太犬区历尤友匹车巨牙屯比互切瓦止少日中冈贝内水见午牛手毛气升长仁什片仆化仇币仍仅斤爪反介父从今凶分乏公仓月氏勿欠风丹匀乌凤勾文六方火为斗忆订计户认心尺引丑巴孔队办以允予劝双书幻玉刊示末未击打巧正扑扒功扔去甘世古节本术可丙左厉右石布龙平灭轧东卡北占业旧帅归且旦目叶甲申叮电号田由史只央兄叼叫另叨叹四生失禾丘付仗代仙们仪白仔他斥瓜乎丛令用甩印乐
02-18 11:05:41.434: I/lil(25142): pw:义弓么丸广之
02-18 11:05:41.444: I/lil(25142): enPassword:么广亡门义之

哟~这都是嘛东西,经过上面的分析我们已经知道了,只要让pw和enPassword相等就没问题了,可是这一堆汉字都是嘛东西?
仔细看一下,发现enPassword中出现的汉字其实都在table中,好像就是个单表替换嘛~,这样就简单了,把pw拿到table里匹配一下,发现从“丸”这个字开始,分别对应0123456789。也就是:
丸么广亡门义之尸弓己 -> 0123456789
那把pw翻译出来就是581026,破解成功。