flag格式
alictf{ }

下载链接: https://pan.baidu.com/s/1uvsl_xusNMMnnrjOzuoffg 密码: me4j

本题解压后是一个apk。。。b53506eba384796c651d91913aa76d6e.apk,照例先拖到模拟器运行下。

打开后,又是这么一个花俏的界面,随便输入字符显示 Not Right。。。先用LEB打开瞧瞧。

package net.bluelotus.tomorrow.easyandroid;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View$OnClickListener;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("lhm");
    }

    public MainActivity() {
        super();
    }

    public native int chec(int arg1, int arg2) {
    }

    public int check(int arg2, int arg3) {
        return this.chec(arg2, arg3);
    }

    public int check1(int arg4, int arg5) {
        int v1 = arg4;
        int v0 = 1;
    label_2:
        if(v0 < 100) {
            v1 += v0;
            ++v0;
            goto label_2;
        }

        return this.chec(v1, arg5);
    }

    public int check2(int arg5, int arg6) {
        int v2;
        int v3 = 1000;
        int v1 = arg5;
        if(arg6 % 2 == 0) {
            int v0;
            for(v0 = 1; v0 < v3; ++v0) {
                v1 += v0;
            }

            v2 = this.chec(v1, arg6);
        }
        else {
            for(v0 = 1; v0 < v3; ++v0) {
                v1 -= v0;
            }

            v2 = this.chec(v1, arg6);
        }

        return v2;
    }

    public int check3(int arg4, int arg5) {
        int v1 = arg4;
        int v0;
        for(v0 = 1; v0 < 10000; ++v0) {
            v1 += v0;
        }

        return this.chec(v1, arg5);
    }

    public String messageMe(String arg3) {
        return "LoopOk" + arg3;
    }

    protected void onCreate(Bundle arg6) {
        super.onCreate(arg6);
        this.setContentView(0x7F040018);
        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) == 1835996258) {
                    this.val$tv1.setText("The flag is:");
                    this.val$tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(v1) + "}");
                }
                else {
                    this.val$tv1.setText("Not Right!");
                }
            }
        });
    }

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

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

    public native String stringFromJNI2(int arg1) {
    }
}

我的天,这代码有点长,根据之前的做题经验,要先看onCreate函数。所以马上就可以定位到关键程序了。

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

又是stringFromJNI2这个东东,这个东东调用了本地的.c或者.cpp的程序,此外,还有chec也是本地函数。

所以其实我们要去逆出对应的程序,在哪里嘞?

Libraries下有个.so文件,导出来,用IDA打开~~~~搜索stringFrom

进去后,F5得到伪代码:

int __fastcall Java_net_bluelotus_tomorrow_easyandroid_MainActivity_stringFromJNI2(int *a1, int a2, int a3)
{
  int v3; // ST10_4
  int v4; // ST24_4
  int v5; // ST08_4
  int v6; // ST0C_4
  int v7; // ST18_4
  int v8; // r2
  char v10; // [sp+28h] [bp-30h]
  char v11; // [sp+29h] [bp-2Fh]
  char v12; // [sp+2Ah] [bp-2Eh]
  char v13; // [sp+2Bh] [bp-2Dh]
  char v14; // [sp+2Ch] [bp-2Ch]
  char v15; // [sp+2Dh] [bp-2Bh]
  char v16; // [sp+2Eh] [bp-2Ah]
  char v17; // [sp+2Fh] [bp-29h]
  char v18; // [sp+30h] [bp-28h]
  char v19; // [sp+31h] [bp-27h]
  char v20; // [sp+32h] [bp-26h]
  char v21; // [sp+33h] [bp-25h]

  v3 = a3 / 1000 % 10;
  v4 = a3 / 10000 % 10;
  v5 = a3 % 10 & 0xFF;
  v10 = v3 + a3 % 10 * v4;
  v6 = a3 / 1000000 % 10 & 0xFF;
  v7 = a3 / 100 % 10 & 0xFF;
  v11 = v4 * v6 + 10 * v7 + 3;
  v12 = 10 * (v3 + v4);
  v13 = v4 * v6;
  v14 = 19 * (a3 / 100000 % 10) + 2;
  v15 = v5 * v6 + 1;
  v16 = v5 * v6;
  v17 = 12 * v7;
  v19 = 12 * v7 + 3;
  v18 = 10 * (v3 + v4) + v3;
  v8 = *a1;
  v20 = 2 * (v4 * v6 + 10 * v7 - 37);
  v21 = 0;
  return (*(int (__fastcall **)(int *, char *))(v8 + 668))(a1, &v10);
}

由此找到

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);
  return result;
}

chec伪代码其实并不是很清晰:调用Java层的三个check函数对输入数字进行处理,要得到真正的逻辑我们还需要看汇编。

.text:00000E8C ; __unwind {
.text:00000E8C                 PUSH    {R4-R7,LR}
.text:00000E8E                 SUB     SP, SP, #0x2C
.text:00000E90                 STR     R1, [SP,#0x40+var_2C]
.text:00000E92                 STR     R3, [SP,#0x40+var_30]
.text:00000E94                 LDR     R1, =(aNetBluelotusTo - 0xE9E)
.text:00000E96                 LDR     R3, [R0]
.text:00000E98                 MOVS    R4, R0
.text:00000E9A                 ADD     R1, PC          ; "net/bluelotus/tomorrow/easyandroid/Main"...
.text:00000E9C                 LDR     R3, [R3,#0x18]
.text:00000E9E                 STR     R2, [SP,#0x40+var_34]
.text:00000EA0                 BLX     R3
.text:00000EA2                 LDR     R6, =(aIiI - 0xEAC)
.text:00000EA4                 LDR     R2, =(aCheck1 - 0xEB2)
.text:00000EA6                 MOVS    R7, R0
.text:00000EA8                 ADD     R6, PC          ; "(II)I"
.text:00000EAA                 MOVS    R3, R6
.text:00000EAC                 MOVS    R1, R7
.text:00000EAE                 ADD     R2, PC          ; "check1"
.text:00000EB0                 MOVS    R0, R4
.text:00000EB2                 BL      _ZN7_JNIEnv11GetMethodIDEP7_jclassPKcS3_ ; _JNIEnv::GetMethodID(_jclass *,char const*,char const*)
.text:00000EB6                 LDR     R2, =(aCheck2 - 0xEC2)
.text:00000EB8                 MOVS    R3, R6
.text:00000EBA                 STR     R0, [SP,#0x40+var_24]
.text:00000EBC                 MOVS    R1, R7
.text:00000EBE                 ADD     R2, PC          ; "check2"
.text:00000EC0                 MOVS    R0, R4
.text:00000EC2                 BL      _ZN7_JNIEnv11GetMethodIDEP7_jclassPKcS3_ ; _JNIEnv::GetMethodID(_jclass *,char const*,char const*)
.text:00000EC6                 LDR     R2, =(aCheck3 - 0xED6)
.text:00000EC8                 ADD     R5, SP, #0x40+var_24
.text:00000ECA                 STR     R0, [R5,#4]
.text:00000ECC                 MOVS    R3, R6
.text:00000ECE                 MOVS    R0, R4
.text:00000ED0                 MOVS    R1, R7
.text:00000ED2                 ADD     R2, PC          ; "check3"
.text:00000ED4                 BL      _ZN7_JNIEnv11GetMethodIDEP7_jclassPKcS3_ ; _JNIEnv::GetMethodID(_jclass *,char const*,char const*)
.text:00000ED8                 LDR     R6, [SP,#0x40+var_30]
.text:00000EDA                 STR     R0, [R5,#8]
.text:00000EDC                 SUBS    R6, #1
.text:00000EDE                 CMP     R6, #0
.text:00000EE0                 BLE     loc_EFE
.text:00000EE2                 LDR     R3, [SP,#0x40+var_30]
.text:00000EE4                 MOVS    R1, #3
.text:00000EE6                 LSLS    R0, R3, #1
.text:00000EE8                 BL      __aeabi_idivmod
.text:00000EEC                 LSLS    R1, R1, #2
.text:00000EEE                 LDR     R2, [R1,R5]
.text:00000EF0                 MOVS    R0, R4
.text:00000EF2                 STR     R6, [SP,#0x40+var_40]
.text:00000EF4                 LDR     R1, [SP,#0x40+var_2C]
.text:00000EF6                 LDR     R3, [SP,#0x40+var_34]
.text:00000EF8                 BL      _ZN7_JNIEnv13CallIntMethodEP8_jobjectP10_jmethodIDz ; _JNIEnv::CallIntMethod(_jobject *,_jmethodID *,...)
.text:00000EFC                 B       loc_F00

这里 _JNIEnv::CallIntMethod*少传了一个参数
因此调用的应该是:

result = _JNIEnv::CallIntMethod(Evn, obj, *(&check1_id + 2 * _99 % 3), _99 - 1);

chec其实起一个选择的作用,根据第二个参数乘2对3取模的结果,决定调用哪一个check函数。

写个逆向算法得到密码:

a = 1835996258
i = 99
while i > 0:
    i -= 1
    if i > 0:
        now = ((i + 1) * 2) % 3 #注意一下这里是i+1,因为这里判断的是没有减一的第二个参数
        if now == 0:
            a -= 4950
        elif now == 1:
            if i % 2 == 0:
                a -= 499500
            else:
                a += 499500
        else:
            a -= 49995000
print a

输入236492408

alictf{Jan6N100p3r}