Android題目分析部分思路總結
我總結了幾種經典的類型,列個目錄吧:
1.Strange apk:firda dump正在運行的dex文件
2.app3:怎么打開.ab文件,jeb動態調試
3.ph0en1x_100:jeb動態調試
4.easydex:so層靜態分析,idc腳本
一、Strange apk
首先看一下java層
(JEB誰用誰說好,我感覺這個比as好用,動態調試的時候不用操作太多,還可以修改參數類型和名稱,比jadx,jdgui好用好分析,還可以保存上次沒有分析完的程序)
先看manifest文件,找到程序的入口點:

sctf.demo.myapplication.t這個是程序的入口點。

但是找不到啊,這里我猜測應該就是在app運行過程中動態釋放出來的。
那就用frida_dump試試吧。

在指定文件夾里找到dump下的dex文件進行分析就可以了。

只有第一個dex文件能找到了.t文件。

在這個.t文件的oncreate又定位到了這里:

因為程序一開始分析的是沒有sctf.demo.myapplication.t文件了 xml文件里只有sctf.demo.myapplication.t和sctf.demo.myapplication.s文件,所以這個MAIN應該是sctf.demo.myapplication.s文件里面的,所以就要分析sctf.demo.myapplication.s文件了。

然后修復一下代碼,修改一下變量的名稱,加一些注釋便于分析:
protected void onCreate(Bundle arg5) { super.onCreate(arg5); this.setContentView(0x7F09001D); View input_str = this.findViewById(0x7F070022); // 輸入字符串 this.findViewById(0x7F07008A); ((Button)input_str).setOnClickListener(new View$OnClickListener(this.findViewById(0x7F070037)) { public void onClick(View arg9) { String str1 = ""; // 兩個字符串臨時存儲變量 String str2 = ""; int t = 0; // 臨時變量 String str = this.val$ed.getText().toString(); int num_30 = 30; if(str.length() == num_30) { // 輸入的字符串的長度為30 while(t < 12) { str1 = str1 + str.charAt(t); // 將前12個輸入的字符存儲到str1里面 ++t; } str1 = f.sctf(str1); // 跟進去發現是將前12個字符進行base64加密 while(t < num_30) { str2 = str2 + str.charAt(t); // 將后12個字符存儲到str2字符串里面 ++t; } if(str1.equals("c2N0ZntXM2xjMG1l")) { // 根據字符串1和該字符串相匹配,直接就能解出來前12個字符sctf{W3lc0me Intent v4_1 = new Intent(); v4_1.putExtra("data_return", str2);//返回.t文件中分析str2字符串的加密操作 s.this.setResult(-1, v4_1); s.this.finish(); } else { Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show(); } } else { Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show(); } } }); }}
通過這段代碼的分析可以得到前半部分的flag:sctf{W3lc0me。
總結一下就是通過base64解密得到的(詳細分析過程在代碼注釋)。
返回.t文件對str2字符串進程分析。

修復一下代碼:
public class t extends AppCompatActivity { public t() { super(); } protected void onActivityResult(int arg9, int arg10, Intent arg11) { View v0 = this.findViewById(0x7F07008B); View v1 = this.findViewById(0x7F070023); if(arg9 == 1 && arg10 == -1) { try { MessageDigest str = MessageDigest.getInstance("MD5"); str.update("syclover".getBytes()); // 將syclover字符串進行md5加密 String temp_str = new BigInteger(1, str.digest()).toString(16); // 將加密后的字符轉化為字符串8bfc8af07bca146c937f283b8ec768d4 } catch(Exception v5) { v5.printStackTrace(); } if(f.encode(arg11.getStringExtra("data_return"), temp_str).equals("~8t808_8A8n848r808i8d8-8w808r8l8d8}8")) { ((TextView)v0).setVisibility(0); ((Button)v1).setVisibility(4); // 去除所有的8之后剩下~t0_An4r0id-w0rld} } else { Toast.makeText(this.getApplicationContext(), "one more step", 1).show(); } } }
這個是中間的encode的函數(代碼修復之后的)分析:
public static String encode(String temp_str, String str) { // 將data_return字符串和加密后的字符串8bfc8af07bca146c937f283b8ec768d4格一位放一個 int len_temp_str = temp_str.length(); int len_str = str.length(); StringBuilder t_str = new StringBuilder(); int t; for(t = 0; t < len_temp_str; ++t) { t_str.append(temp_str.charAt(t)); t_str.append(str.charAt(t / len_str)); // 每次都是只能取到第一個字符8 } return t_str.toString(); // 返回字符8 }
通過分析后半部分代碼得到了:~t0_An4r0id-w0rld}
總結一下就是md5解密,然后分析encode函數,得到flag的后半部分
這道題的關鍵所在就是,找到程序的入口點,dump出正在運行的dex文件,字符串加密簡單分析即可得到。
二、app3
打開之后一看是.ab文件(在對安卓手機進行取證時,經常需要備份手機的應用程序數據,備份后得到的數據文件為ab格式,ab文件一般分兩種,一種是沒有加密,這種文件前面有24字節的文件頭,文件頭包含none標志,文件頭之后就是數據;一種是加密的備份文件,它的文件頭就比較復雜了,文件頭包含AES-256標志。)

一般的話.ab文件是用這個文件進行操作的

我總結出來一般的話使用這兩條指令中的一條:
java -jar abe-all.jar unpack app3.ab app3.jar
java -jar abe-all.jar unpack app3.ab app3.tar

將.ab文件轉化為.tar文件

解壓縮之后發現了apk文件,就可以進行分析了:
先從程序的入口點開始分析:
private void a() { SQLiteDatabase.loadLibs(((Context)this)); // 加載數據庫 this.b = new a(((Context)this), "Demo.db", null, 1); ContentValues v0 = new ContentValues(); // 實例化v0這個對象,用于存儲用戶名和密碼 v0.put("name", "Stranger"); // name = Stranger v0.put("password", Integer.valueOf(0x1E240)); // password = 0x1E240 = 123456 com.example.yaphetshan.tencentwelcome.a.a v1 = new com.example.yaphetshan.tencentwelcome.a.a(); // 初始化v1對象 String v2 = v1.a(v0.getAsString("name"), v0.getAsString("password")); // 將獲取到的用戶名和密碼的值轉化為字符型變量存儲到v2中,調用的v1.a方法,然后跟進去分信息v1.a的方法,如果傳入兩個參數,就是分別取0-4個字符,就是返回Stra1234 this.a = this.b.getWritableDatabase(v1.a(v2 + v1.b(v2, v0.getAsString("password"))).substring(0, 7)); // 調用了數據庫函數,將剛剛的變量作為參數傳入,調用了v1.a的方法和v1.b的方法 this.a.insert("TencentMicrMsg", null, v0); }
這個代碼是調用了這個方法:
String v2 = v1.a(v0.getAsString("name"), v0.getAsString("password"));

最后這個代碼這里,由于這里是給數據庫傳參數的函數,所以就是要分析這個,一開始的那個tar文件里面解壓之后發現了.db文件,就是數據庫文件,所以我猜測,這里傳入的參數應該是登錄數據庫的密碼,重點就是要分析這個函數了。
this.a = this.b.getWritableDatabase(v1.a(v2 + v1.b(v2, v0.getAsString("password"))).substring(0, 7));
先是外層調用了v1.a的這個方法:

然后又內層調用了這個方法:

先分析這個函數的返回值:
v1.b(v2, v0.getAsString("password"))
跟進去分析:
package com.example.yaphetshan.tencentwelcome.a; public class a { private String a; public a() { super(); this.a = "yaphetshan"; } public String a(String arg4, String arg5) { return arg4.substring(0, 4) + arg5.substring(0, 4); // 返回值截取0-4位字符 } public String a(String arg3) { new b(); return b.b(arg3 + this.a); // 調用了b方法,跟進去分析一下 } public String b(String arg2, String arg3) { new b(); return b.a(arg2); }}
繼續跟進去分析b方法:


其實也沒有什么,就是md5加密和SHA-1加密。
這里我就用動態調試的方法吧:
先獲得這個函數的返回值: 
跟進去下個斷點:

附上進程:

把v0的類型改為string類型是這樣的:

v0 = 44e2e4457d4e252ca5b9fe9d20b3fea5
接下來就要看看這個函數的返回值了
v1.a(v2 + v1.b(v2, v0.getAsString("password")))
繼續下斷點:

v0 = "yaphetshan"
然后分析這個:
(v1.a(v2 + v1.b(v2, v0.getAsString("password"))).substring(0, 7))
下斷點,再調:

v0 = "ae56f99638285eb0743d8bf76d2b0c80e5cbb096"
然后取前七位:ae56f99(這就是登錄數據庫的密碼了,其實直接從最后一步那里下斷點就行了,我想一步步的看看加密算法的結果)
用ae56f99登錄數據庫找到:VGN0ZntIM2xsMF9Eb19ZMHVfTG92M19UZW5jM250IX0=
這一眼就是base64編碼,直接解碼就行了
Tctf{H3ll0_Do_Y0u_Lov3_Tenc3nt!}
三、ph0en1x_100
既然JEB動態調試這么好用,那就再來一個題(這個題就非常簡單了,直接一步到位)。
關鍵代碼就是這里,修復一下代碼在分析就是這樣的:
protected void onCreate(Bundle arg2) { super.onCreate(arg2); this.setContentView(0x7F040019); this.input_flag = this.findViewById(0x7F0C004F); } public void onGoClick(View arg5) { if(this.getSecret(this.getFlag()).equals(this.getSecret(this.encrypt(this.input_flag.getText().toString())))) { Toast.makeText(((Context)this), "Success", 1).show(); } else { Toast.makeText(((Context)this), "Failed", 1).show(); } }
if(this.getSecret(this.getFlag()).equals(this.getSecret(this.encrypt(this.input_flag.getText().toString()))))
如果這個if分支返回為真,就能讓程序正確運行。
然后equals的兩個參數在傳入之前都調用了getSecret()函數,所以這個函數就不再用分析了。
先看一下getflag()函數:
再看一下encrypt()函數:
終于可以用ida分析了:


上ida導入.h文件在改一下參數:

分析一下encrypt函數,發現邏輯很簡單,就是將傳進來的字符串的每個字符的ASCII碼減一。

對于getFlag函數直接用JEB動態調試該函數得到返回值即可:
下好斷點,附加進程:


string@4175:"ekfz@q2^x/t^fn0mF^6/^rbqanqntfg^E`hq|"
直接上腳本就行了:

四 easydex
apk沒有加固:

首先看看apk的java層:

發現只有一些資源文件,并沒有發現dex文件,所以dex文件應該也是動態加載出來的。
這里我嘗試用frida-dexdump想dump下正在運行的dex文件,但是啥也沒有:


所以就要分析so層了:
我想著先從jnionload或者java開始的來著,但是都沒有,然后我就想著從這個函數分析吧:

一看這么多邏輯,先動態看看程序是怎么走的吧:
先在這里下個斷點,可以找出程序在系統中(經過了異或運算)的釋放的dex文件的位置。


然后接著動態分析就能分析出程序的流程:
這個是我修復之后的代碼:


這個31分支這里就是解密dex文件的位置:

下面就是對解密出來的dex文件(從byte_7004開始,長度為0x35A0C是加密后的數據)進行的操作:

這里有兩種方法:
1.在刪除dex文件操作之前下個斷點,然后將手機中的dex文件pull到電腦上
2.使用idc腳本直接解密出dex文件:

然后就可以得到dex文件進行分析了。
但是再dex文件中并沒有發現明顯的邏輯:

然后將字符串解密:

然后再源apk文件中發現了two fish算法(這道題的關鍵應該是so層的分析,這個twofish算法我還是第一次聽說)

然后找個在線解密網站就行了:
