欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

BugKuCTF 逆向 LoopAndLoop

程序员文章站 2022-05-15 11:24:39
...

1.

下载文件,发现是个APK文件,用模拟器装一下(这里我用的是BlueStack)。

BugKuCTF 逆向 LoopAndLoop

随便输入几个数字,显示“Not Right”,应该是输入正确的密码就能获得flag,好的,我们打开JEB进行反编译。

2.

打开MainActivity,核心代码如下。

 

protected void onCreate(Bundle arg6) {
        super.onCreate(arg6);
        this.setContentView(017701000030);
        this.findViewById(0x7F0C0052).setOnClickListener(new View$OnClickListener(this.findViewById(0x7F0C0050), this.findViewById(0x7F0C0051), this.findViewById(0x7F0C0053)) {
            public void onClick(View arg7) {
                int v1;
                String v2 = this.val$ed.getText().toString();
                try {
                    v1 = Integer.parseInt(v2);
                }
                catch(NumberFormatException v0) {
                    this.val$tv1.setText("Not a Valid Integer number");
                    return;
                }

                if(MainActivity.this.check(v1, 99) == 0x6D6F1462) {  // 1835996258
                    this.val$tv1.setText("The flag is:");
                    this.val$tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(v1) + "}");
                }
                else {
                    this.val$tv1.setText("Not Right!");
                }
            }
        });
    }

 

可以看到这段代码先对我们输入的字符串进行了检测,如果格式不对就抛出异常,这个暂且不用管。其后有一个判断,调用check方法将我们输入的值与99进行一番操作,如果返回值与1835996258相等就输出flag。好的,接下来看看check函数对我们的输入做了什么。

3.

由于check函数在native层,我们需要用IDA打开liblhm.so。找到它的位置,反编译后的代码如下:

int __fastcall Java_net_bluelotus_tomorrow_easyandroid_MainActivity_chec(int a1, int a2, int a3, int a4)
{
  int v4; // r4
  int v5; // r7
  int result; // r0
  int v7; // [sp+Ch] [bp-34h]
  int v8; // [sp+10h] [bp-30h]
  int v9; // [sp+14h] [bp-2Ch]
  int v10; // [sp+1Ch] [bp-24h]
  int v11; // [sp+20h] [bp-20h]
  int v12; // [sp+24h] [bp-1Ch]

  v9 = a2;
  v8 = a4;
  v4 = a1;
  v7 = a3;
  v5 = (*(int (**)(void))(*(_DWORD *)a1 + 24))();
  v10 = _JNIEnv::GetMethodID(v4, v5, "check1", "(II)I");
  v11 = _JNIEnv::GetMethodID(v4, v5, "check2", "(II)I");
  v12 = _JNIEnv::GetMethodID(v4, v5, "check3", "(II)I");
  if ( v8 - 1 <= 0 )
    result = v7;
  else
    result = _JNIEnv::CallIntMethod(v4, v9, *(&v10 + 2 * v8 % 3), v7, v8 - 1);
  return result;
}

看一看这段代码,当v8-1<=0时返回第一个参数,为终止条件,*(&v10 + 2 * v8 % 3)决定了调用三个check方法中的哪一个,当2*v8%3=0时调用check1,等于1时调用check2,等于2时调用check3,v7和v8-1为传入方法的变量。总结一下,基本流程为传入两个值,第一个值是我们的输入值,通过调用不同的方法后改变其数值;第二个值初始值为99,用来控制流程终止以及判断调用check1,check2,check3中的哪一个函数,每次调用时第二个值都减1,当返回值等于1835996258时输出flag。

4.

知道基本逻辑后,我们可以写出算法求解答案了。

#include <bits/stdc++.h>

using namespace std;

int main()
{
	long long num=99;
	long long flag=1835996258;
	while(num-1>0)
		{
			long long tmp=num*2%3;
			if(tmp==0)
				for(long long i=0;i<100;i++)
					flag-=i;
			if(tmp==1)
				if((num-1)%2==0)
					for(long long i=0;i<1000;i++)
						flag-=i;
				else
					for(long long i=0;i<1000;i++)
						flag+=i;
			if(tmp==2)
				for(long long i=0;i<10000;i++)
					flag-=i;
			num--;
		}
	cout<<flag<<endl;
	return 0;
}