一次有关算法的安卓CTF

某年的CTF大赛题目
总结:遇到算法别头大,耐心、细心,让自己静下来。

点开之后长这样子,随便输入几个字符,提示错误。
在这里插入图片描述
查壳发现没有壳。
直接使用Jeb进行反编译。

在这里插入图片描述

看出来了,突破口是this.b.check()这个函数。

package ctf.bobbydylan;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class M extends T {
    public M() {
        super();
    }

    public void check(String arg10) {
        String v0_1;
        int v7 = 15;
        int v6 = 7;
        int v1 = 0;
        int v5 = 5;
        if(arg10.length() != 16) {
            throw new RuntimeException();
        }

        try {
            v0_1 = this.getKey();
        }
        catch(Exception v0) {
            v0_1 = this.getKey();
            System.arraycopy(v0_1, 0, arg10, v5, v5);
        }

        int[] v2 = new int[16];
        v2[0] = 0;
        v2[12] = 14;
        v2[10] = v6;
        v2[14] = v7;
        v2[v7] = 42;
        int v4 = 3;
        try {
            v2[1] = v4;
            v2[5] = 5;
            System.out.println();
        }
        catch(Exception v3) {
            v2[v5] = 37;
            v2[1] = 85;
        }

        v2[6] = v7;
        v2[2] = 13;
        v2[3] = 19;
        v2[11] = 68;
        v2[4] = 85;
        v2[13] = v5;
        v2[9] = v6;
        v2[v6] = 78;
        v2[8] = 22;
        while(v1 < arg10.length()) {
            if((v2[v1] & 0xFF) != ((arg10.charAt(v1) ^ v0_1.charAt(v1 % v0_1.length())) & 0xFF)) {
                throw new RuntimeException();
            }

            ++v1;
        }
    }

    public String getKey() {
        return "bobbydylan";
    }

    public void onCreate(Bundle arg4) {
        super.onCreate(arg4);
        this.setContentView(0x7F030000);
        this.startService(new Intent(((Context)this), P.class));
        this.findViewById(0x7F060001).setOnClickListener(new a(this, this.findViewById(0x7F060000)));
    }

    protected void onPause() {
        this.stopService(new Intent(((Context)this), P.class));
        super.onPause();
    }
}

看到这个check,很长说实话。

里面嵌套了好几层的try catch语句,我不确定这个程序大体流程执行下去会是怎样的,也许在try看起来怎么都不会有异常抛出的地方就有异常了呢,我不想继续审代码了,还是动态调试更舒服。

Jeb动态调试

在这里插入图片描述
找到对应进程,不解释。
Jeb没有调试伪代码的功能,所以就对着smali代码和伪代码一起看吧。

这里的逻辑经过调通,只要输入的字符串长度等于16,代码逻辑就不会跳进catch里。
在这里插入图片描述
经过阅读,其实只要字符串长度满足16,和下面的算法即可。

在这里插入图片描述
其实也就是

v2[v1] = arg10.charAt(v1) ^ v0_1.charAt(v1 % v0_1.length())

这个等式成立就行。
其中v2是死的,v0_1是getKey()接口返回的"bobbydylan"也是死的。

这里我们可以通过Jeb动态调试的时候看到v2和v0_1的值。
求出来的args10就是我们要的flag。
如果v2和v0_1类型不对,显示的值不太好观察时,直接修改它们的类型就可以。
在这里插入图片描述
直接修改类型。
在这里插入图片描述
在这里插入图片描述

我们知道对一个数异或两次,其实就等于没有做任何运算。
所以这个算法可以这样去做。

v2[v1] ^ v0_1.charAt(v1 % 8) = args.charAt(v1)

贴上C语言代码

#include <stdio.h>
#include <stdlib.h>
int main(){
	int v2[16] = {0,3,13,19,85,5,15,78,0,7,7,68,14,5,15,42};
	char v0_1[] = "bobdylan";
	for(int i = 0; i < 16; i++){
		int nIndex = i % 8;
		int nTmp = v0_1[nIndex];
		printf("%c", v2[i] ^ nTmp);
	}
}

我使用go,结果给我的是这个玩意,而且还费了我不少时间。
在这里插入图片描述
在这里插入图片描述
很明显不对。

有可能是自己太久没写代码或者当时使用go很少做异或或者ascii之类的计算吧,果然底层运算还是选C靠谱。
在这里插入图片描述

在这里插入图片描述

结束。

发布了53 篇原创文章 · 获赞 36 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/nini_boom/article/details/105730529