首页
论坛
专栏
课程

[原创]看雪.纽盾 KCTF晋级赛2019 Q2 第五题 丛林的秘密

2019-6-18 15:31 451

[原创]看雪.纽盾 KCTF晋级赛2019 Q2 第五题 丛林的秘密

2019-6-18 15:31
451
用JADX找到MainActivity
public class MainActivity extends AppCompatActivity {
    private Button button1;
    private EditText eText1;
    private TextView txView1;
    public String u = gogogoJNI.sayHello();

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

    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView((int) R.layout.activity_main);
        this.eText1 = (EditText) findViewById(R.id.editText);
        this.txView1 = (TextView) findViewById(R.id.textView);
        ((WebView) findViewById(R.id.text1View)).loadUrl(this.u);
        ((WebView) findViewById(R.id.text1View)).getSettings().setJavaScriptEnabled(true);
        this.button1 = (Button) findViewById(R.id.button);
        this.button1.setOnClickListener(new OnClickListener() {
            public void onClick(View view) {
                if (gogogoJNI.check_key(MainActivity.this.eText1.getText().toString()) == 1) {
                    MainActivity.this.txView1.setText("Congratulations!");
                } else {
                    MainActivity.this.txView1.setText("Not Correct!");
                }
            }
        });
    }
}
看起来是调用了gogogo文件里的check_key函数,但是上面出现了一个很可疑的东西:WebView,并且加载sayHello返回的网址。
那我们用IDA打开 libgogogo.so
先看Java_com_example_assemgogogo_gogogoJNI_sayHello函数
int __fastcall Java_com_example_assemgogogo_gogogoJNI_sayHello(JNIEnv *a1)
{
  int v1; // r11
  JNIEnv *v2; // r4
  int i; // r0
  int v5; // [sp+0h] [bp-98h]
  char v6; // [sp+15h] [bp-83h]
  int v7; // [sp+88h] [bp-10h]

  v7 = v1;
  v2 = a1;
  _aeabi_memclr8(&v5, 128);
  for ( i = 0; i != 21; ++i )
    *((_BYTE *)&v5 + i) = byte_2D28[i] ^ 0x66;  // 异或解密,结果是http://127.0.0.1:8000
  v6 = 0;
  return ((int (__fastcall *)(JNIEnv *, int *))(*v2)->NewStringUTF)(v2, &v5);
}
那 MainActivity加载的就是本机的8000端口。但是在dex文件没发现类似网页的内容,我们继续分析so文件
先看JNI_OnLoad
signed int JNI_OnLoad()
{
  j_inti_proc();
  return 65540;
}
只调用了一个j_inti_proc, j_inti_proc里面又调用了inti_proc
int inti_proc()
{
  char *v0; // r0
  signed int v1; // r1
  signed int i; // r6
  struct addrinfo **v3; // r0
  int v4; // r4
  struct addrinfo *v5; // r5
  int result; // r0
  int arg; // [sp+8h] [bp-70h]
  int v8; // [sp+Ch] [bp-6Ch]
  int v9; // [sp+10h] [bp-68h]
  struct addrinfo *pai; // [sp+14h] [bp-64h]
  struct addrinfo req; // [sp+18h] [bp-60h]
  char v12[32]; // [sp+38h] [bp-40h]
  int v13; // [sp+58h] [bp-20h]

  v0 = &mm0;
  v1 = 34291;
  v9 = 1;
  while ( v1 )
  {
    --v1;
    *v0 ^= 0x67u;
    ++v0;
  }
  i = 1;
  *(_QWORD *)&req.ai_protocol = 0LL;
  *(_QWORD *)&req.ai_addr = 0LL;
  req.ai_family = 0;
  req.ai_flags = 1;
  req.ai_socktype = 1;
  req.ai_next = 0;
  if ( getaddrinfo(0, "8000", &req, &pai) )
    goto LABEL_19;
  v3 = &pai;
  i = 1;
  while ( 1 )
  {
    v5 = *v3;
    if ( !*v3 )
    {
      i = 2;
      goto LABEL_19;
    }
    v4 = socket(v5->ai_family, v5->ai_socktype, v5->ai_protocol);
    if ( v4 != -1 )
      break;
LABEL_10:
    v3 = &v5->ai_next;
  }
  if ( setsockopt(v4, 1, 2, &v9, 4u) == -1 )
    goto LABEL_19;
  if ( bind(v4, (const struct sockaddr *)v5->ai_canonname, v5->ai_addrlen) == -1 )
  {
    close(v4);
    goto LABEL_10;
  }
  freeaddrinfo(pai);
  if ( listen(v4, 128) == -1 )
  {
    i = 1;
  }
  else
  {
    for ( i = 0; i != 32; i += 4 )
    {
      arg = v4;
      v8 = 0;
      pthread_create((pthread_t *)&v12[i], 0, (void *(*)(void *))nullsub_, &arg);
    }
    sock_fd_g = v4;
  }
LABEL_19:
  result = _stack_chk_guard - v13;
  if ( _stack_chk_guard == v13 )
    result = i;
  return result;
}
发现了初始化socket的代码,而且也是监视8000端口。代码一开始貌似在解密什么,尝试把mm0处的数据导出解密一下

我们要找的html就在这, 由于完整html代码太长,我就放在附件吧。 
保存到html文件里面后,用chrome调试。JS代码调用了check_key来检查key, 而check_key这个函数在WebAssembly实现。

1024-1055这个空间是用来存放key的,大小刚好32。
WebAssembly 的代码读起来太费劲,而且这个wasm代码量略多,人工容易出错,我们把数据另存为一份wasm文件,用wasm2c反编译回C文件,然后再编译一次C文件,再用IDA分析编译后的文件。
BOOL check_key()
{
  BOOL result; // eax

  if ( ++wasm_rt_call_stack_depth > 0x1F4u )
    wasm_rt_trap(7);
  o(1024, 1025, 1026, 1027);
  oo(1028, 1029, 1030, 1031);
  ooo(1032, 1033, 1034, 1035);
  oooo(1036, 1037, 1038, 1039);
  ooooo(1040, 1041, 1042, 1043);
  oooooo(1044, 1045, 1046, 1047);
  ooooooo(1048, 1049, 1050, 1051);
  oooooooo(1052, 1053, 1054, 1055);
  result = xxx();
  --wasm_rt_call_stack_depth;
  return result;
}
那些o函数对key做异或,每位都不一样,分析得到异或数组{24,9,3,107,1,90,50,87,48,93,64,70,43,70,86,61,2,67,23,0,50,83,31,38,42,1,0,16,16,30,64,0}
xxx函数是将key代入三十二元一次方程组,伪代码太长就不放了,有IDA优化过,整理不用费多少时间。整理完后用z3解方程,得到结果
[v28 = 51, v12 = 101, v14 = 48, v2 = 108, v10 = 49, v20 = 116, v3 = 117, v29 = 109, v15 = 99, v30 = 83, v23 = 95, v32 = 51, v19 = 116, v7 = 117, v4 = 102, v11 = 95, v5 = 51, v9 = 115, v16 = 95, v24 = 101, v26 = 105, v18 = 49, v33 = 51, v6 = 115, v25 = 109, v31 = 48, v22 = 108, v8 = 95, v17 = 101, v27 = 116, v13 = 100, v21 = 49]
然后我们调整一下乱序的解,异或解密后就能得到key了
if __name__=='__main__':
    resultindex={'v33':1054,'v32':1055,'v31':1025,'v30':1024,'v29':1026,'v28':1027,'v27':1028,'v26':1029,'v25':1030,'v24':1031,'v23':1032,'v22':1033,'v21':1034,'v20':1035,'v19':1036,'v18':1037,'v17':1038,'v16':1039,'v15':1040,'v14':1041,'v13':1042,'v12':1043,'v11':1044,'v10':1045,'v9':1046,'v8':1047,'v7':1048,'v6':1049,'v5':1050,'v4':1051,'v3':1052,'v2':1053}
    result={'v28' : 51,'v12' : 101,'v14' : 48,'v2' : 108,'v10' : 49,'v20' : 116,'v3' : 117,'v29' : 109,'v15' : 99,'v30' : 83,'v23' : 95,'v32' : 51,'v19' : 116,'v7' : 117,'v4' : 102,'v11' : 95,'v5' : 51,'v9' : 115,'v16' : 95,'v24' : 101,'v26' : 105,'v18' : 49,'v33' : 51,'v6' : 115,'v25' : 109,'v31' : 48,'v22' : 108,'v8' : 95,'v17' : 101,'v27' : 116,'v13' : 100,'v21' : 49}
    xor=[24,9,3,107,1,90,50,87,48,93,64,70,43,70,86,61,2,67,23,0,50,83,31,38,42,1,0,16,16,30,64,0]
    text=[0]*32
    out=''
    for x in result.keys():
        text[resultindex[x]-1024]=result[x]
    for i in range(len(text)):
        out+=chr(text[i]^xor[i])
    print out
运行得到key:K9nXu3_2o1q2_w3bassembly_r3vers3


[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

上传的附件:
最新回复 (0)
游客
登录 | 注册 方可回帖
返回