美文网首页ida
Android逆向练习 crackme NDKapp

Android逆向练习 crackme NDKapp

作者: h080294 | 来源:发表于2017-10-28 03:15 被阅读109次

应用如下,
1、点击执行功能,弹出弹窗;
2、弹窗中点击取消退出应用;点击确定后跳到注册页面。
3、注册页面完整注册后,弹窗提示,之后退出app

一、分析apk

1、class MyApp

加载了一个本地库,有三个native方法,initSN()、saveSN(String)、work()。在onCreate()中执行了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()方法,启动后读取MyApp的成员m,并把值赋给int i。然后根据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 = "-未注册";
    }
    for (;;)  # 这里的反编译是有问题的,通过上一篇章,我们知道这块是一个循环,不影响代码的阅读
    {
      setTitle("NDK保护与重启验证演示程序" + str);
      this.btn1 = ((Button)findViewById(2131165184));
      this.btn1.setOnClickListener(new MainActivity.1(this));
      return;
      if (i == 1) {
        str = "-正式版";
      } else if (i == 2) {
        str = "-专业版";
      } else if (i == 3) {
        str = "-企业版";
      } else if (i == 4) {
        str = "-专供版";
      } else {
        str = "-未知版";
      }
    }
  }
3、按钮的OnClickListener

跟进button的OnClickListener事件,先判断了MyApp的m成员是否为0,如果为0就走注册的逻辑。不为0,调用native的work()方法,然后弹对应的Toast提示--MainActivity.access$0(),其实就是private static String workString的值。而workString的值又是从work(String)方法而来。

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; #企业版
sput v0, Lcom/droider/ndkapp/MyApp;m:I

打包进行验证,发现点击执行功能后,还是弹出了需要注册的弹窗,所以直接赋值的路不通了。还是看onClick方法,我们之前分析到了要调用native的work方法,所以就得深入libjuan.so进行查看了。

三、分析so文件

我用的是mac,工具IDA,因为是初次使用,所以也记录下初始化过程。

1、导入jni.h

至于为什要要导入,是因为IDA不能识别JNI结构体,影响反汇编代码的阅读。
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

按照介绍,注释掉开头的 #include <sys/cdefs.h>,#include <stdarg.h>。将#define JNIEXPORT attribute ((visibility ("default")))注释掉,改成#define JNIEXPORT

image.png

导入完成后会有成功提示,如果报错,就安装报错的提示修改。注意copy文件,不要影响原ndk的jni文件。

2、structure中添加JNINativeInterface

structures选项卡汇总,将JNINativeInterface和JNIInvokeInterface添加进去。

添加成功后,回到主页面,在0x240处点击右键,可以看到已经能够解析出JNINativeInterface.GetStaticFiledID函数了。

3、搜索函数

app中有3个native方法,initSN()、saveSN(String)、work()。在左侧的function搜索函数名,发现并没有搜到有用的内容,所以到JNI_OnLoad中一探究竟。

在.text:000013AC LDR PC, [R12,#JNINativeInterface.RegisterNatives]这行,明显的看到了RegisterNatives,其实就是把本地函数和java类方法关联起来。

而RegisterNatives函数需要传进来class(native methods),此处传的是_data_start首地址。跟进看看发现数据如下,看到了熟悉的方法initSn、saveSn、work。所以得出结论:
n1对应着initSn()方法
n2对应着saveSn(String)方法
n3对应着work()方法

4、分析函数

前面的分析中,我们点击执行功能会调用work方法,所以先看n3。细节的地方还不太懂,能看出大概的意思。先执行n1(initSN)方法,然后getValue取到值,再对值分别做比较1、2、3、4,跳转各个分支,并且在最后都调用了callWork方法。

调用了com/droider/ndkapp/MainActivity类的work(String)方法,work(String)方法是给String workString赋值的,也就是toast的string文案。

回到n1(initSN)函数
先是打开(fopen)sd卡中的reg.dat文件,经过一些判断没问题后读到内存中(fread)。对比几个字符串是否相等后,跳转不同分支,但最后也都执行setValue方法。

setValue函数,其实就是给MyApp的m成员赋值的。

n2(saveSn)函数:
通过观察,写一个reg.dat文件,使用md5加密。

5、修改

n3函数主要是拿到initSN后的m值,根据对应的m值给出toast文案
n2函数就是写数据reg.data
n1函数主要是读数据,然后经过判断给MyApp的m成员赋值

所以直接修改n1的赋值相关逻辑是比较的好的方法。这里修改的思路是直接学习来的,仍以判断条件处入手。比如我们认定3号了(企业版),就得让setValue到“3号位置”。这里学习到的技巧就是直接修改字节码,让CMP比较结果为真即可。

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

查看字节码,该条指令字节码为00 00 50 E3,将0x15E7修改成E1,这样相当于CMP R0,R0,也就是比较结果为真。修改完成后重新打包安装。

6、成功

由于sd中已有reg.data文件,所以点击执行后直接弹toast提示,这样标明我们的破解成功了。

四、题外话

saveSn的时候采用的是MD5加密算法,开发者提供了4个加密的字符串12345678、2345678、32345678、42345678。用MD5验证下,分别对应着25d55ad283aa400af464c76d713c07ad、08e0750210f66396eb83957973705aad、b2db1185c9e5b88d9b70d7b3278a4947、18e56d777d194c4d589046d62801501c。

也就说4个注册码分别为上面的字符串

APK连接:
链接: https://pan.baidu.com/s/1i5CHQvV 密码: umqp

相关文章

网友评论

    本文标题:Android逆向练习 crackme NDKapp

    本文链接:https://www.haomeiwen.com/subject/snjdpxtx.html