DASCTF 2024.7RE全WP
想成为安卓高手 发表于 湖北 CTF 257浏览 · 2024-11-08 14:12

DosSnake

拿去DOSBOX跑一下就是个贪吃蛇,汇编代码量还挺大,ida又不能反编译,但是查找字符串发现有DASCTF,直接查看他的交叉引用找到这里。

加密逻辑就是enc[i]^=key[i%6],DASCTF的起始地址是0x324,当di为0x32A时,把di重新设为0x324。

exp

enc = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x09, 0x63, 0x34, 0x32, 0x13, 0x2A, 0x2F, 0x2A, 0x37, 0x3C, 0x23, 0x00, 0x2E, 0x20, 0x10, 0x3A, 0x27, 0x2F, 0x24, 0x3A, 0x30, 0x75, 0x67, 0x65, 0x3C]
key = b"DASCTF"

for i in range(len(enc)):
    enc[i] ^= key[i%6]

print(bytes(enc))
# DASCTF{H0wfUnnytheDosSnakeis!!!}

Strangeprograme

ida打开,搜索字符串进入的函数很简单,就是输入然后比较,而比较的字符串是个fake flag。


那就可以猜到是hook了函数,猜测是memcmp函数,但是看着几个函数的交叉引用都没找到什么东西。只能在左边函数列表中找了。
找到一个很可疑的函数,这里原本是红色的jmp 0x41D000,这里是修复之后的。

找到他跳转的位置,看这个段的名字就有东西,而且一开始进去也是一片红,但是断点打在这里,调试并没有停下。

查一下这个函数的交叉引用。就找到了这个函数,这里有反调试很显然就是我们要找的函数了,动态调试过一下反调试。

上面这个sub_411064就是获取memcmp函数的地址(其中有字符串比较找memcmp函数),下面这个函数就是hook了,第一个参数是memcmp的地址,那么第二个函数肯定就是真正的check了。
进入这个函数。
发现进行了魔改的tea加密,并且还增加了xor操作。魔改tea只对前两个DWORD进行加密,后面的输入只是xor了前两个DWORD加密后的结果。
这里重命名了一下,更好看一点。

__int64 __cdecl sub_41D250(char *input)
{
  __int64 v1; // rax
  __int64 v3; // [esp-8h] [ebp-24Ch]
  int j; // [esp+D0h] [ebp-174h]
  size_t i; // [esp+F4h] [ebp-150h]
  char *v6; // [esp+100h] [ebp-144h]
  int v7; // [esp+124h] [ebp-120h] BYREF
  int v8; // [esp+128h] [ebp-11Ch]
  int v9; // [esp+12Ch] [ebp-118h]
  int v10; // [esp+130h] [ebp-114h]
  char enc[260]; // [esp+13Ch] [ebp-108h] BYREF

  sub_4114D8(&unk_4250F3);
  enc[0] = -7;
  enc[1] = 77;
  enc[2] = 43;
  enc[3] = -68;
  enc[4] = 19;
  enc[5] = -35;
  enc[6] = 19;
  enc[7] = 98;
  enc[8] = -55;
  enc[9] = -4;
  enc[10] = -1;
  enc[11] = -119;
  enc[12] = 125;
  enc[13] = 79;
  enc[14] = -55;
  enc[15] = 15;
  enc[16] = 99;
  enc[17] = 29;
  enc[18] = 109;
  enc[19] = 82;
  enc[20] = 80;
  enc[21] = -3;
  enc[22] = 65;
  enc[23] = -29;
  enc[24] = 51;
  enc[25] = 118;
  enc[26] = 40;
  enc[27] = -105;
  enc[28] = 56;
  enc[29] = 54;
  enc[30] = -7;
  enc[31] = 107;
  enc[32] = -112;
  enc[33] = 57;
  enc[34] = 20;
  enc[35] = -125;
  enc[36] = 44;
  enc[37] = -30;
  enc[38] = 44;
  enc[39] = 31;
  memset(&enc[40], 0, 216);
  v7 = 0;
  v8 = 0;
  v9 = 0;
  v10 = 0;
  if ( j_strlen(input) == 40 )
  {
    v6 = input + 4;
    v7 = *(_DWORD *)input;
    v8 = *((_DWORD *)input + 1);
    j_tea(&v7, &unk_422100);
    *(_DWORD *)input = v7;
    *((_DWORD *)input + 1) = v8;
    for ( i = 2; i < j_strlen(input) >> 2; i += 2 )
    {
      j_tea(&v7, &unk_422100);
      *(_DWORD *)input = v7;
      *(_DWORD *)v6 = v8;
      *(_DWORD *)&input[4 * i] ^= *(_DWORD *)input;
      *(_DWORD *)&input[4 * i + 4] ^= *(_DWORD *)v6;
    }
    for ( j = 0; j < 40; ++j )
    {
      HIDWORD(v1) = j;
      if ( input[j] != enc[j] )
      {
        LODWORD(v1) = 1;
        goto LABEL_12;
      }
    }
    LODWORD(v1) = 0;
  }
  else
  {
    LODWORD(v1) = 1;
  }
LABEL_12:
  v3 = v1;
  sub_41130C();
  return v3;
}
int __cdecl tea(unsigned int *a1, _DWORD *key)
{
  int result; // eax
  unsigned int i; // [esp+DCh] [ebp-2Ch]
  int sum; // [esp+E8h] [ebp-20h]
  unsigned int next; // [esp+F4h] [ebp-14h]
  unsigned int prev; // [esp+100h] [ebp-8h]

  sub_4114D8(&unk_4250F3);
  prev = *a1;
  next = a1[1];
  sum = 0;
  for ( i = 0; i < 0x10; ++i )
  {
    prev += (key[1] + (next >> 5)) ^ (sum + next) ^ (*key + 16 * next);
    next += (key[3] + (prev >> 5)) ^ (sum + prev) ^ (key[2] + 16 * prev);
    sum -= 0x61C88647;
  }
  *a1 = prev;
  result = 4;
  a1[1] = next;
  return result;
}

据此可以写出解密脚本。

exp

from struct import unpack, pack
enc = [-7,77,43,-68,19,-35,19,98,-55,-4,-1,-119,125,79,-55,15,99,29,109,82,80,-3,65,-29,51,118,40,-105,56,54,-7,107,-112,57,20,-125,44,-30,44,31]

for i in range(len(enc)):
    enc[i] &= 0xff

print(unpack('<10I', bytes(enc)))


from struct import unpack, pack

enc = [3156954617, 1645468947, 2315254985, 264851325, 1382882659, 3812752720, 2536011315, 1811494456, 2199140752, 523035180]
key = [0x12345678, 0x09101112, 0x13141516, 0x15161718]
# 最后两个直接xor enc[0]和enc[1]即可
print(pack('<I', enc[8]^enc[0]).decode()+pack('<I', enc[9]^enc[1]).decode())

for i in range(len(enc)//2-2, 0, -1):
    prev = enc[0]
    next = enc[1]
    sum = (-0x61C88647 * 16) & 0xffffffff
    for round in range(16):
        sum += 0x61C88647
        sum &= 0xffffffff
        next -= (key[3] + (prev >> 5)) ^ (sum + prev) ^ (key[2] + 16 * prev)
        next &= 0xffffffff
        prev -= (key[1] + (next >> 5)) ^ (sum + next) ^ (key[0] + 16 * next)
        prev &= 0xffffffff
    enc[0] = prev
    enc[1] = next
    print(pack('<I', enc[2*i]^prev).decode()+pack('<I', enc[2*i+1]^next).decode())

# 最后enc[0]和enc[1]还要进行两次tea解密
for i in range(2):
    prev = enc[0]
    next = enc[1]
    sum = (-0x61C88647 * 16) & 0xffffffff
    for round in range(16):
        sum += 0x61C88647
        sum &= 0xffffffff
        next -= (key[3] + (prev >> 5)) ^ (sum + prev) ^ (key[2] + 16 * prev)
        next &= 0xffffffff
        prev -= (key[1] + (next >> 5)) ^ (sum + next) ^ (key[0] + 16 * next)
        prev &= 0xffffffff
    enc[0] = prev
    enc[1] = next


print(pack('<I', enc[0]).decode()+pack('<I', enc[1]).decode())

# DASCTF{I4TH0ok_I5S0ooFunny_Isnotit?????}

BabyAndroid

解压缩附件发现除了安卓的apk文件还有一个Request.txt文件

java层分析

用jeb打开,找到mainactivity,在里面找到可疑的NoteActivity

进入NoteActivity,可以看到有和之前txt里相似格式的输出,说明这里就是对flag进行加密的地方了。

package site.qifen.note.ui;

import android.os.AsyncTask;
import android.os.Build.VERSION;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.ListPopupWindow;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import dalvik.system.InMemoryDexClassLoader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import site.qifen.note.db.NoteDao;
import site.qifen.note.db.NoteDatabase;
import site.qifen.note.model.Note;
import site.qifen.note.model.sendRequest;
import site.qifen.note.util.App;
import site.qifen.note.util.NoteUtil;
import site.qifen.note.util.SpUtil;

public class NoteActivity extends AppCompatActivity {
    class EncryptAndSendTask extends AsyncTask {
        private EncryptAndSendTask() {
        }

        EncryptAndSendTask(site.qifen.note.ui.NoteActivity.1 x1) {
        }

        @Override  // android.os.AsyncTask
        protected Object doInBackground(Object[] arr_object) {
            return this.doInBackground(((String[])arr_object));
        }

        protected String doInBackground(String[] params) {
            String contentText = params[0];
            try {
                ByteBuffer byteBuffer0 = ByteBuffer.wrap(NoteActivity.this.loadData("Sex.jpg"));
                Class class0 = (Build.VERSION.SDK_INT < 26 ? null : new InMemoryDexClassLoader(byteBuffer0, NoteActivity.this.getClassLoader())).loadClass("site.qifen.note.ui.Encrypto");
                Method method0 = class0.getMethod("encrypt", String.class);
                NoteActivity.this.contentText_back = contentText;
                String cipher = (String)method0.invoke(class0.getDeclaredConstructor().newInstance(), NoteActivity.this.sendInit(contentText));
                Log.d("JNITest", "Server Response: " + sendRequest.sendPost("http://yuanshen.com/", "data=" + cipher));
                return cipher;
            }
            catch(Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override  // android.os.AsyncTask
        protected void onPostExecute(Object object0) {
            this.onPostExecute(((String)object0));
        }

        protected void onPostExecute(String cipher) {
            if(cipher != null) {
                String s1 = NoteActivity.this.noteWriteTitleEdit.getText().toString();
                String s2 = NoteActivity.this.noteWriteTagEdit.getText().toString();
                if(NoteActivity.this.note == null) {
                    NoteActivity.this.noteDao.insertNote(new Note(s2, s1, NoteActivity.this.contentText_back, "2024-11-08 19:37", false));
                    NoteUtil.toast("保存成功");
                    NoteActivity.this.finish();
                    return;
                }

                NoteActivity.this.note.setTitle(s1);
                NoteActivity.this.note.setContent(NoteActivity.this.contentText_back);
                NoteActivity.this.note.setDate("2024-11-08 19:37");
                NoteActivity.this.note.setTag(NoteActivity.this.contentText_back);
                NoteActivity.this.noteDao.updateNote(NoteActivity.this.note);
                NoteUtil.toast("修改成功");
                NoteActivity.this.finish();
                return;
            }

            NoteUtil.toast("加密失败");
        }
    }

    public String contentText_back;
    private String[] list;
    private ListPopupWindow listPopupWindow;
    private Note note;
    private NoteDao noteDao;
    @BindView(0x7F0900D9)  // id:note_text_info
    TextView noteTextInfo;
    @BindView(0x7F0900DA)  // id:note_write_back_btn
    Button noteWriteBackBtn;
    @BindView(0x7F0900DB)  // id:note_write_backup_btn
    Button noteWriteBackupBtn;
    @BindView(0x7F0900DC)  // id:note_write_content_text
    EditText noteWriteContentText;
    @BindView(0x7F0900DD)  // id:note_write_save_btn
    Button noteWriteSaveBtn;
    @BindView(0x7F0900DE)  // id:note_write_tag_edit
    EditText noteWriteTagEdit;
    @BindView(0x7F0900DF)  // id:note_write_tag_text
    TextView noteWriteTagText;
    @BindView(0x7F0900E0)  // id:note_write_title_edit
    EditText noteWriteTitleEdit;
    @BindView(0x7F0900E1)  // id:note_write_title_text
    TextView noteWriteTitleText;

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

    public NoteActivity() {
        this.noteDao = NoteDatabase.getInstance().noteDao();
        this.list = new String[0];
        this.contentText_back = null;
        this.listPopupWindow = null;
    }

    public byte[] loadData(String str) {
        try {
            InputStream inputStream0 = this.getAssets().open(str);
            byte[] encryptedData = new byte[inputStream0.available()];
            inputStream0.read(encryptedData);
            inputStream0.close();
            return this.rc4Decrypt("DASCTF".getBytes(), encryptedData);
        }
        catch(IOException e) {
            Log.e("错误", "加载数据时发生错误", e);
            return null;
        }
    }

    @Override  // androidx.appcompat.app.AppCompatActivity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(0x7F0C001F);  // layout:activity_note
        ButterKnife.bind(this);
        String[] arr_s = SpUtil.read("tag").split(",");
        this.list = arr_s;
        this.noteWriteTagEdit.setText(arr_s[0]);
        ListPopupWindow listPopupWindow0 = new ListPopupWindow(this);
        this.listPopupWindow = listPopupWindow0;
        listPopupWindow0.setAdapter(new ArrayAdapter(this, 0x1090003, this.list));
        this.listPopupWindow.setAnchorView(this.noteWriteTagEdit);
        if(this.getIntent().getBooleanExtra("isEdit", false)) {
            Note note0 = this.noteDao.getNoteById(this.getIntent().getIntExtra("noteId", -1));
            this.note = note0;
            if(note0 != null) {
                this.noteTextInfo.setText("改/查笔记");
                this.noteWriteTitleEdit.setText(this.note.getTitle());
                this.noteWriteTagEdit.setText(this.note.getTag());
                this.noteWriteContentText.setText(this.note.getContent());
            }
        }
    }

    @Override  // androidx.appcompat.app.AppCompatActivity
    protected void onDestroy() {
        super.onDestroy();
    }

    @OnClick({0x7F0900DA, 0x7F0900DB, 0x7F0900DD, 0x7F0900DE})  // id:note_write_back_btn
    public void onViewClicked(View view) {
        String s = this.noteWriteTitleEdit.getText().toString();
        String s1 = this.noteWriteTagEdit.getText().toString();
        String s2 = this.noteWriteContentText.getText().toString();
        switch(view.getId()) {
            case 0x7F0900DA: {  // id:note_write_back_btn
                this.finish();
                return;
            }
            case 0x7F0900DB: {  // id:note_write_backup_btn
                if(this.note == null) {
                    this.noteDao.insertNote(new Note(s1, "[备份]" + s, s2, "2024-11-08 19:37", true));
                    NoteUtil.toast("备份成功");
                    this.finish();
                    return;
                }

                return;
            }
            case 0x7F0900DD: {  // id:note_write_save_btn
                if(!s.equals("") && !s1.equals("") && !s2.equals("")) {
                    new EncryptAndSendTask(this, null).execute(new String[]{s2});
                    return;
                }

                NoteUtil.toast("标题/标签/内容不能为空");
                return;
            }
            case 0x7F0900DE: {  // id:note_write_tag_edit
                this.listPopupWindow.setAdapter(new ArrayAdapter(App.getContext(), 0x1090003, this.list));
                if(!this.listPopupWindow.isShowing()) {
                    this.showListPopWindow();
                    return;
                }

                return;
            }
            default: {
                return;
            }
        }
    }

    private byte[] rc4Decrypt(byte[] key, byte[] data) {
        int[] S = new int[0x100];
        for(int i = 0; i < 0x100; ++i) {
            S[i] = i;
        }

        int j = 0;
        for(int i = 0; i < 0x100; ++i) {
            j = (S[i] + j + (key[i % key.length] & 0xFF)) % 0x100;
            int temp = S[i];
            S[i] = S[j];
            S[j] = temp;
        }

        byte[] result = new byte[data.length];
        int i = 0;
        int j = 0;
        for(int k = 0; k < data.length; ++k) {
            i = (i + 1) % 0x100;
            j = (S[i] + j) % 0x100;
            int temp = S[i];
            S[i] = S[j];
            S[j] = temp;
            result[k] = (byte)(data[k] ^ S[(S[i] + S[j]) % 0x100]);
        }

        return result;
    }

    public native String sendInit(String arg1) {
    }

    private void showListPopWindow() {
        this.listPopupWindow.setOnItemClickListener((AdapterView adapterView, View view, int i, long l) -> {
            this.noteWriteTagEdit.setText(this.list[i]);
            this.listPopupWindow.dismiss();
        });
        this.listPopupWindow.show();
    }

    class site.qifen.note.ui.NoteActivity.1 {
    }

}

取出了assets里的sex.jpg文件,对他进行了rc4解密
这里cyberchef解密得到一个新的文件。

在用jeb打开这个文件

package site.qifen.note.ui;

import android.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class Encrypto {
    private static final String KEY = "DSACTF";
    private static final String TAG = "Encrypto";

    private static byte[] customHash(String input) {
        byte[] keyBytes = new byte[16];
        int[] temp = new int[16];
        for(int i = 0; i < input.length(); ++i) {
            int v1 = input.charAt(i);
            for(int j = 0; j < 16; ++j) {
                temp[j] = (temp[j] * 0x1F + v1) % 0xFB;
            }
        }

        for(int i = 0; i < 16; ++i) {
            keyBytes[i] = (byte)(temp[i] % 0x100);
        }

        return keyBytes;
    }

    public static String encrypt(String data) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(new byte[]{13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, "AES");
        Cipher cipher0 = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher0.init(1, secretKeySpec);
        return Base64.encodeToString(cipher0.doFinal(data.getBytes("UTF-8")), 2);
    }
}

进行了AES加密。
再看NoteActivity中,doInBackground调用了这个被解密文件中的site.qifen.note.ui.Encrypto类,再使用encrypt来加密输入。
Recipe

到这里这一堆浮点数就可以看出是个dct加密了,这里加载了so库。

so层分析

这里ida打开so库搜索encrypt,果然是dct加密。

exp

直接idct解密即可,详细可以看之前的文章。

import numpy as np
import math

def dct(x):
    result=[]
    size = len(x)
    for i in range(size):
        sum = 0
        for j in range(size):
            v1 = math.cos((j+0.5)*math.pi*i/size)
            v2 = x[j]*v1
            sum += v2
        if i:
            v3 = math.sqrt(2/size)
        else:
            v3 = math.sqrt(1/size)

        result.append(v3*sum)
    return result

def idct(x):
    result = []
    size = len(x)
    for i in range(size):
        sum = 0
        for j in range(size):
            v1 = math.cos((i + 0.5) * math.pi * j / size)
            if j == 0:
                v2 = math.sqrt(1 / size)
            else:
                v2 = math.sqrt(2 / size)
            v3 = x[j]*v2
            sum += v3 * v1

        result.append(sum)
    return result


x = np.array([458.853181,-18.325492,-18.251911,-2.097520,-21.198660,-22.304648,21.103162,-5.786284,-15.248906,15.329286,16.919499,-19.669045,30.928253,-37.588034,-16.593954,-5.505211,3.014744,6.553616,31.131491,16.472500,6.802400,-78.278577,15.280099,3.893073,56.493581,-34.576344,30.146729,4.445671,6.732204
])


z = idct(x)
print("解密: ", np.round(z))

print(bytes( [68,  65,  83,  67,  84,  70, 123,  89,  48, 117,  95,  65, 114,  51,
  82, 101,  52, 108, 108, 121,  95,  72,  64,  99, 107,  51, 114,  33,
 125]))
#DASCTF{Y0u_Ar3Re4lly_H@ck3r!}
0 条评论
某人
表情
可输入 255