Android逆向練習(xí) crackme NDKapp

應(yīng)用如下,
1、點(diǎn)擊執(zhí)行功能,彈出彈窗;
2、彈窗中點(diǎn)擊取消退出應(yīng)用;點(diǎn)擊確定后跳到注冊(cè)頁(yè)面。
3、注冊(cè)頁(yè)面完整注冊(cè)后,彈窗提示,之后退出app

一、分析apk

1、class MyApp

加載了一個(gè)本地庫(kù),有三個(gè)native方法,initSN()、saveSN(String)、work()。在onCreate()中執(zhí)行了initSN()方法。

public class MyApp extends Application
{
  public static int m = 0;
  
  static{
    System.loadLibrary("juan"); }

  public native void initSN();

  public void onCreate(){
    initSN();
    super.onCreate();}

  public native void saveSN(String paramString);

  public native void work();
}
2、class MainActivity

主要看onCreate()方法,啟動(dòng)后讀取MyApp的成員m,并把值賦給int i。然后根據(jù)i的值,定義不同的string。

  private static String workString;
  public void work(String paramString){
    workString = paramString;
  }

  public void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130903040);
    ((MyApp)getApplication());
    int i = MyApp.m;
    String str;
    if (i == 0) {
      str = "-未注冊(cè)";
    }
    for (;;)  # 這里的反編譯是有問(wèn)題的,通過(guò)上一篇章,我們知道這塊是一個(gè)循環(huán),不影響代碼的閱讀
    {
      setTitle("NDK保護(hù)與重啟驗(yàn)證演示程序" + str);
      this.btn1 = ((Button)findViewById(2131165184));
      this.btn1.setOnClickListener(new MainActivity.1(this));
      return;
      if (i == 1) {
        str = "-正式版";
      } else if (i == 2) {
        str = "-專(zhuān)業(yè)版";
      } else if (i == 3) {
        str = "-企業(yè)版";
      } else if (i == 4) {
        str = "-專(zhuān)供版";
      } else {
        str = "-未知版";
      }
    }
  }
3、按鈕的OnClickListener

跟進(jìn)button的OnClickListener事件,先判斷了MyApp的m成員是否為0,如果為0就走注冊(cè)的邏輯。不為0,調(diào)用native的work()方法,然后彈對(duì)應(yīng)的Toast提示--MainActivity.access$0(),其實(shí)就是private static String workString的值。而workString的值又是從work(String)方法而來(lái)。

class MainActivity$1
  implements View.OnClickListener
{
  MainActivity$1(MainActivity paramMainActivity) {}
  
  public void onClick(View paramView)
  {
    ((MyApp)this.this$0.getApplication());
    if (MyApp.m == 0)
    {
      this.this$0.doRegister();
      return;
    }
    ((MyApp)this.this$0.getApplication()).work();
    Toast.makeText(this.this$0.getApplicationContext(), MainActivity.access$0(), 0).show();
  }
}

二、嘗試修改app

1、在MyApp中,重新給成員m賦值

修改方法就不介紹了,直接在smali添加兩行,將自定義的值賦給m。

const /4 v0, 0x3; #企業(yè)版
sput v0, Lcom/droider/ndkapp/MyApp;m:I

打包進(jìn)行驗(yàn)證,發(fā)現(xiàn)點(diǎn)擊執(zhí)行功能后,還是彈出了需要注冊(cè)的彈窗,所以直接賦值的路不通了。還是看onClick方法,我們之前分析到了要調(diào)用native的work方法,所以就得深入libjuan.so進(jìn)行查看了。

三、分析so文件

我用的是mac,工具IDA,因?yàn)槭浅醮问褂?,所以也記錄下初始化過(guò)程。

1、導(dǎo)入jni.h

至于為什要要導(dǎo)入,是因?yàn)镮DA不能識(shí)別JNI結(jié)構(gòu)體,影響反匯編代碼的閱讀。
File -- Liad file -- Parse C header file,選擇jni.h文件。這里我用了Android sdk的NDK目錄下的jni文件:
/android-sdk-macosx/ndk-bundle/platforms/android-24/arch-arm/usr/include/jni.h

按照介紹,注釋掉開(kāi)頭的 #include <sys/cdefs.h>,#include <stdarg.h>。將#define JNIEXPORT attribute ((visibility ("default")))注釋掉,改成#define JNIEXPORT

image.png

導(dǎo)入完成后會(huì)有成功提示,如果報(bào)錯(cuò),就安裝報(bào)錯(cuò)的提示修改。注意copy文件,不要影響原ndk的jni文件。

2、structure中添加JNINativeInterface

structures選項(xiàng)卡匯總,將JNINativeInterface和JNIInvokeInterface添加進(jìn)去。

添加成功后,回到主頁(yè)面,在0x240處點(diǎn)擊右鍵,可以看到已經(jīng)能夠解析出JNINativeInterface.GetStaticFiledID函數(shù)了。

3、搜索函數(shù)

app中有3個(gè)native方法,initSN()、saveSN(String)、work()。在左側(cè)的function搜索函數(shù)名,發(fā)現(xiàn)并沒(méi)有搜到有用的內(nèi)容,所以到JNI_OnLoad中一探究竟。

在.text:000013AC LDR PC, [R12,#JNINativeInterface.RegisterNatives]這行,明顯的看到了RegisterNatives,其實(shí)就是把本地函數(shù)和java類(lèi)方法關(guān)聯(lián)起來(lái)。

而RegisterNatives函數(shù)需要傳進(jìn)來(lái)class(native methods),此處傳的是_data_start首地址。跟進(jìn)看看發(fā)現(xiàn)數(shù)據(jù)如下,看到了熟悉的方法initSn、saveSn、work。所以得出結(jié)論:
n1對(duì)應(yīng)著initSn()方法
n2對(duì)應(yīng)著saveSn(String)方法
n3對(duì)應(yīng)著work()方法

4、分析函數(shù)

前面的分析中,我們點(diǎn)擊執(zhí)行功能會(huì)調(diào)用work方法,所以先看n3。細(xì)節(jié)的地方還不太懂,能看出大概的意思。先執(zhí)行n1(initSN)方法,然后getValue取到值,再對(duì)值分別做比較1、2、3、4,跳轉(zhuǎn)各個(gè)分支,并且在最后都調(diào)用了callWork方法。

調(diào)用了com/droider/ndkapp/MainActivity類(lèi)的work(String)方法,work(String)方法是給String workString賦值的,也就是toast的string文案。

回到n1(initSN)函數(shù)
先是打開(kāi)(fopen)sd卡中的reg.dat文件,經(jīng)過(guò)一些判斷沒(méi)問(wèn)題后讀到內(nèi)存中(fread)。對(duì)比幾個(gè)字符串是否相等后,跳轉(zhuǎn)不同分支,但最后也都執(zhí)行setValue方法。

setValue函數(shù),其實(shí)就是給MyApp的m成員賦值的。

n2(saveSn)函數(shù):
通過(guò)觀察,寫(xiě)一個(gè)reg.dat文件,使用md5加密。

5、修改

n3函數(shù)主要是拿到initSN后的m值,根據(jù)對(duì)應(yīng)的m值給出toast文案
n2函數(shù)就是寫(xiě)數(shù)據(jù)reg.data
n1函數(shù)主要是讀數(shù)據(jù),然后經(jīng)過(guò)判斷給MyApp的m成員賦值

所以直接修改n1的賦值相關(guān)邏輯是比較的好的方法。這里修改的思路是直接學(xué)習(xí)來(lái)的,仍以判斷條件處入手。比如我們認(rèn)定3號(hào)了(企業(yè)版),就得讓setValue到“3號(hào)位置”。這里學(xué)習(xí)到的技巧就是直接修改字節(jié)碼,讓CMP比較結(jié)果為真即可。

...........
.text:000015DC                 ADD     R1, PC, R1      ; "b2db1185c9e5b88d9b70d7b3278a4947"
.text:000015E0                 BL      strcmp
.text:000015E4                 CMP     R0, #0
.text:000015E8                 BEQ     loc_163C
...........

查看字節(jié)碼,該條指令字節(jié)碼為00 00 50 E3,將0x15E7修改成E1,這樣相當(dāng)于CMP R0,R0,也就是比較結(jié)果為真。修改完成后重新打包安裝。

6、成功

由于sd中已有reg.data文件,所以點(diǎn)擊執(zhí)行后直接彈toast提示,這樣標(biāo)明我們的破解成功了。

四、題外話(huà)

saveSn的時(shí)候采用的是MD5加密算法,開(kāi)發(fā)者提供了4個(gè)加密的字符串12345678、2345678、32345678、42345678。用MD5驗(yàn)證下,分別對(duì)應(yīng)著25d55ad283aa400af464c76d713c07ad、08e0750210f66396eb83957973705aad、b2db1185c9e5b88d9b70d7b3278a4947、18e56d777d194c4d589046d62801501c。

也就說(shuō)4個(gè)注冊(cè)碼分別為上面的字符串

APK連接:
鏈接: https://pan.baidu.com/s/1i5CHQvV 密碼: umqp

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容