0x00下載附件
下載下來是一個.ab文件,百度了一下ab文件,一些分析文章說該格式是一個安卓備份文件,分爲有加密與無加密兩種。
若是已加密的備份文件,則文件頭會顯示加密方式。.ab這種東西第一次接觸,有點矇蔽.......
0x01解壓.ab文件
java -jar abe.jar unpack app3.ab app3.rar
解壓之後發現裏面有一個base.apk以及一個Encrypt.db以及一個demo.db
0x02分析
估計這個加密的.db文件裏面有我們想要的。base.apk拖進模擬器之後效果如下:
打開這個demo.db看下:
嘗試用SQLiteSpy打開,發現需要輸入密碼:
找不到密碼,那就先把apk上dex2jar看看代碼邏輯吧:
package com.example.yaphetshan.tencentwelcome;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import com.example.yaphetshan.tencentwelcome.a.a;
import net.sqlcipher.database.SQLiteDatabase;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private SQLiteDatabase a;
private a b;
private Button c;
private void a() {
SQLiteDatabase.loadLibs((Context)this);
this.b = new a((Context)this, "Demo.db", null, 1);
ContentValues contentValues = new ContentValues();
contentValues.put("name", "Stranger");
contentValues.put("password", Integer.valueOf(123456));
a a1 = new a();
String str2 = a1.a(contentValues.getAsString("name"), contentValues.getAsString("password"));
String str3 = a1.b(str2, contentValues.getAsString("password"));
String str1 = a1.a(str2 + str3);
this.a = this.b.getWritableDatabase(str1.substring(0, 7));
this.a.insert("TencentMicrMsg", null, contentValues);
}
public void onClick(View paramView) {
if (paramView == this.c) {
Intent intent = new Intent();
intent.putExtra("name", "name");
intent.putExtra("password", "pass");
intent.setClass((Context)this, AnotherActivity.class);
startActivity(intent);
}
}
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(2130968603);
this.c = (Button)findViewById(2131427417);
this.c.setOnClickListener(this);
SharedPreferences.Editor editor = getSharedPreferences("test", 0).edit();
editor.putString("Is_Encroty", "1");
editor.putString("Encryto", "SqlCipher");
editor.putString("ver_sion", "3_4_0");
editor.apply();
a();
}
}
看看onCreate:
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(2130968603);
this.c = (Button)findViewById(2131427417);
this.c.setOnClickListener(this);
SharedPreferences.Editor editor = getSharedPreferences("test", 0).edit();
editor.putString("Is_Encroty", "1"); //設置加密標誌位爲1
editor.putString("Encryto", "SqlCipher"); //????
editor.putString("ver_sion", "3_4_0"); //版本爲3.4.0
editor.apply();
a(); //上述操作執行完畢,執行本地a()方法
}
現在來看a()方法:
private void a() {
SQLiteDatabase.loadLibs((Context)this); //加載數據庫lib
this.b = new a((Context)this, "Demo.db", null, 1); //創建Demo.db的數據庫文件
//其中,this.b主要是創建如下語句:
//create table TencentMicrMsg(name text,password integer,F_l_a_g text)
ContentValues contentValues = new ContentValues(); //ContentValues用於存儲基本數據類型
contentValues.put("name", "Stranger");
contentValues.put("password", Integer.valueOf(123456));
a a1 = new a(); //實例化一個a對象
String str2 = a1.a(contentValues.getAsString("name"), contentValues.getAsString("password"));
String str3 = a1.b(str2, contentValues.getAsString("password"));
String str1 = a1.a(str2 + str3);
this.a = this.b.getWritableDatabase(str1.substring(0, 7));
this.a.insert("TencentMicrMsg", null, contentValues);
}
實例化這個a a1 = new a()的時候,a的代碼如下:
package com.example.yaphetshan.tencentwelcome.a;
public class a {
private String a = "yaphetshan";
public String a(String paramString) {
new b();
return b.b(paramString + this.a);
}
public String a(String paramString1, String paramString2) {
paramString1 = paramString1.substring(0, 4);
paramString2 = paramString2.substring(0, 4);
return paramString1 + paramString2;
}
public String b(String paramString1, String paramString2) {
new b();
return b.a(paramString1);
}
}
這裏我的dex2jar好像出了點問題,執行到
new b();
return b.a(paramString1);
這兩句的時候,點擊進去b的代碼是這樣子的:
package com.example.yaphetshan.tencentwelcome.a;
public class b {
public static final String a(String paramString) { // Byte code:
// 0: iconst_0
// 1: istore_1
// 2: bipush #16
// 4: newarray char
// 6: astore #6
// 8: aload #6
// 10: dup
// 11: iconst_0
// 12: ldc 48
// 14: castore
// 15: dup
// 16: iconst_1
// 17: ldc 49
// 19: castore
// 20: dup
// 21: iconst_2
// 22: ldc 50
// 24: castore
// 25: dup
// 26: iconst_3
// 27: ldc 51
// 29: castore
// 30: dup
// 31: iconst_4
// 32: ldc 52
// 34: castore
// 35: dup
// 36: iconst_5
// 37: ldc 53
// 39: castore
// 40: dup
// 41: bipush #6
// 43: ldc 54
// 45: castore
// 46: dup
// 47: bipush #7
// 49: ldc 55
// 51: castore
// 52: dup
// 53: bipush #8
// 55: ldc 56
// 57: castore
// 58: dup
// 59: bipush #9
// 61: ldc 57
// 63: castore
// 64: dup
// 65: bipush #10
// 67: ldc 97
// 69: castore
// 70: dup
// 71: bipush #11
// 73: ldc 98
// 75: castore
// 76: dup
// 77: bipush #12
// 79: ldc 99
// 81: castore
// 82: dup
// 83: bipush #13
// 85: ldc 100
// 87: castore
// 88: dup
// 89: bipush #14
// 91: ldc 101
// 93: castore
// 94: dup
// 95: bipush #15
// 97: ldc 102
// 99: castore
// 100: pop
// 101: aload_0
// 102: invokevirtual getBytes : ()[B
// 105: astore_0
// 106: ldc 'MD5'
// 108: invokestatic getInstance : (Ljava/lang/String;)Ljava/security/MessageDigest;
// 111: astore #7
// 113: aload #7
// 115: aload_0
// 116: invokevirtual update : ([B)V
// 119: aload #7
// 121: invokevirtual digest : ()[B
// 124: astore_0
// 125: aload_0
// 126: arraylength
// 127: istore_3
// 128: iload_3
// 129: iconst_2
// 130: imul
// 131: newarray char
// 133: astore #7
// 135: iconst_0
// 136: istore_2
// 137: goto -> 155
// 140: new java/lang/String
// 143: dup
// 144: aload #7
// 146: invokespecial <init> : ([C)V
// 149: astore_0
// 150: aload_0
// 151: areturn
// 152: astore_0
// 153: aconst_null
// 154: areturn
// 155: iload_1
// 156: iload_3
// 157: if_icmpge -> 140
// 160: aload_0
// 161: iload_1
// 162: baload
// 163: istore #4
// 165: iload_2
// 166: iconst_1
// 167: iadd
// 168: istore #5
// 170: aload #7
// 172: iload_2
// 173: aload #6
// 175: iload #4
// 177: iconst_4
// 178: iushr
// 179: bipush #15
// 181: iand
// 182: caload
// 183: castore
// 184: iload #5
// 186: iconst_1
// 187: iadd
// 188: istore_2
// 189: aload #7
// 191: iload #5
// 193: aload #6
// 195: iload #4
// 197: bipush #15
// 199: iand
// 200: caload
// 201: castore
// 202: iload_1
// 203: iconst_1
// 204: iadd
// 205: istore_1
// 206: goto -> 155
// Exception table:
// from to target type
// 101 135 152 java/lang/Exception
// 140 150 152 java/lang/Exception }
public static final String b(String paramString) { // Byte code:
// 0: iconst_0
// 1: istore_1
// 2: bipush #16
// 4: newarray char
// 6: astore #6
// 8: aload #6
// 10: dup
// 11: iconst_0
// 12: ldc 48
// 14: castore
// 15: dup
// 16: iconst_1
// 17: ldc 49
// 19: castore
// 20: dup
// 21: iconst_2
// 22: ldc 50
// 24: castore
// 25: dup
// 26: iconst_3
// 27: ldc 51
// 29: castore
// 30: dup
// 31: iconst_4
// 32: ldc 52
// 34: castore
// 35: dup
// 36: iconst_5
// 37: ldc 53
// 39: castore
// 40: dup
// 41: bipush #6
// 43: ldc 54
// 45: castore
// 46: dup
// 47: bipush #7
// 49: ldc 55
// 51: castore
// 52: dup
// 53: bipush #8
// 55: ldc 56
// 57: castore
// 58: dup
// 59: bipush #9
// 61: ldc 57
// 63: castore
// 64: dup
// 65: bipush #10
// 67: ldc 97
// 69: castore
// 70: dup
// 71: bipush #11
// 73: ldc 98
// 75: castore
// 76: dup
// 77: bipush #12
// 79: ldc 99
// 81: castore
// 82: dup
// 83: bipush #13
// 85: ldc 100
// 87: castore
// 88: dup
// 89: bipush #14
// 91: ldc 101
// 93: castore
// 94: dup
// 95: bipush #15
// 97: ldc 102
// 99: castore
// 100: pop
// 101: aload_0
// 102: invokevirtual getBytes : ()[B
// 105: astore_0
// 106: ldc 'SHA-1'
// 108: invokestatic getInstance : (Ljava/lang/String;)Ljava/security/MessageDigest;
// 111: astore #7
// 113: aload #7
// 115: aload_0
// 116: invokevirtual update : ([B)V
// 119: aload #7
// 121: invokevirtual digest : ()[B
// 124: astore_0
// 125: aload_0
// 126: arraylength
// 127: istore_3
// 128: iload_3
// 129: iconst_2
// 130: imul
// 131: newarray char
// 133: astore #7
// 135: iconst_0
// 136: istore_2
// 137: goto -> 155
// 140: new java/lang/String
// 143: dup
// 144: aload #7
// 146: invokespecial <init> : ([C)V
// 149: astore_0
// 150: aload_0
// 151: areturn
// 152: astore_0
// 153: aconst_null
// 154: areturn
// 155: iload_1
// 156: iload_3
// 157: if_icmpge -> 140
// 160: aload_0
// 161: iload_1
// 162: baload
// 163: istore #4
// 165: iload_2
// 166: iconst_1
// 167: iadd
// 168: istore #5
// 170: aload #7
// 172: iload_2
// 173: aload #6
// 175: iload #4
// 177: iconst_4
// 178: iushr
// 179: bipush #15
// 181: iand
// 182: caload
// 183: castore
// 184: iload #5
// 186: iconst_1
// 187: iadd
// 188: istore_2
// 189: aload #7
// 191: iload #5
// 193: aload #6
// 195: iload #4
// 197: bipush #15
// 199: iand
// 200: caload
// 201: castore
// 202: iload_1
// 203: iconst_1
// 204: iadd
// 205: istore_1
// 206: goto -> 155
// Exception table:
// from to target type
// 101 135 152 java/lang/Exception
// 140 150 152 java/lang/Exception }
}
只有一堆類似彙編的註釋,因此換用其他反編譯工具試試:
改用jadx之後反編譯的代碼如下:
package com.example.yaphetshan.tencentwelcome.a;
import java.security.MessageDigest;
/* compiled from: SHA1Manager */
public class b {
public static final String a(String str) {
char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] bytes = str.getBytes();
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.update(bytes);
char[] cArr2 = new char[(r4 * 2)];
int i = 0;
for (byte b : instance.digest()) {
int i2 = i + 1;
cArr2[i] = cArr[(b >>> 4) & 15];
i = i2 + 1;
cArr2[i2] = cArr[b & 15];
}
return new String(cArr2);
} catch (Exception e) {
return null;
}
}
public static final String b(String str) {
char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] bytes = str.getBytes();
MessageDigest instance = MessageDigest.getInstance("SHA-1");
instance.update(bytes);
char[] cArr2 = new char[(r4 * 2)];
int i = 0;
for (byte b : instance.digest()) {
int i2 = i + 1;
cArr2[i] = cArr[(b >>> 4) & 15];
i = i2 + 1;
cArr2[i2] = cArr[b & 15];
}
return new String(cArr2);
} catch (Exception e) {
return null;
}
}
}
看到是兩個MD5和SHA-1加密的方法。
然而這b代碼還是有問題,將代碼複製到eclipse可以發現,變量r4彷彿是憑空變出來的,作爲一種強類型語言,Java不允許像python那樣不聲明直接使用一個變量。因此,jadx還是不行,於是上JEB2:
JEB2下載好之後配置一下JAVA_HOME爲jdk 1.8.121及以下版本,高了會閃退。
將弄出來的.dex拖進JEB:
點擊MainActivity:
出來的是smali代碼,這裏我們右鍵MainActivity,點擊Decompile,就出來java代碼了。
然後上述兩個函數實際如下:
public static final String a(String arg9) {
String v0_2;
int v0 = 0;
char[] v2 = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] v1 = arg9.getBytes();
MessageDigest v3 = MessageDigest.getInstance("MD5");
v3.update(v1);
byte[] v3_1 = v3.digest();
int v4 = v3_1.length;
char[] v5 = new char[v4 * 2];
int v1_1 = 0;
while(v0 < v4) {
int v6 = v3_1[v0];
int v7 = v1_1 + 1;
v5[v1_1] = v2[v6 >>> 4 & 15];
v1_1 = v7 + 1;
v5[v7] = v2[v6 & 15];
++v0;
}
v0_2 = new String(v5);
}
catch(Exception v0_1) {
v0_2 = null;
}
return v0_2;
}
public static final String b(String arg9) {
String v0_2;
int v0 = 0;
char[] v2 = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] v1 = arg9.getBytes();
MessageDigest v3 = MessageDigest.getInstance("SHA-1");
v3.update(v1);
byte[] v3_1 = v3.digest();
int v4 = v3_1.length;
char[] v5 = new char[v4 * 2];
int v1_1 = 0;
while(v0 < v4) {
int v6 = v3_1[v0];
int v7 = v1_1 + 1;
v5[v1_1] = v2[v6 >>> 4 & 15];
v1_1 = v7 + 1;
v5[v7] = v2[v6 & 15];
++v0;
}
v0_2 = new String(v5);
}
catch(Exception v0_1) {
v0_2 = null;
}
return v0_2;
}
這次就沒有無中生有的變量出現了。
根據這次的代碼我們就可以根據"Strange"、"123456"、"yaphetshan"計算數據庫的密碼。
此處給出計算數據庫密碼的Java代碼:
package Test;
import java.security.MessageDigest;
public class Test {
public static void main(String[] args) throws Exception {
String name = "Stranger",password="123456";
String v2 = v1a(name, password);
System.out.println(v1a(v2+v1b(v2,password)).substring(0,7));
}
public static String v1b(String arg2, String arg3) {
return MD5(arg2);
}
public static String v1a(String arg4, String arg5) {
return arg4.substring(0, 4) + arg5.substring(0, 4);
}
public static String v1a(String arg3) {
String a = "yaphetshan";
return SHA1(arg3+a);
}
public static final String MD5(String arg9) {
String v0_2;
int v0 = 0;
char[] v2 = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] v1 = arg9.getBytes();
MessageDigest v3 = MessageDigest.getInstance("MD5");
v3.update(v1);
byte[] v3_1 = v3.digest();
int v4 = v3_1.length;
char[] v5 = new char[v4 * 2];
int v1_1 = 0;
while(v0 < v4) {
int v6 = v3_1[v0];
int v7 = v1_1 + 1;
v5[v1_1] = v2[v6 >>> 4 & 15];
v1_1 = v7 + 1;
v5[v7] = v2[v6 & 15];
++v0;
}
v0_2 = new String(v5);
}
catch(Exception v0_1) {
v0_2 = null;
}
return v0_2;
}
public static final String SHA1(String arg9) {
String v0_2;
int v0 = 0;
char[] v2 = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] v1 = arg9.getBytes();
MessageDigest v3 = MessageDigest.getInstance("SHA-1");
v3.update(v1);
byte[] v3_1 = v3.digest();
int v4 = v3_1.length;
char[] v5 = new char[v4 * 2];
int v1_1 = 0;
while(v0 < v4) {
int v6 = v3_1[v0];
int v7 = v1_1 + 1;
v5[v1_1] = v2[v6 >>> 4 & 15];
v1_1 = v7 + 1;
v5[v7] = v2[v6 & 15];
++v0;
}
v0_2 = new String(v5);
}
catch(Exception v0_1) {
v0_2 = null;
}
return v0_2;
}
}
這裏說拿到密碼之後怎麼操作。
其他大神的wp我不知道是因爲他們太吊所以省略步驟,還是題目有所變動,他們的wp在拿到數據庫密碼之後直接就用DB Browser for SQLite或者SQLiteStudio直接就能打開,太(芬芳)了。
我研究了半天,發現需要先用sqlcipher對.db文件操作一波,將裏面的內容複製到一個新的無密碼的.db文件裏面:
1.命令行在sqlcipher的安裝目錄中輸入:sqlcipher-shell64.exe encryted.db
2.進入sqlLite>
3.sqlite> PRAGMA key = '這裏是數據庫的密碼';
4.sqlite> ATTACH DATABASE '這裏是要複製到的新數據庫名' AS plaintext KEY '';
5.sqlite> SELECT sqlcipher_export('plaintext');
6.sqlite> DETACH DATABASE plaintext;
上面步驟操作完,就可以拿到一個無加密的數據庫.db文件。
然後用SQLiteSpy打開它:
拿去base64一波就能得到flag。
-----------這道題讓我覺得我菜得真實----------------