BugKU:Timer(阿里CTF);打包,签名

题目描述:

给了一个Android的apk文件

安卓上安装一下,运行截图:

                                                                 BugKU:Timer(阿里CTF);打包,签名_第1张图片

由运行截图可以看出,这个程序是要求把时间耗尽,才会出flag,所以思路是修改时间(找到数字200000)。

首先ApkTool先解包一下:

                      BugKU:Timer(阿里CTF);打包,签名_第2张图片

得到编译过后的文件,文件夹下内容为:

                   BugKU:Timer(阿里CTF);打包,签名_第3张图片

打开AndroidMainfest.xml,其文件表示其主要活动问MainActivity:

BugKU:Timer(阿里CTF);打包,签名_第4张图片

找到lib文件夹下的.so文件,IDA打开提示有两种模式,经过一番折腾没效果:

                             BugKU:Timer(阿里CTF);打包,签名_第5张图片

将安装包放到jeb中,进行反编译:

 

                                  BugKU:Timer(阿里CTF);打包,签名_第6张图片

  • MainActivity代码:
package net.bluelotus.tomorrow.easyandroid;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    int beg;
    int k;
    int now;
    long t;

    static {
        System.loadLibrary("lhm");
    }

    public MainActivity() {
        super();
        this.beg = (((int)(System.currentTimeMillis() / 1000))) + 200000;
        this.k = 0;
        this.t = 0;
    }

    public static boolean is2(int arg4) {
        boolean v1 = true;
        if(arg4 > 3) {
            if(arg4 % 2 != 0 && arg4 % 3 != 0) {
                int v0 = 5;
                while(true) {
                    if(v0 * v0 <= arg4) {
                        if(arg4 % v0 != 0 && arg4 % (v0 + 2) != 0) {
                            v0 += 6;
                            continue;
                        }

                        return false;
                    }
                    else {
                        return v1;
                    }
                }

                return false;
            }

            v1 = false;
        }
        else if(arg4 <= 1) {
            v1 = false;
        }

        return v1;
    }

    protected void onCreate(Bundle arg7) {
        super.onCreate(arg7);
        this.setContentView(2130968600);
        View v2 = this.findViewById(2131492944);
        View v3 = this.findViewById(2131492945);
        Handler v0 = new Handler();
        v0.postDelayed(new Runnable(((TextView)v3), ((TextView)v2), v0) {
            public void run() {
                MainActivity.this.t = System.currentTimeMillis();
                MainActivity.this.now = ((int)(MainActivity.this.t / 1000));
                MainActivity.this.t = 1500 - MainActivity.this.t % 1000;
                this.val$tv2.setText("AliCTF");
                if(MainActivity.this.beg - MainActivity.this.now <= 0) {
                    this.val$tv1.setText("The flag is:");
                    this.val$tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(MainActivity.this.k) + "}");
                }

                if(MainActivity.is2(MainActivity.this.beg - MainActivity.this.now)) {
                    MainActivity.this.k += 100;
                }
                else {
                    --MainActivity.this.k;
                }

                this.val$tv1.setText("Time Remaining(s):" + (MainActivity.this.beg - MainActivity.this.now));
                this.val$handler.postDelayed(((Runnable)this), MainActivity.this.t);//延时函数
            }
        }, 0);
    }

    public boolean onCreateOptionsMenu(Menu arg3) {
        this.getMenuInflater().inflate(2131558400, arg3);
        return 1;
    }

    public boolean onOptionsItemSelected(MenuItem arg3) {
        boolean v1 = arg3.getItemId() == 2131492959 ? true : super.onOptionsItemSelected(arg3);
        return v1;
    }

    public native String stringFromJNI2(int arg1) {
    }
}

  • 其主要逻辑与猜测相符:现在的时间(秒)加上200000 - 现在的时间,代码为:
    //加200000
    this.beg = (((int)(System.currentTimeMillis() / 1000))) + 200000;
    
    //之后的减法操作,小于0则打印出flag
    if(MainActivity.this.beg - MainActivity.this.now <= 0) {
    	this.val$tv1.setText("The flag is:");
    	this.val$tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(MainActivity.this.k) + "}");
    }
    
  • 尝试着去修改MainActivity.smali    :

             BugKU:Timer(阿里CTF);打包,签名_第7张图片

  • 200000 的16进制为 0x30d40,查找到这一行的常量,将其修改为 0

     BugKU:Timer(阿里CTF);打包,签名_第8张图片

重新打包

这个操作会在其项目文件夹下生成一个dist文件夹,文件夹下就是新打包的apk:

                     BugKU:Timer(阿里CTF);打包,签名_第9张图片

签名

对于反编译的apk,我们可以通过jarsigner(存在于Java JDK的安装包中,配好Java环境,可以直接在命令行使用)来对它进行签名。

  • 生成keystore文件

签名需要keystore文件,可以使用keytool工具生成,一般Java环境都带有keytool命令,java环境配置好了直接可以在命令行使用。先把目录切换到前面说到的dist文件夹下,执行:

keytool -genkey -alias reverse1.keystore -keyalg RSA -validity 30000 -keystore reverse1.keystore

各个参数解释如下:

-genkey 产生证书文件 
-alias 产生别名 
-keystore 指定密钥库的.keystore文件 
-keyalg 指定密钥的算法,这里指定为RSA(非对称密钥算法) 
-validity 为证书有效天数

 输入上述命令之后会有一系列的输入,倒数第二步它会问你确认自己的输入正确吗,你打个 y 进去就可以了,这里我的口令设置的是123456

                 BugKU:Timer(阿里CTF);打包,签名_第10张图片

 

  • 签名apk

使用jarsigner

jarsigner -verbose -keystore reverse1.keystore 60bac132cc17b02c58f27e3724e6f202.apk reverse1.keystore
参数分别是:apk文件,keystore文件的别名

-verbose 指定生成详细输出 
-keystore 指定数字证书存储路径

运行成功结果截图: 

            BugKU:Timer(阿里CTF);打包,签名_第11张图片

最后安装到手机后发现flag里面包的是乱码,原来是自己的逻辑有错,它这里的k值是根据 is2 函数进行改变的输出也有着相应的改变,stringFromJNI2里面要的是k值,根据k值的变化会改变本地调用的接口返回的值。

所以说k的最终的值变的很关键,可以模拟它的调用过程算出来k的值,那个is2代码粘过来时需要删掉一个return flase;语句,因为那个语句不可达,this.val$handler.postDelayed(((Runnable)this), MainActivity.this.t);将这个函数进行延时,但是这个操作我看不太懂,app上显示的时间是每次减少一秒:

package test;

public class Home {
 
    public static boolean is2(int arg4) {
        boolean v1 = true;
        if(arg4 > 3) {
            if(arg4 % 2 != 0 && arg4 % 3 != 0) {
                int v0 = 5;
                while(true) {
                    if(v0 * v0 <= arg4) {
                        if(arg4 % v0 != 0 && arg4 % (v0 + 2) != 0) {
                            v0 += 6;
                            continue;
                        }

                        return false;
                    }
                    else {
                        return v1;
                    }
                }
                
            }

            v1 = false;
        }
        else if(arg4 <= 1) {
            v1 = false;
        }

        return v1;
    }
 
    public static void main(String args[]) {
        int time = 200000;
        int k = 0;
        while (time > 0) {
            if (is2(time)) {
                k += 100;
            }
            else {
                k--;
            }
            time--;    //因为app运行时,每一次刷新都是减一,代码我也没看懂
        }
        System.out.println(k);
    }
 
}

模拟出来的值为:1616384

  • 之后修改MainActivity$1.smali 中关于if(MainActivity.this.beg - MainActivity.this.now <= 0)的ARM汇编代码:
    不管判断条件是什么改成相反那就有200000秒的时间看flag,,

BugKU:Timer(阿里CTF);打包,签名_第12张图片

  • k值的修改
    16进制的k值为0x18aa00,这里进行了赋值,所以想要修改一下值可以在iget v3, v3 后添加一条指令const v3, 0x18aa00
    这里的v0,v1,v2,v3等都是寄存器,添加的指令就是寄存器的赋值
    BugKU:Timer(阿里CTF);打包,签名_第13张图片

之后进行以上的重新打包和签名的过程,然后安装的手机即可得到flag:

说明一下这里的TIme Remaining(s):0是因为我前面把 MainActivity.smali 中的   const v1, 0x30d40改成了0x0 才导致的

                                                         提交:      flag{Y0vAr3TimerMa3te7}

                      

 

 

 

知识点

  • 两个命令:
    1.apktool d test.apk 解包test.apk
    2.apktool b test 重打包test(没有后缀)
       新生成的apk文件在test文件夹下新生成的dist文件夹中
  • 安卓的三个工具的使用
    https://blog.csdn.net/dreamer2020/article/details/52761606
  • JNI
    JNI是JDK的一部分,用于为Java提供一个本地代码的接口。通过使用JNI编写的程序能够确保你的代码能够完全的移植到所有的平台。JNI使得运行在JVM虚拟机上的Java代码能够操作使用其它语言编写的应用程序和库,比如 C/C++以及汇编语言等。此外JNI提供的某些API还允许你把JVM嵌入到本地应用程序中

你可能感兴趣的:(逆向工程)