KCTF2019 变形金刚

一道安卓逆向,由于没找到关键函数,比赛时没搞出来,复现一下

对APK进行反编译,结果十分诡异,看起来就像是对用户名进行了字符串反转,但测试后并非这样

public class MainActivity extends AppCompiatActivity {
    class MyHandler extends Handler {
        WeakReference mWeakReference;

        public MyHandler(MainActivity arg2) {
            super();
            this.mWeakReference = new WeakReference(arg2);
        }

        public void handleMessage(Message arg6) {
            String v6_1;
            StringBuilder v1;
            super.handleMessage(arg6);
            Object v0 = this.mWeakReference.get();
            if(v0 == null) {
                return;
            }

            switch(arg6.what) {
                case 0: {
                    Object v6 = arg6.obj;
                    if(!TextUtils.isEmpty(((CharSequence)v6))) {
                        v1 = new StringBuilder();
                        int v3;
                        for(v3 = 0; v3 < ((String)v6).length() / 2; ++v3) {
                            v1.append(((String)v6).charAt(v3));
                        }

                        v6_1 = v1.toString();
                    }

                    v1 = new StringBuilder();
                    v1.append("flag{");
                    v1.append(v6_1);
                    v1.append("}");
                    Toast.makeText(((Context)v0), v1.toString(), 1).show();
                    break;
                }
                case 1: {
                    Toast.makeText(((Context)v0), "登录失败", 1).show();
                    break;
                }
                default: {
                    break;
                }
            }

            ((MainActivity)v0).login.setEnabled(true);
        }
    }

private void login(String arg3, String arg4, Handler arg5) {
        Toast.makeText(((Context)this), "登录中。。。", 1).show();
        MainActivity.runnable = new Runnable(arg4, arg3, arg5) {
            public void run() {
                Message v0 = Message.obtain();
                StringBuilder v1 = new StringBuilder(this.val$password);
                if(this.val$name.equals(v1.reverse().toString())) {
                    v0.obj = v1.toString();
                }
                else {
                    v0.what = 1;
                }

                this.val$handler.sendMessage(v0);
            }
        };
        MainActivity.cachedThreadPool.execute(MainActivity.runnable);
    }

    protected void onCreate(Bundle arg2) {
        super.onCreate(arg2);
        this.setContentView(2131296283);
        this.login = this.findViewById(2131165260);
        this.handler = new MyHandler(this);
        this.login.setOnClickListener(new View$OnClickListener() {
            public void onClick(View arg4) {
                MainActivity.this.mName = MainActivity.this.name.getText().toString();
                MainActivity.this.mPassword = MainActivity.this.password.getText().toString();
                if(!TextUtils.isEmpty(MainActivity.this.mName)) {
                    if(TextUtils.isEmpty(MainActivity.this.mPassword)) {
                    }
                    else {
                        MainActivity.hasLogin = true;
                        MainActivity.this.login.setEnabled(false);
                        MainActivity.this.login(MainActivity.this.mName, MainActivity.this.mPassword, MainActivity.this.handler);
                        return;
                    }
                }

                Toast.makeText(MainActivity.this, "用户名或密码为空", 1).show();
            }
        });
        this.name = this.findViewById(2131165265);
        this.password = this.findViewById(2131165277);
    }
}

所以直接考虑在native层动了手脚(然而也可能并不是,因为这里功能本质上都由MainActivity的父类AppCompiatApp来完成,并在完成功能后将button设置为不可点击状态,这使得MainActivity中的功能无法使用。谁特喵想得到它把这个东西换了呢orz),来到JNI_LOAD函数中查看

signed int __fastcall JNI_OnLoad(_JNIEnv *a1)
{
  int v1; // r8
  signed int result; // r0
  _JNIEnv *env_; // r5
  int v4; // r6
  _JNIEnv *v5; // [sp+0h] [bp-18h]
  int v6; // [sp+4h] [bp-14h]
  int v7; // [sp+8h] [bp-10h]

  v7 = v1;
  v5 = 0;
  if ( !((int (*)(void))a1->functions->FindClass)() )
    goto LABEL_4;
LABEL_2:
  result = -1;
  while ( _stack_chk_guard != v6 )
  {
LABEL_4:
    env_ = v5;
    v4 = ((int (__fastcall *)(_JNIEnv *, char (*)[42]))v5->functions->FindClass)(v5, off_51504010);// android/support/v7/app/AppCompiatActivity
    dword_51504110 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->NewGlobalRef)(env_, v4);
    if ( !v4
      || ((int (__fastcall *)(_JNIEnv *, int, char **, signed int))env_->functions->RegisterNatives)(env_, v4, aEq_, 1) <= -1 )
    {
      goto LABEL_2;
    }
    result = 0x10006;
  }
  return result;
}

 可以看到这里在android/support/v7/app/AppCompiatActivity活动中注册了Eq函数,去到AppCompiatActivity中查看


    protected native boolean eq(String arg1) {
    }

    protected void onStart() {
        super.onStart();
        this.login = this.findViewById(2131165260);
        this.login.setOnClickListener(new View$OnClickListener() {
            public void onClick(View arg5) {
                AppCompiatActivity.this.mName = AppCompiatActivity.this.name.getText().toString();
                AppCompiatActivity.this.mPassword = AppCompiatActivity.this.password.getText().toString();
                if(!TextUtils.isEmpty(AppCompiatActivity.this.mName)) {
                    if(TextUtils.isEmpty(AppCompiatActivity.this.mPassword)) {
                    }
                    else {
                        int v1 = 0;
                        AppCompiatActivity.this.login.setEnabled(false);
                        if(AppCompiatActivity.this.eq(AppCompiatActivity.this.mPassword)) {
                            byte[] v5 = AppCompiatActivity.this.mPassword.getBytes();
                            int v3 = 24;
                            if(v5.length != v3) {
                                byte[] v2 = new byte[v3];
                                while(v1 < v2.length) {
                                    byte v3_1 = v1 < v5.length ? v5[v1] : ((byte)v1);
                                    v2[v1] = v3_1;
                                    ++v1;
                                }

                                v5 = v2;
                            }

                            v5 = AppCompiatActivity.dec(v5, "2ggdrsLgM7iPNYPQrD58Rg==".getBytes());
                            AppCompiatActivity v1_1 = AppCompiatActivity.this;
                            StringBuilder v2_1 = new StringBuilder();
                            v2_1.append("flag{");
                            v2_1.append(new String(v5));
                            v2_1.append("}");
                            Toast.makeText(((Context)v1_1), v2_1.toString(), 1).show();
                        }
                        else {
                            Toast.makeText(AppCompiatActivity.this, "error", 1).show();
                        }

                        return;
                    }
                }

                Toast.makeText(AppCompiatActivity.this, "用户名或密码为空", 1).show();
            }
        });
        this.name = this.findViewById(2131165265);
        this.name.setEnabled(false);
        this.password = this.findViewById(2131165277);
    }

可以看到这里使用我们的输入作为AES密钥去解密一串base64来得到flag,而输入的验证则在Eq函数中,Eq函数在JNI_LOAD中注册,在native层可以找到Eq函数的代码如下

int __fastcall sub_51500784(_JNIEnv *a1)
{
  size_t lenth; // r10
  unsigned __int8 *buffer0; // r6
  _BYTE *buffer1; // r8
  _BYTE *buffer2; // r11
  signed int i; // r0
  size_t lenth_; // r2
  char *v7; // r1
  int c; // r3
  int i_; // r1
  unsigned int v10; // r2
  int v11; // r3
  int v12; // r0
  int v13; // r4
  unsigned __int8 v14; // r0
  _BYTE *buffer2_; // r3
  char *buffer0_c; // r5
  char *v17; // r4
  int j; // r5
  int v19; // r1
  int v20; // r0
  signed int v21; // r1
  int v22; // r2
  size_t len_input; // r0
  unsigned int len_input_; // r8
  unsigned int v25; // r5
  _BYTE *v26; // r0
  int v27; // r3
  int n; // r10
  unsigned int index; // r2
  int v30; // r12
  bool v31; // zf
  _BYTE *v32; // r1
  bool v33; // zf
  int v34; // r3
  int v35; // r1
  unsigned __int8 v36; // r11
  unsigned int v37; // lr
  char v38; // r1
  const char *v39; // r2
  int v40; // t1
  unsigned int v42; // [sp+4h] [bp-234h]
  unsigned int v43; // [sp+8h] [bp-230h]
  unsigned int v44; // [sp+10h] [bp-228h]
  char *input; // [sp+14h] [bp-224h]
  char box0[256]; // [sp+18h] [bp-220h]
  char buffer3[256]; // [sp+118h] [bp-120h]
  int v48; // [sp+218h] [bp-20h]

  input = (char *)((int (*)(void))a1->functions->GetStringUTFChars)();
  lenth = strlen(a650f909c721736);
  buffer0 = (unsigned __int8 *)malloc(lenth);
  buffer1 = malloc(lenth);
  buffer2 = malloc(lenth);
  _aeabi_memclr((int)buffer0, lenth);           // memset(dest, 0, n);
  _aeabi_memclr((int)buffer1, lenth);
  _aeabi_memclr((int)buffer2, lenth);
  if ( lenth )
  {
    i = 0;
    lenth_ = lenth;
    v7 = a650f909c721736;
    do
    {
      c = (unsigned __int8)*v7++;
      if ( c != '-' )
        buffer1[i++] = c;
      --lenth_;
    }
    while ( lenth_ );                           // clear '-'
    if ( i >= 1 )
    {
      i_ = i - 1;
      v10 = -8;
      v11 = 0;
      v12 = 0;
      do
      {
        if ( (v11 | (v10 >> 2)) > 3 )
        {
          v13 = v12;
        }
        else
        {
          v13 = v12 + 1;
          buffer0[v12] = 45;
        }
        v14 = buffer1[i_--];
        v11 += 0x40000000;
        buffer0[v13] = v14;
        ++v10;
        v12 = v13 + 1;
      }
      while ( i_ != -1 );
      if ( v13 >= 0 )
      {
        buffer2_ = buffer2;
        while ( 1 )
        {
          buffer0_c = (char *)*buffer0;
          if ( (unsigned __int8)((_BYTE)buffer0_c - 'a') <= 5u )
            break;
          if ( (unsigned __int8)((_BYTE)buffer0_c - '0') <= 9u )
          {
            buffer0_c = &aDbeafc24097158[(_DWORD)buffer0_c - '*'];
            goto LABEL_18;
          }
LABEL_19:
          *buffer2_++ = (_BYTE)buffer0_c;
          --v12;
          ++buffer0;
          if ( !v12 )
            goto LABEL_20;
        }
        buffer0_c = &aDbeafc24097158[(_DWORD)buffer0_c - 'a'];
LABEL_18:
        LOBYTE(buffer0_c) = *buffer0_c;
        goto LABEL_19;
      }
    }
  }
LABEL_20:
  _aeabi_memcpy8(box0, &unk_515023E8, 256);
  v17 = buffer3;
  j = 0;
  do
  {
    sub_51500D20(j, lenth);
    buffer3[j++] = buffer2[v19];
  }
  while ( j != 256 );
  v20 = (unsigned __int8)(buffer3[0] - 41);
  box0[0] = box0[v20];
  box0[v20] = -41;
  v21 = 1;
  do
  {
    v22 = (unsigned __int8)box0[v21];
    v20 = (v20 + (unsigned __int8)buffer3[v21] + v22) % 256;
    box0[v21++] = box0[v20];
    box0[v20] = v22;
  }
  while ( v21 != 256 );
  len_input = strlen(input);                    // 以下check()
  len_input_ = len_input;
  v25 = (unsigned __int8)buffer2[3];
  v43 = 8 * (3 - -3 * (len_input / 3));
  v42 = v25 + v43 / 6;
  v26 = malloc(v42 + 1);
  if ( len_input_ )
  {
    n = 0;
    index = 0;
    v30 = 0;
    v44 = v25;
    do
    {
      n = (n + 1) % 256;
      v35 = (unsigned __int8)box0[n];
      v30 = (v30 + v35) % 256;
      box0[n] = box0[v30];
      box0[v30] = v35;
      v17 = (char *)(unsigned __int8)box0[n];
      v36 = box0[(unsigned __int8)(v35 + (_BYTE)v17)] ^ input[index];
      if ( index && (v27 = 0xAAAAAAAB * (unsigned __int64)index >> 32, v37 = 3 * (index / 3), v37 != index) )// index不为3的倍数
      {
        v31 = index == 1;
        if ( index != 1 )
          v31 = v37 + 1 == index;
        if ( v31 )
        {
          v32 = aAbcdefghijklmn;
          v26[v44 + index] = aAbcdefghijklmn[(unsigned __int8)v26[v44 + index] | ((unsigned int)v36 >> 4)];
          v17 = &v26[v44 + index];
          v27 = 4 * v36 & 0x3C;
          v17[1] = v27;
          if ( index + 1 >= len_input_ )
            goto LABEL_53;
        }
        else
        {
          v33 = index == 2;
          if ( index != 2 )
            v33 = v37 + 2 == index;
          if ( v33 )
          {
            v17 = (char *)(v36 & 0xC0);
            v34 = v44++ + index;
            v26[v34] = aAbcdefghijklmn[(unsigned __int8)v26[v34] | ((unsigned int)v17 >> 6)] ^ 0xF;
            v27 = (int)&v26[v34];
            *(_BYTE *)(v27 + 1) = aAbcdefghijklmn[v36 & 0x3F];
          }
        }
      }
      else
      {
        v26[v44 + index] = aAbcdefghijklmn[(unsigned int)v36 >> 2] ^ 7;
        v17 = &v26[v44 + index];
        v27 = 16 * v36 & 0x30;
        v17[1] = v27;
        if ( index + 1 >= len_input_ )
        {
          v38 = aAbcdefghijklmn[v27];
          *((_WORD *)v17 + 1) = 15163;
          goto LABEL_43;
        }
      }
      ++index;
    }
    while ( index < len_input_ );
  }
  while ( 1 )
  {
    if ( v43 )
    {
      v32 = (_BYTE *)1;
      v17 = (char *)v42;
      v39 = " {9*8ga*l!Tn?@#fj'j$\\g;;";
      do
      {
        v27 = (unsigned __int8)v26[v25++];
        v40 = *(unsigned __int8 *)v39++;
        if ( v40 != v27 )
          v32 = 0;
      }
      while ( v25 < v42 );
    }
    else
    {
      v32 = (_BYTE *)1;
    }
    v26 = (_BYTE *)(_stack_chk_guard - v48);
    if ( _stack_chk_guard == v48 )
      break;
LABEL_53:
    v38 = v32[v27];
    v17[2] = 52;
LABEL_43:
    v17[1] = v38;
  }
  return (unsigned __int8)v32;
}

关于这段代码,主要是rc4的魔改和base64的魔改,看了大佬们的WP,大概有3种解法。

1.根据加密算法逆出解密算法

常规的做法,不过得花一定时间

2.穷举输入

v36 = sbox[(unsigned __int8)(v35 + (_BYTE)v17)] ^ input[index];

由于最终的验证就是魔改的base64对v36的数据流进行编码来得到密文,且密文已知,这里又是逐字节异或,所以我们可以不逆魔改的RC4,通过动态调试得到生成的sbox,然后copy这段RC4加密进行穷举。

3.手动解密

v36 = sbox[(unsigned __int8)(v35 + (_BYTE)v17)] ^ input[index];

还是这行代码,由于密文已知,sbox已知,这里使用的是异或加密,所以本质上如果将我们的输入改为密文,则v36的数据流能直接得到最终的key,但由于这里v36作为字节在异或后立即进行了魔改的base64,所以在最终验证时得到的是NewBase64(key),所以还需要进行一次解NewBase64即可得到key。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值