SHangwendada 发表于 2023-3-5 11:51:33

KCTF2019 变形金刚

#Transformer题解

## 前言

本题是我目前做过加密步骤最繁杂的题目,写一篇wp记录一下。

涉及知识点

1. ​        base64变表加密
2. ​        RC4
3. ​        Android应用基础
4. ​        Activity的方法和调用顺序

首先看到这个标题,我是很震惊的,transformer 变压器?变形金刚??,还是深度学习模型,本人最近也在研究清华的Anomaly-transformer,一看这标题就来劲了。

## Jadx静态分析

文件是安卓的apk文件,我们选择jadx进行分析

~~~java
package com.zhuotong.crackme;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.p003v7.app.AppCompiatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompiatActivity {
    public static final int MSG_LOGIN = 0;
    private static final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    private static boolean hasLogin;
    private static Runnable runnable;
    private Handler handler;
    private Button login;
    private String mName;
    private String mPassword;
    private EditText name;
    private EditText password;

    private static class MyHandler extends Handler {
      WeakReference<MainActivity> mWeakReference;

      public MyHandler(MainActivity mainActivity) {
            this.mWeakReference = new WeakReference<>(mainActivity);
      }

      public void handleMessage(Message message) {
            super.handleMessage(message);
            MainActivity mainActivity = this.mWeakReference.get();
            if (mainActivity != null) {
                switch (message.what) {
                  case 0:
                        String str = (String) message.obj;
                        if (!TextUtils.isEmpty(str)) {
                            StringBuilder sb = new StringBuilder();
                            for (int i = 0; i < str.length() / 2; i++) {
                              sb.append(str.charAt(i));
                            }
                            str = sb.toString();
                        }
                        Toast.makeText(mainActivity, "flag{" + str + "}", 1).show();
                        break;
                  case 1:
                        Toast.makeText(mainActivity, "登录失败", 1).show();
                        break;
                }
                mainActivity.login.setEnabled(true);
            }
      }
    }

    /* access modifiers changed from: protected */
    @Override // android.support.p000v4.app.SupportActivity, android.support.p003v7.app.AppCompatActivity, android.support.p000v4.app.FragmentActivity
    public void onCreate(Bundle bundle) {
      super.onCreate(bundle);
      setContentView(C0245R.layout.activity_main);
      this.login = (Button) findViewById(C0245R.C0247id.login_button);
      this.handler = new MyHandler(this);
      this.login.setOnClickListener(new View.OnClickListener() {
            /* class com.zhuotong.crackme.MainActivity.View$OnClickListenerC02431 */

            public void onClick(View view) {
                MainActivity.this.mName = MainActivity.this.name.getText().toString();
                MainActivity.this.mPassword = MainActivity.this.password.getText().toString();
                if (TextUtils.isEmpty(MainActivity.this.mName) || TextUtils.isEmpty(MainActivity.this.mPassword)) {
                  Toast.makeText(MainActivity.this, "用户名或密码为空", 1).show();
                  return;
                }
                boolean unused = MainActivity.hasLogin = true;
                MainActivity.this.login.setEnabled(false);
                MainActivity.this.login(MainActivity.this.mName, MainActivity.this.mPassword, MainActivity.this.handler);
            }
      });
      this.name = (EditText) findViewById(C0245R.C0247id.name);
      this.password = (EditText) findViewById(C0245R.C0247id.password);
    }

    /* access modifiers changed from: private */
    /* access modifiers changed from: public */
    private void login(final String str, final String str2, final Handler handler2) {
      Toast.makeText(this, "登录中。。。", 1).show();
      runnable = new Runnable() {
            /* class com.zhuotong.crackme.MainActivity.RunnableC02442 */

            public void run() {
                Message obtain = Message.obtain();
                StringBuilder sb = new StringBuilder(str2);
                if (str.equals(sb.reverse().toString())) {
                  obtain.obj = sb.toString();
                } else {
                  obtain.what = 1;
                }
                handler2.sendMessage(obtain);
            }
      };
      cachedThreadPool.execute(runnable);
    }
}
~~~

该应用程序具有登录功能,接受用户名和密码,并检查反转后的密码是否与用户名相同。如果相同,将返回一个包含密码的Toast消息,并在密码前加上"flag{"和"}"。否则,将显示一个登录失败的Toast消息。该应用程序中的代码包括创建一个处理器,定义一个可运行对象和一个线程池,以及定义登录功能的方法。在创建处理器时,使用了一个弱引用来避免内存泄漏。在登录方法中,使用线程池来执行可运行对象,检查密码是否与反转后的用户名相同,并使用处理器发送消息来处理结果。当用户单击登录按钮时,将调用登录方法。

分析完毕,但是没得出什么有用的东西,我们先开文件看一手。

!(https://shangwendada.co/wp-content/uploads/2023/03/image-1677949502150.png)

看到已经有账户名了,上面分析发现如果账号反转等于密码的话就会输出flag,我们将账号反转尝试一下。

发现输出“error”,可是我们在代码中并未发现给出error提示的代码,使用字符串搜索也没有该字符串,查看主函数是发现其继承了一个AppCompiatActivity类,仔细一看好家伙,这玩意多了一个i。是个自写类。我们跟进查看看看有没有什么惊喜。

```java
package android.support.p003v7.app;

import android.os.Handler;
import android.text.TextUtils;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.zhuotong.crackme.C0245R;
import java.io.PrintStream;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/* renamed from: android.support.v7.app.AppCompiatActivity */
public class AppCompiatActivity extends AppCompatActivity {
    public static final int MSG_LOGIN = 0;
    private Handler handler;
    private Button login;
    private String mName;
    private String mPassword;
    private EditText name;
    private EditText password;

    /* access modifiers changed from: protected */
    /* renamed from: eq */
    public native boolean mo4191eq(String str);

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

    /* access modifiers changed from: protected */
    @Override // android.support.p003v7.app.AppCompatActivity, android.support.p000v4.app.FragmentActivity
    public void onStart() {
      super.onStart();
      this.login = (Button) findViewById(C0245R.C0247id.login_button);
      this.login.setOnClickListener(new View.OnClickListener() {
            /* class android.support.p003v7.app.AppCompiatActivity.View$OnClickListenerC01791 */

            public void onClick(View view) {
                AppCompiatActivity.this.mName = AppCompiatActivity.this.name.getText().toString();
                AppCompiatActivity.this.mPassword = AppCompiatActivity.this.password.getText().toString();
                if (TextUtils.isEmpty(AppCompiatActivity.this.mName) || TextUtils.isEmpty(AppCompiatActivity.this.mPassword)) {
                  Toast.makeText(AppCompiatActivity.this, "用户名或密码为空", 1).show();
                  return;
                }
                int i = 0;
                AppCompiatActivity.this.login.setEnabled(false);
                if (AppCompiatActivity.this.mo4191eq(AppCompiatActivity.this.mPassword)) {
                  byte[] bytes = AppCompiatActivity.this.mPassword.getBytes();
                  if (bytes.length != 24) {
                        byte[] bArr = new byte;
                        while (i < bArr.length) {
                            bArr = i < bytes.length ? bytes : (byte) i;
                            i++;
                        }
                        bytes = bArr;
                  }
                  byte[] dec = AppCompiatActivity.dec(bytes, "2ggdrsLgM7iPNYPQrD58Rg==".getBytes());
                  AppCompiatActivity appCompiatActivity = AppCompiatActivity.this;
                  Toast.makeText(appCompiatActivity, "flag{" + new String(dec) + "}", 1).show();
                  return;
                }
                Toast.makeText(AppCompiatActivity.this, "error", 1).show();
            }
      });
      this.name = (EditText) findViewById(C0245R.C0247id.name);
      this.name.setEnabled(false);
      this.password = (EditText) findViewById(C0245R.C0247id.password);
    }

    /* access modifiers changed from: private */
    public static byte[] dec(byte[] bArr, byte[] bArr2) {
      byte[] decode = Base64.decode(bArr2, 0);
      SecretKeySpec secretKeySpec = new SecretKeySpec(bArr, "AES");
      try {
            secretKeySpec.getEncoded();
            PrintStream printStream = System.out;
            printStream.println("getFormat = " + secretKeySpec.getFormat() + " ; getAlgorithm = " + secretKeySpec.getAlgorithm());
            Cipher instance = Cipher.getInstance("AES/CFB/PKCS5Padding");
            PrintStream printStream2 = System.out;
            printStream2.println("getBlockSize = " + instance.getBlockSize());
            instance.init(2, secretKeySpec, new IvParameterSpec(new byte));
            return instance.doFinal(decode);
      } catch (Throwable th) {
            th.printStackTrace();
            return null;
      }
    }
}
```

代码包含了一些操作 EditText、Button 和 Toast 的逻辑,实现了一个简单的登录功能。

在代码的顶部,import 了一些类和库,如 Handler、TextUtils、Base64 等。

代码中定义了一个类 AppCompiatActivity,继承了 AppCompatActivity 类。在该类中,有一些变量和方法:

- handler:用于处理消息的 Handler 对象。
- login、name、password:分别代表登录按钮、用户名输入框、密码输入框。
- mName、mPassword:分别代表用户名和密码,用于保存从 EditText 中获取的字符串。
- mo4191eq():一个 native 方法,可能用于检查密码是否正确。
- onStart():当 Activity 开始时调用的方法,用于初始化界面和添加监听器。
- dec():解密密码的方法,使用 AES/CFB/PKCS5Padding 算法。

在 onStart() 方法中,找到了 login、name、password 这三个控件,并添加了一个点击监听器。当登录按钮被点击时,代码会尝试使用 mo4191eq() 方法判断密码是否正确,如果正确,则使用 dec() 方法对密码进行解密,并通过 Toast 显示 flag。

总的来说,这段代码是一个简单的登录功能,使用了一些常见的 Android 应用程序组件和加密算法。

主要发现这里使用了一个native方法,将apk文件使用压缩包打开,提取出该so文件,使用ida观察.

## IDA静态分析

~~~C
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
int v3; // r5
int v4; // r6
int v5; // BYREF

v5 = 0;
if ( (*vm)->GetEnv(vm, (void **)&v5, 65540) )
    return -1;
v3 = v5;
v4 = (*(int (__fastcall **)(int, void *))(*(_DWORD *)v5 + 24))(v5, off_4010);
dword_4110 = (*(int (__fastcall **)(int, int))(*(_DWORD *)v3 + 84))(v3, v4);
if ( !v4 || (*(int (__fastcall **)(int, int, char **, int))(*(_DWORD *)v3 + 860))(v3, v4, off_4014, 1) <= -1 )
    return -1;
else
    return 65542;
}
~~~

这段代码是一个 JNI(Java Native Interface)函数,当本地库被加载时,它会被调用。JNI 允许 Java 程序与本地代码交互,例如在本地实现一些功能,并将其作为 Java 方法调用。JNI_OnLoad 函数是在本地库加载时由 JVM(Java 虚拟机)调用的。

这个函数的作用是获取当前线程的 JNIEnv 指针,并将它存储在局部变量 v5 中。然后,它调用 off_4010 指向的函数来获取一个整数值,将它存储在 v4 中。接着,它使用 v3 和 v4 作为参数调用 off_4014 指向的函数,并将其返回值与 -1 进行比较,如果小于等于 -1,则返回 -1,否则返回 65542。

这段代码比较晦涩,其中 off_4010 和 off_4014 是两个函数指针,它们的具体含义不得而知

~~~c
char *datadiv_decode5009363700628197108()
{
int i; // r0
int j; // r0
int k; // r0
char *result; // r0

for ( i = 0; i != 37; ++i )
    byte_4020 ^= 0xA5u;
for ( j = 0; j != 66; ++j )
    byte_4050 ^= 0xA5u;
for ( k = 0; k != 42; ++k )
    byte_40A0 ^= 0x84u;
result = &byte_40D0;
byte_40CA ^= 0xFCu;
byte_40CB ^= 0xFCu;
byte_40CC ^= 0xFCu;
byte_40D0 ^= 0x62u;
byte_40D1 ^= 0x62u;
byte_40D2 ^= 0x62u;
byte_40D3 ^= 0x62u;
byte_40D4 ^= 0x62u;
byte_40D5 ^= 0x62u;
word_40D6 ^= 0x6262u;
byte_40D8 ^= 0x62u;
byte_40D9 ^= 0x62u;
byte_40DA ^= 0x62u;
byte_40DB ^= 0x62u;
byte_40DC ^= 0x62u;
byte_40DD ^= 0x62u;
byte_40DE ^= 0x62u;
byte_40DF ^= 0x62u;
byte_40E0 ^= 0x62u;
byte_40E1 ^= 0x62u;
byte_40E2 ^= 0x62u;
byte_40E3 ^= 0x62u;
byte_40E4 ^= 0x62u;
byte_40E5 ^= 0x62u;
return result;
}
~~~

这一段代码对一大串字符做了处理,我们使用脚本分析,看看处理出了什么东西。

~~~c
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
#include<windows.h>
using namespace std;
typedef int status;
typedef int selemtype;
int v3; // r10
char *v4; // r6
char *v5; // r8
char *v6; // r11
int v7; // r0
int v8; // r2
char *v9; // r1
int v10; // r3
int v11; // r1
unsigned int v12; // r2
int v13; // r3
int v14; // r0
int v15; // r4
char v16; // r0
char *v17; // r3
char *v18; // r5
// r5
int v20; // r1
int v21; // r0
// r1
int v23; // r2
int v24; // r8
unsigned int v25; // r5
char *v26; // r0
int v27; // r10
unsigned int v28; // r2
int v29; // r12
bool v30; // zf
char *v31; // r4
int v32; // r3
bool v33; // zf
int v34; // r3
int v35; // r1
char v36; // r11
char v37; // lr
int v38; // r3
unsigned __int8 v39; // r1
char *v40; // r2
int v41; // r3
int v42; // t1
char v44; // r1
unsigned int v45; //
unsigned int v46; //
unsigned int v47; //
char *v48; //
char v49; // BYREF
char v50; //
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\=/O
               ____/`---'\____
            .'\\|   |//`.
            /\\|||:|||//\
         /_||||| -:- |||||-\
         |   | \\\-/// |   |
         | \_|''\---/''|   |
         \.-\__`-`___/-. /
         ___`. .'/--.--\`. . __
      ."" '<`.___\_<|>_/___.'>'"".
   | | :`- \`.;`\ _ /`;.`/ - ` : | |
   \\ `-.   \_ __\ /__ _/   .-` //
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    佛祖保佑       永不宕机   永无BUG
*/
unsigned char ida_chars1[] =
{
0x93, 0x90, 0x95, 0xC3, 0x9C, 0x95, 0x9C, 0xC6, 0x88, 0x92,
0x97, 0x94, 0x92, 0x88, 0x96, 0x93, 0x91, 0x92, 0x88, 0x9C,
0x96, 0x96, 0x94, 0x88, 0xC6, 0x9D, 0x97, 0xC1, 0xC3, 0x9D,
0xC7, 0x9C, 0x9D, 0xC0, 0x9C, 0x9D, 0xA5

};
unsigned char ida_chars2[] =
{
0x84, 0x9F, 0x86, 0x81, 0x80, 0x83, 0x8D, 0x8C, 0x8E, 0x88,
0x8F, 0x8A, 0xC5, 0xDB, 0xFA, 0xFE, 0xF8, 0xDE, 0xD8, 0x9A,
0x99, 0x9B, 0x89, 0x8B, 0xE5, 0xFB, 0xC4, 0xC7, 0xC6, 0xC1,
0xC0, 0xC3, 0xC2, 0xCD, 0xCC, 0xCF, 0xCE, 0xC9, 0xC8, 0xCB,
0xCA, 0xD5, 0xD4, 0xD7, 0xD6, 0xD1, 0xD0, 0xD3, 0xD2, 0xDD,
0xDC, 0xDF, 0x95, 0x94, 0x97, 0x96, 0x91, 0x90, 0x93, 0x92,
0x9D, 0x9C, 0xF9, 0x82, 0x9E, 0xA5
};
unsigned char ida_chars3[] =
{
0xE5, 0xEA, 0xE0, 0xF6, 0xEB, 0xED, 0xE0, 0xAB, 0xF7, 0xF1,
0xF4, 0xF4, 0xEB, 0xF6, 0xF0, 0xAB, 0xF2, 0xB3, 0xAB, 0xE5,
0xF4, 0xF4, 0xAB, 0xC5, 0xF4, 0xF4, 0xC7, 0xEB, 0xE9, 0xF4,
0xED, 0xE5, 0xF0, 0xC5, 0xE7, 0xF0, 0xED, 0xF2, 0xED, 0xF0,
0xFD, 0x84
};
unsigned char ida_chars4[] =
{
0x99, 0x8D, 0xFC, 0x00, 0x00, 0x00, 0x4A, 0x2E, 0x08, 0x03,
0x14, 0x03, 0x4D, 0x0E, 0x03, 0x0C, 0x05, 0x4D, 0x31, 0x16,
0x10, 0x0B, 0x0C, 0x05, 0x59, 0x4B, 0x38, 0x62
};
unsigned char ida_chars[] =
{
0xD7, 0xDF, 0x02, 0xD4, 0xFE, 0x6F, 0x53, 0x3C, 0x25, 0x6C,
0x99, 0x97, 0x06, 0x56, 0x8F, 0xDE, 0x40, 0x11, 0x64, 0x07,
0x36, 0x15, 0x70, 0xCA, 0x18, 0x17, 0x7D, 0x6A, 0xDB, 0x13,
0x30, 0x37, 0x29, 0x60, 0xE1, 0x23, 0x28, 0x8A, 0x50, 0x8C,
0xAC, 0x2F, 0x88, 0x20, 0x27, 0x0F, 0x7C, 0x52, 0xA2, 0xAB,
0xFC, 0xA1, 0xCC, 0x21, 0x14, 0x1F, 0xC2, 0xB2, 0x8B, 0x2C,
0xB0, 0x3A, 0x66, 0x46, 0x3D, 0xBB, 0x42, 0xA5, 0x0C, 0x75,
0x22, 0xD8, 0xC3, 0x76, 0x1E, 0x83, 0x74, 0xF0, 0xF6, 0x1C,
0x26, 0xD1, 0x4F, 0x0B, 0xFF, 0x4C, 0x4D, 0xC1, 0x87, 0x03,
0x5A, 0xEE, 0xA4, 0x5D, 0x9E, 0xF4, 0xC8, 0x0D, 0x62, 0x63,
0x3E, 0x44, 0x7B, 0xA3, 0x68, 0x32, 0x1B, 0xAA, 0x2D, 0x05,
0xF3, 0xF7, 0x16, 0x61, 0x94, 0xE0, 0xD0, 0xD3, 0x98, 0x69,
0x78, 0xE9, 0x0A, 0x65, 0x91, 0x8E, 0x35, 0x85, 0x7A, 0x51,
0x86, 0x10, 0x3F, 0x7F, 0x82, 0xDD, 0xB5, 0x1A, 0x95, 0xE7,
0x43, 0xFD, 0x9B, 0x24, 0x45, 0xEF, 0x92, 0x5C, 0xE4, 0x96,
0xA9, 0x9C, 0x55, 0x89, 0x9A, 0xEA, 0xF9, 0x90, 0x5F, 0xB8,
0x04, 0x84, 0xCF, 0x67, 0x93, 0x00, 0xA6, 0x39, 0xA8, 0x4E,
0x59, 0x31, 0x6B, 0xAD, 0x5E, 0x5B, 0x77, 0xB1, 0x54, 0xDC,
0x38, 0x41, 0xB6, 0x47, 0x9F, 0x73, 0xBA, 0xF8, 0xAE, 0xC4,
0xBE, 0x34, 0x01, 0x4B, 0x2A, 0x8D, 0xBD, 0xC5, 0xC6, 0xE8,
0xAF, 0xC9, 0xF5, 0xCB, 0xFB, 0xCD, 0x79, 0xCE, 0x12, 0x71,
0xD2, 0xFA, 0x09, 0xD5, 0xBC, 0x58, 0x19, 0x80, 0xDA, 0x49,
0x1D, 0xE6, 0x2E, 0xE3, 0x7E, 0xB7, 0x3B, 0xB3, 0xA0, 0xB9,
0xE5, 0x57, 0x6E, 0xD9, 0x08, 0xEB, 0xC7, 0xED, 0x81, 0xF1,
0xF2, 0xBF, 0xC0, 0xA7, 0x4A, 0xD6, 0x2B, 0xB4, 0x72, 0x9D,
0x0E, 0x6D, 0xEC, 0x48, 0xE2, 0x33
};
char vs16[] = "2409715836dbeafc";
int main ()
{
        for(int i = 0 ; i < 38 ; i ++ )
        {
                ida_chars1^=0xa5;
        }
        for(int i = 0 ; i < 66 ; i ++ )
        {
                ida_chars2^=0xa5;
        }
        for(int i = 0 ; i <42 ; i ++)
        {
                ida_chars3^=0x84;
        }
        for(int i = 0 ; i <28 ; i++ )
        {
                if(i<=2)
                {
                        ida_chars4^=0xfc;
                }
                else
                {
                        ida_chars4^=0x62;
                }
        }
        ida_chars4='"';
        printf("%s\n%s\n%s\n%s\n",ida_chars1,ida_chars2,ida_chars3,ida_chars4);
~~~

输出结果:

~~~c
650f909c-7217-3647-9331-c82df8b98e98
!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';
android/support/v7/app/AppCompiatActivity
eq"bbb(Ljava/lang/String;)Z
~~~

发现第一个字符串像是一串序列号,第二个字符串是一个变化的base64密码表,第三第四个则是调用我们的eq函数

JNI_Onload()里边有两个off_4010 off_4014 分别对应的就是第三第四个字符串。

我们对密码表进行跟进

!(https://shangwendada.co/wp-content/uploads/2023/03/image-1677949523587.png)

发现784函数调用了该密码表,肯定是在其中进行了base64变表加密。

查看整个784函数,我们将该函数分为几段来进行讲解

~~~c
if ( v3 )
{
    v7 = 0;
    v8 = v3;
    v9 = byte_4020;
    do
    {
      v10 = (unsigned __int8)*v9++;
      if ( v10 != 45 )
      *((_BYTE *)v5 + v7++) = v10;
      --v8;
    }
    while ( v8 );
    if ( v7 >= 1 )
    {
      v11 = v7 - 1;
      v12 = -8;
      v13 = 0;
      v14 = 0;
      do
      {
      if ( (v13 | (v12 >> 2)) > 3 )
      {
          v15 = v14;
      }
      else
      {
          v15 = v14 + 1;
          v4 = 45;
      }
      v16 = *((_BYTE *)v5 + v11--);
      v13 += 0x40000000;
      v4 = v16;
      ++v12;
      v14 = v15 + 1;
      }
      while ( v11 != -1 );
      if ( v15 >= 0 )
      {
      v17 = v6;
      while ( 1 )
      {
          v18 = (_BYTE *)*v4;
          if ( (unsigned __int8)((_BYTE)v18 - 97) <= 5u )
            break;
          if ( (unsigned __int8)((_BYTE)v18 - 48) <= 9u )
          {
            v18 = (char *)&unk_23DE + (_DWORD)v18 - 48;
            goto LABEL_18;
          }
LABEL_19:
          *v17++ = (unsigned __int8)v18;
          --v14;
          ++v4;
          if ( !v14 )
            goto LABEL_20;
      }
      v18 = (char *)&unk_23D8 + (_DWORD)v18 - 97;
LABEL_18:
      LOBYTE(v18) = *v18;
      goto LABEL_19;
      }
    }
}
~~~

这一串呢,首先是取出我们之前异或得到的那个类似于序列号的字符串,在此处实际上是作为rc4的key,然后对对其处理方式是删除其中所有的'-'然后逆序,再加上'-',再利用码表,将其替换成码表里面的值。

~~~c
for ( i = 0; i != 256; ++i )
{
    sub_D20(i, v3);
    v50 = v6;
}
v21 = (unsigned __int8)(v50 - 41);
v49 = v49;
v49 = -41;
for ( j = 1; j != 256; ++j )
{
    v23 = (unsigned __int8)v49;
    v21 = (v21 + (unsigned __int8)v50 + v23) % 256;
    v49 = v49;
    v49 = v23;
}
v24 = strlen(v48);
v25 = v6;
v46 = 8 * (3 - -3 * (v24 / 3));
v45 = v25 + v46 / 6;
v26 = malloc(v45 + 1);
if ( !v24 )
    goto LABEL_44;
v27 = 0;
v28 = 0;
v29 = 0;
v47 = v25;
~~~

这一段就是对rc4的s盒子,t盒进行处理,然而本次的rc4为魔改rc4它使用了自己给出的S盒。内容储存在unk_23E8中

~~~c
while ( 1 )
{
    v27 = (v27 + 1) % 256;
    v35 = (unsigned __int8)v49;
    v29 = (v29 + v35) % 256;
    v49 = v49;
    v49 = v35;
    v36 = v49[(unsigned __int8)(v35 + v49)] ^ v48;
    if ( !v28 )
      break;
    v37 = 3 * (v28 / 3);
    if ( v37 == v28 )
      break;
    v30 = v28 == 1;
    if ( v28 != 1 )
      v30 = v37 + 1 == v28;
    if ( v30 )
    {
      v26 = byte_4050[(unsigned __int8)v26 | (v36 >> 4)];
      v31 = &v26;
      v32 = (4 * v36) & 0x3C;
      v31 = v32;
      if ( v28 + 1 >= v24 )
      {
      v44 = byte_4050;
      v31 = 52;
      goto LABEL_43;
      }
    }
    else
    {
      v33 = v28 == 2;
      if ( v28 != 2 )
      v33 = v37 + 2 == v28;
      if ( v33 )
      {
      v34 = v47 + v28;
      ++v47;
      v26 = byte_4050[(unsigned __int8)v26 | ((unsigned __int8)(v36 & 0xC0) >> 6)] ^ 0xF;
      v26 = byte_4050;
      }
    }
LABEL_40:
    if ( ++v28 >= v24 )
      goto LABEL_44;
}
~~~

这一段就是将每个字符先rc4加密在使用base64加密,同时进行。

但是每当迭代器为2的倍数时需要异或一个0xF。

~~~c
v26 = byte_4050 ^ 7;
v31 = &v26;
v38 = (16 * v36) & 0x30;
v31 = v38;
if ( v28 + 1 < v24 )
    goto LABEL_40;
v44 = byte_4050;
*((_WORD *)v31 + 1) = 15163;
LABEL_43:
v31 = v44;
LABEL_44:
if ( v46 )
{
    v39 = 1;
    v40 = &byte_24E8;
    do
    {
      v41 = (unsigned __int8)v26;
      v42 = (unsigned __int8)*v40++;
      if ( v42 != v41 )
      v39 = 0;
    }
    while ( v25 < v45 );
}
else
{
    return 1;
}
return v39;
~~~

这一段是每当迭代器为4的倍数时就需要将密文异或一个0x7,然后与储存的值进行比较。至此解析完毕,那么这个密文肯定就是密码。

## 解题

我们需要将密文先按规则异或回去然后base64 解码。

脚本如下

~~~python
import string

s = " {9*8ga*l!Tn?@#fj'j$\g;;"
cc = ''
for i in xrange(len(s)):
   if (i % 4 == 0):
             cc += chr(ord(s) ^ 7)
   elif (i % 4 == 2):
             cc += chr(ord(s) ^ 0xF)
   else:
             cc += s
my_base64table = "!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\\';"
std_base64table ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
t = string.maketrans(my_base64table, std_base64table)
print map(ord, cc.translate(t).decode('base64'))
#
~~~

然后我们可以得到处理后的密钥的值。

接下来我们需要处理rc4的key.

~~~c
v3 = 36;
        v5 = (char *)malloc(v3);
        v6 = (char *)malloc(v3);
        v4 = (char *)malloc(v3);
        if(v3)
        {
                v7 = 0;
                v8 = v3;
                v9 = ida_chars5;
                do
                {
                        v10 = *v9++;
                        if(v10!=45)
                        {
                                *(v5 + v7++) = v10;
                        }
                        --v8;
                       
                }while(v8);
                if ( v7 >= 1 )
          {
                        v11 = v7 - 1;
                        v12 = -8;
                        v13 = 0;
                        v14 = 0;
                        do
                        {
                                if ( (v13 | (v12 >> 2)) > 3 )
                                {
                                  v15 = v14;
                                }
                                else
                                {
                                  v15 = v14 + 1;
                                  v4 = '-';
                                }
                                v16 = *(v5 + v11--);
                                v13 += 0x40000000;
                                v4 = v16;
                                ++v12;
                                v14 = v15 + 1;
              }while ( v11 != -1 );
                }
               
                for(int i = 0 ; i < 36 ; i ++ )
                {
                        if((unsigned char)(v4-'a') <= 5u )
                        {
                          v4 = vs16-'a'+10];
                        }else if((unsigned char)(v4-'0') <= 9u )
                        {
                          v4= vs16-'0'];
                        }
                }
                printf("%s\n",v4);
        }
~~~

得到36f36b3c-a03e-4996-8759-8408e626c215

然后初始化RC4的密钥流

~~~c
char s={0};
        unsigned char K={0};
        memcpy(&s,&ida_chars,0x100);
        for(int i = 0 ; i < 256 ; i ++ )
        {
                K=v4;
                //printf("%d,",K);
        }
        int j = 0;
       
        j = (unsigned char)(K - 41);
        s=s;
        s=0xd7;
        for(int i = 1 ; i<256 ; i ++ )
        {
                int tmp = (unsigned char)s;
                j =(j+(unsigned char)K+tmp)%256;
                s=s;
                s=tmp;
        }
        for(int i=0;i<0x100;i++){
                printf("%02X ",(unsigned char)s);
                if((i+1)%16==0){
                        printf("\n");
                }
        }
~~~

异或得到答案

~~~c
        int txi = 0 , txj = 0;
        unsigned char tx =0;
        for(int i = 0 ; i < 16 ; i ++ )
        {
                txi = (txi+1)%256;
                tx = (unsigned char)s;
                txj=(txj+tx)%256;
                s=s;
                s=tx;
        //        printf("%X,",s]);
                printf("%c",s[(tx+s)%256]^debase);
        }
~~~

## 完整exp

~~~c
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
#include<windows.h>
using namespace std;
typedef int status;
typedef int selemtype;
int v3; // r10
char *v4; // r6
char *v5; // r8
char *v6; // r11
int v7; // r0
int v8; // r2
char *v9; // r1
int v10; // r3
int v11; // r1
unsigned int v12; // r2
int v13; // r3
int v14; // r0
int v15; // r4
char v16; // r0
char *v17; // r3
char *v18; // r5
// r5
int v20; // r1
int v21; // r0
// r1
int v23; // r2
int v24; // r8
unsigned int v25; // r5
char *v26; // r0
int v27; // r10
unsigned int v28; // r2
int v29; // r12
bool v30; // zf
char *v31; // r4
int v32; // r3
bool v33; // zf
int v34; // r3
int v35; // r1
char v36; // r11
char v37; // lr
int v38; // r3
unsigned __int8 v39; // r1
char *v40; // r2
int v41; // r3
int v42; // t1
char v44; // r1
unsigned int v45; //
unsigned int v46; //
unsigned int v47; //
char *v48; //
char v49; // BYREF
char v50; //
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\=/O
               ____/`---'\____
            .'\\|   |//`.
            /\\|||:|||//\
         /_||||| -:- |||||-\
         |   | \\\-/// |   |
         | \_|''\---/''|   |
         \.-\__`-`___/-. /
         ___`. .'/--.--\`. . __
      ."" '<`.___\_<|>_/___.'>'"".
   | | :`- \`.;`\ _ /`;.`/ - ` : | |
   \\ `-.   \_ __\ /__ _/   .-` //
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    佛祖保佑       永不宕机   永无BUG
*/
unsigned char ida_chars1[] =
{
0x93, 0x90, 0x95, 0xC3, 0x9C, 0x95, 0x9C, 0xC6, 0x88, 0x92,
0x97, 0x94, 0x92, 0x88, 0x96, 0x93, 0x91, 0x92, 0x88, 0x9C,
0x96, 0x96, 0x94, 0x88, 0xC6, 0x9D, 0x97, 0xC1, 0xC3, 0x9D,
0xC7, 0x9C, 0x9D, 0xC0, 0x9C, 0x9D, 0xA5

};
unsigned char ida_chars2[] =
{
0x84, 0x9F, 0x86, 0x81, 0x80, 0x83, 0x8D, 0x8C, 0x8E, 0x88,
0x8F, 0x8A, 0xC5, 0xDB, 0xFA, 0xFE, 0xF8, 0xDE, 0xD8, 0x9A,
0x99, 0x9B, 0x89, 0x8B, 0xE5, 0xFB, 0xC4, 0xC7, 0xC6, 0xC1,
0xC0, 0xC3, 0xC2, 0xCD, 0xCC, 0xCF, 0xCE, 0xC9, 0xC8, 0xCB,
0xCA, 0xD5, 0xD4, 0xD7, 0xD6, 0xD1, 0xD0, 0xD3, 0xD2, 0xDD,
0xDC, 0xDF, 0x95, 0x94, 0x97, 0x96, 0x91, 0x90, 0x93, 0x92,
0x9D, 0x9C, 0xF9, 0x82, 0x9E, 0xA5
};
unsigned char ida_chars3[] =
{
0xE5, 0xEA, 0xE0, 0xF6, 0xEB, 0xED, 0xE0, 0xAB, 0xF7, 0xF1,
0xF4, 0xF4, 0xEB, 0xF6, 0xF0, 0xAB, 0xF2, 0xB3, 0xAB, 0xE5,
0xF4, 0xF4, 0xAB, 0xC5, 0xF4, 0xF4, 0xC7, 0xEB, 0xE9, 0xF4,
0xED, 0xE5, 0xF0, 0xC5, 0xE7, 0xF0, 0xED, 0xF2, 0xED, 0xF0,
0xFD, 0x84
};
unsigned char ida_chars4[] =
{
0x99, 0x8D, 0xFC, 0x00, 0x00, 0x00, 0x4A, 0x2E, 0x08, 0x03,
0x14, 0x03, 0x4D, 0x0E, 0x03, 0x0C, 0x05, 0x4D, 0x31, 0x16,
0x10, 0x0B, 0x0C, 0x05, 0x59, 0x4B, 0x38, 0x62
};
unsigned char ida_chars[] =
{
0xD7, 0xDF, 0x02, 0xD4, 0xFE, 0x6F, 0x53, 0x3C, 0x25, 0x6C,
0x99, 0x97, 0x06, 0x56, 0x8F, 0xDE, 0x40, 0x11, 0x64, 0x07,
0x36, 0x15, 0x70, 0xCA, 0x18, 0x17, 0x7D, 0x6A, 0xDB, 0x13,
0x30, 0x37, 0x29, 0x60, 0xE1, 0x23, 0x28, 0x8A, 0x50, 0x8C,
0xAC, 0x2F, 0x88, 0x20, 0x27, 0x0F, 0x7C, 0x52, 0xA2, 0xAB,
0xFC, 0xA1, 0xCC, 0x21, 0x14, 0x1F, 0xC2, 0xB2, 0x8B, 0x2C,
0xB0, 0x3A, 0x66, 0x46, 0x3D, 0xBB, 0x42, 0xA5, 0x0C, 0x75,
0x22, 0xD8, 0xC3, 0x76, 0x1E, 0x83, 0x74, 0xF0, 0xF6, 0x1C,
0x26, 0xD1, 0x4F, 0x0B, 0xFF, 0x4C, 0x4D, 0xC1, 0x87, 0x03,
0x5A, 0xEE, 0xA4, 0x5D, 0x9E, 0xF4, 0xC8, 0x0D, 0x62, 0x63,
0x3E, 0x44, 0x7B, 0xA3, 0x68, 0x32, 0x1B, 0xAA, 0x2D, 0x05,
0xF3, 0xF7, 0x16, 0x61, 0x94, 0xE0, 0xD0, 0xD3, 0x98, 0x69,
0x78, 0xE9, 0x0A, 0x65, 0x91, 0x8E, 0x35, 0x85, 0x7A, 0x51,
0x86, 0x10, 0x3F, 0x7F, 0x82, 0xDD, 0xB5, 0x1A, 0x95, 0xE7,
0x43, 0xFD, 0x9B, 0x24, 0x45, 0xEF, 0x92, 0x5C, 0xE4, 0x96,
0xA9, 0x9C, 0x55, 0x89, 0x9A, 0xEA, 0xF9, 0x90, 0x5F, 0xB8,
0x04, 0x84, 0xCF, 0x67, 0x93, 0x00, 0xA6, 0x39, 0xA8, 0x4E,
0x59, 0x31, 0x6B, 0xAD, 0x5E, 0x5B, 0x77, 0xB1, 0x54, 0xDC,
0x38, 0x41, 0xB6, 0x47, 0x9F, 0x73, 0xBA, 0xF8, 0xAE, 0xC4,
0xBE, 0x34, 0x01, 0x4B, 0x2A, 0x8D, 0xBD, 0xC5, 0xC6, 0xE8,
0xAF, 0xC9, 0xF5, 0xCB, 0xFB, 0xCD, 0x79, 0xCE, 0x12, 0x71,
0xD2, 0xFA, 0x09, 0xD5, 0xBC, 0x58, 0x19, 0x80, 0xDA, 0x49,
0x1D, 0xE6, 0x2E, 0xE3, 0x7E, 0xB7, 0x3B, 0xB3, 0xA0, 0xB9,
0xE5, 0x57, 0x6E, 0xD9, 0x08, 0xEB, 0xC7, 0xED, 0x81, 0xF1,
0xF2, 0xBF, 0xC0, 0xA7, 0x4A, 0xD6, 0x2B, 0xB4, 0x72, 0x9D,
0x0E, 0x6D, 0xEC, 0x48, 0xE2, 0x33
};
char vs16[] = "2409715836dbeafc";
int main ()
{
        for(int i = 0 ; i < 38 ; i ++ )
        {
                ida_chars1^=0xa5;
        }
        for(int i = 0 ; i < 66 ; i ++ )
        {
                ida_chars2^=0xa5;
        }
        for(int i = 0 ; i <42 ; i ++)
        {
                ida_chars3^=0x84;
        }
        for(int i = 0 ; i <28 ; i++ )
        {
                if(i<=2)
                {
                        ida_chars4^=0xfc;
                }
                else
                {
                        ida_chars4^=0x62;
                }
        }
        ida_chars4='"';
        printf("%s\n%s\n%s\n%s\n",ida_chars1,ida_chars2,ida_chars3,ida_chars4);
        // {9*8ga*l!Tn?@#fj'j$\\g;;
        //650f909c-7217-3647-9331-c82df8b98e98
        char ida_chars5={0};
        unsigned char debase={0xFD, 0x1E, 0x8A, 0x4E, 0x09, 0xCA, 0x90, 0x03, 0xE7, 0xF1, 0x85, 0x9F, 0x9B, 0xF7, 0x83, 0x3E};
        for(int i = 0 ; i < 36 ; i ++ )
        {
                ida_chars5=ida_chars1;
        }
        printf("%s\n",ida_chars5);
        int k = 0;
        v3 = 36;
        v5 = (char *)malloc(v3);
        v6 = (char *)malloc(v3);
        v4 = (char *)malloc(v3);
        if(v3)
        {
                v7 = 0;
                v8 = v3;
                v9 = ida_chars5;
                do
                {
                        v10 = *v9++;
                        if(v10!=45)
                        {
                                *(v5 + v7++) = v10;
                        }
                        --v8;
                       
                }while(v8);
                if ( v7 >= 1 )
          {
                        v11 = v7 - 1;
                        v12 = -8;
                        v13 = 0;
                        v14 = 0;
                        do
                        {
                                if ( (v13 | (v12 >> 2)) > 3 )
                                {
                                  v15 = v14;
                                }
                                else
                                {
                                  v15 = v14 + 1;
                                  v4 = '-';
                                }
                                v16 = *(v5 + v11--);
                                v13 += 0x40000000;
                                v4 = v16;
                                ++v12;
                                v14 = v15 + 1;
              }while ( v11 != -1 );
                }
               
                for(int i = 0 ; i < 36 ; i ++ )
                {
                        if((unsigned char)(v4-'a') <= 5u )
                        {
                          v4 = vs16-'a'+10];
                        }else if((unsigned char)(v4-'0') <= 9u )
                        {
                          v4= vs16-'0'];
                        }
                }
                printf("%s\n",v4);
        }
       char s={0};
        unsigned char K={0};
        //v4="09f09b0c-ae0e-baa9-4f2a-4be4e9d9cdc2";
        memcpy(&s,&ida_chars,0x100);
        for(int i = 0 ; i < 256 ; i ++ )
        {
                K=v4;
                //printf("%d,",K);
        }
        int j = 0;
       
        j = (unsigned char)(K - 41);
        s=s;
        s=0xd7;
        for(int i = 1 ; i<256 ; i ++ )
        {
                int tmp = (unsigned char)s;
                j =(j+(unsigned char)K+tmp)%256;
                s=s;
                s=tmp;
        }
        for(int i=0;i<0x100;i++){
                printf("%02X ",(unsigned char)s);
                if((i+1)%16==0){
                        printf("\n");
                }
        }
        int txi = 0 , txj = 0;
        unsigned char tx =0;
        for(int i = 0 ; i < 16 ; i ++ )
        {
                txi = (txi+1)%256;
                tx = (unsigned char)s;
                txj=(txj+tx)%256;
                s=s;
                s=tx;
        //        printf("%X,",s]);
                printf("%c",s[(tx+s)%256]^debase);
        }
}

~~~

输出:

~~~c
650f909c-7217-3647-9331-c82df8b98e98
!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';
android/support/v7/app/AppCompiatActivity
eq"bbb(Ljava/lang/String;)Z
650f909c-7217-3647-9331-c82df8b98e98
36f36b3c-a03e-4996-8759-8408e626c215
F0 37 E1 9B 2A 15 17 9F D7 58 4D 6E 33 A0 39 AE
04 D0 BE ED F8 66 5E 00 D6 91 2F C3 10 4C F7 A6
C1 EC 6D 0B 50 65 BB 34 FA A4 2D 3B 23 A1 96 D5
1D 38 56 0A 5D 4F E4 CC 24 0D 12 87 35 85 8E 6F
C6 13 9A D3 FC E7 08 AC B7 E9 B0 E8 41 AA 55 53
C2 42 BC E6 0F 8A 86 A8 CF 84 C5 48 74 36 07 EB
88 51 F6 7F 57 05 63 3E FE B8 C9 F5 AF DF EA 82
44 F9 CD 06 BA 30 47 40 DE FD 1C 7C 11 5C 02 31
2C 9C 5F 46 27 C4 83 73 16 90 20 76 7B F2 E3 F3
77 52 80 25 09 26 3F C7 18 1B A3 FF FB CB A9 8C
54 7A 68 B4 70 4B E2 49 22 7E A5 B6 81 9D 4E 67
F1 A7 3C D9 94 EF 32 6B 1F B1 60 B9 64 59 01 B3
7D E0 6C AD 97 19 B5 3A F4 D8 8D 98 03 93 1A DC
1E 4A C0 5A E5 D1 3D 14 C8 79 BD 43 DB 69 D2 61
95 9E 21 45 89 2B AB 29 A2 8B 2E D4 0E 62 CA 28
DA 5B 72 8F 99 75 EE 78 0C 71 BF DD CE 92 6A B2
fu0kzHp2aqtZAuY6
~~~

然后我们将fu0kzHp2aqtZAuY6输入到软件中就可以拿到flag了

## 总结

收益良多
页: [1]
查看完整版本: KCTF2019 变形金刚