- drinkSomeTea的附件.zip">drinkSomeTea drinkSomeTea的附件.zip
- Enjoyit-1的附件.zip">Enjoyit-1 Enjoyit-1的附件.zip
- replace的附件.zip">replace replace的附件.zip
drinkSomeTea drinkSomeTea的附件.zip
看到题目盲猜一波TEA加密,果不其然。之前没好好学,这次至少会做题了,之后有空要好好再看看详细的加密流程。附件给了个tea.png.out,即需要解密图片。
32位exe,无壳
在一开始的sub_401000函数里面,应该是判断是不是调试器(我没仔细看),用调试器的时候会退出程序,直接把call sub_401000 nop掉即可。
应该是一个加密函数,点进去看发现加花了
去花,Create Function,F5,发现是TEA加密,关于TEA加密脚本可以参考这个
魔改一下TEA加密即可,要注意的是,加密的时候需要用int指针(保留符号位),否则加密/解密出来的数据会不对。(我在这个地方调了很久,照着还原一次加密过程,动调的时候发现加密出来的数据怎么都不对)
最后用解密函数解密tea.png.out,脚本如下
#include <iostream>
#include <stdio.h>
#include<Windows.h>
void encrypt(int* v, uint32_t* k) {
int v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
int delta = 0x61C88647; /* a key schedule constant */
int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++) { /* basic cycle start */
sum -= delta;
v0 += ((v1 >> 5) + k1) ^ (v1 + sum) ^ ((v1 << 4) + k0);
v1 += ((v0 >> 5) + k3) ^ (v0 + sum) ^ ((v0 << 4) + k2);
} /* end cycle */
v[0] = v0; v[1] = v1;
}
void decrypt(int* v, uint32_t* k) {
int v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i; /* set up */
int delta = 0x61C88647; /* a key schedule constant */
int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++) { /* basic cycle start */
v1 -= ((v0 >> 5) + k3) ^ (v0 + sum) ^ ((v0 << 4) + k2);
v0 -= ((v1 >> 5) + k1) ^ (v1 + sum) ^ ((v1 << 4) + k0);
sum += delta;
} /* end cycle */
v[0] = v0; v[1] = v1;
}
int main()
{
DWORD key[4] = { 0x67616C66, 0x6B61667B, 0x6C665F65, 0x7D216761 };
HANDLE file = CreateFileA("tea.png.out", 0xC0000000, 0, 0, 3u, 0x80u, 0);
HANDLE v4 = file;
char *v7;
DWORD v8;
HANDLE v9;
void *v10;
DWORD NumberOfBytesRead;
DWORD NumberOfBytesWritten;
DWORD v6 = GetFileSize(file, 0);
DWORD dword_409988[15000];
if (v6 < 0xEA60)
{
SetFilePointer(v4, 0, 0, 0);
NumberOfBytesRead = 0;
ReadFile(v4, &dword_409988, v6, &NumberOfBytesRead, 0);
CloseHandle(v4);
if (v6 >> 3)
{
v7 = (char *)&dword_409988;
v8 = v6 >> 3;
do
{
decrypt((int *)v7, (uint32_t *)key);
v7 += 8;
--v8;
} while (v8);
}
v9 = CreateFileA("tea.png", 0xC0000000, 0, 0, 2u, 0x80u, 0);
v10 = v9;
NumberOfBytesWritten = 0;
WriteFile(v9, &dword_409988, v6, &NumberOfBytesWritten, 0);
CloseHandle(v10);
}
}
得到flag: DASCTF{09066cbb91df55502e6fdc83bf84cf45}
Enjoyit-1 Enjoyit-1的附件.zip
Exeinfope发现是c#写的,用dnspy32位打开
一开始定位加密位置的时候想了一会,emm我最后用Console.WriteLine定位的,其实好像点开就行…
发现应该先是base64验证,中间有Thread.Sleep,然后最后直接输出flag
打开class b的定义,base64换表了
my_base64table = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ"
std_base64table ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
s = "yQXHyBvN3g/81gv51QXG1QTBxRr/yvXK1hC="
s = s.translate(str.maketrans(my_base64table,std_base64table))
print(base64.b64decode(s))
#combustible_oolong_tea_plz
然后直接一次性把全部定义都复制到自己新建的一个c#程序,运行一次就跑出来了(而且当然要注释掉Thread.Sleep)
(代码篇幅有点长,抱歉)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
internal class b
{
// Token: 0x06000006 RID: 6 RVA: 0x00002234 File Offset: 0x00000434
public bool B(string A_0)
{
for (int i = 0; i < A_0.Length; i++)
{
if (A_0[i] < '_' || A_0[i] > 'z')
{
Console.WriteLine("Sorry,we don't have this tea");
return false;
}
}
return true;
}
// Token: 0x06000007 RID: 7 RVA: 0x00002278 File Offset: 0x00000478
public string c(string A_0)
{
string text = "";
int num = A_0.Length / 3;
int i;
for (i = 0; i < num; i++)
{
byte index = Convert.ToByte((int)('?' & A_0[i * 3] >> 2));
byte index2 = Convert.ToByte((int)((int)(A_0[i * 3] & '\u0003') << 4 | A_0[1 + i * 3] >> 4));
byte index3 = Convert.ToByte((int)((int)(A_0[1 + i * 3] & '\u000f') << 2 | A_0[2 + i * 3] >> 6));
byte index4 = Convert.ToByte((int)(A_0[2 + i * 3] & '?'));
text += this.a[(int)index].ToString();
text += this.a[(int)index2].ToString();
text += this.a[(int)index3].ToString();
text += this.a[(int)index4].ToString();
}
if (i * 3 < A_0.Length)
{
byte index = Convert.ToByte((int)('?' & A_0[i * 3] >> 2));
byte index2;
byte index3;
byte index4;
if (i * 3 + 1 < A_0.Length)
{
index2 = Convert.ToByte((int)((int)(A_0[i * 3] & '\u0003') << 4 | A_0[i * 3 + 1] >> 4));
index3 = Convert.ToByte((int)((int)(A_0[i * 3 + 1] & '\u000f') << 2));
index4 = 64;
}
else
{
index2 = Convert.ToByte((int)((int)(A_0[i * 3] & '\u0003') << 4));
index3 = 64;
index4 = 64;
}
text += this.a[(int)index].ToString();
text += this.a[(int)index2].ToString();
text += this.a[(int)index3].ToString();
text += this.a[(int)index4].ToString();
}
return text;
}
// Token: 0x06000008 RID: 8 RVA: 0x00002488 File Offset: 0x00000688
public void B(byte[] A_0)
{
string text = "";
for (int i = 0; i < A_0.Length; i++)
{
text += A_0[i].ToString("x2");
}
Console.WriteLine(text);
}
// Token: 0x06000009 RID: 9 RVA: 0x000024C8 File Offset: 0x000006C8
public void B(ref uint[] A_0, byte[] A_1)
{
uint num = 2654435464U;
uint num2 = A_0[0];
uint num3 = A_0[1];
uint num4 = 0U;
for (int i = 0; i < 32; i++)
{
num2 += ((num3 << 4 ^ num3 >> 5) + num3 ^ num4 + (uint)A_1[(int)(num4 & 3U)]);
num4 += num;
num3 += ((num2 << 4 ^ num2 >> 5) + num2 ^ num4 + (uint)A_1[(int)(num4 >> 11 & 3U)]);
}
A_0[0] = num2;
A_0[1] = num3;
}
// Token: 0x0600000A RID: 10 RVA: 0x00002534 File Offset: 0x00000734
public void c(ref uint[] A_0, byte[] A_1)
{
uint num = 2654435769U;
uint num2 = A_0[0];
uint num3 = A_0[1];
uint num4 = num * 32U;
for (int i = 0; i < 32; i++)
{
num3 -= ((num2 << 4 ^ num2 >> 5) + num2 ^ num4 + (uint)A_1[(int)(num4 >> 11 & 3U)]);
num4 -= num;
num2 -= ((num3 << 4 ^ num3 >> 5) + num3 ^ num4 + (uint)A_1[(int)(num4 & 3U)]);
}
A_0[0] = num2;
A_0[1] = num3;
}
// Token: 0x04000003 RID: 3
public string a = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ=";
}
static void Main(string[] args)
{
string text = "";
byte[] a_ = new byte[26];
byte[] array = new byte[8];
byte[] array2 = new byte[]
{
2,
5,
4,
13,
3,
84,
11,
4,
87,
3,
86,
3,
80,
7,
83,
3,
0,
4,
83,
94,
7,
84,
4,
0,
1,
83,
3,
84,
6,
83,
5,
80
};
uint[] array3 = new uint[]
{
288U,
369U
};
b b = new b();
Console.WriteLine("Welcome to my room, and please enjoy some tea by write what you want in this machine:");
string text2 = Console.ReadLine();
if (!b.B(text2))
{
//Thread.Sleep(1000000);
}
if (b.c(text2) != "yQXHyBvN3g/81gv51QXG1QTBxRr/yvXK1hC=")
{
Console.WriteLine("Oops");
//Thread.Sleep(1000000);
}
Console.WriteLine("And,wait a second!");
for (int i = 0; i < 100000; i++)
{
//Thread.Sleep(1000);
//Console.WriteLine(i + 1);
break;
}
a_ = Encoding.Default.GetBytes(text2);
b.B(ref array3, a_);
Console.WriteLine("Here is your tea, and flag!");
text += array3[0].ToString("x2");
text += array3[1].ToString("x2");
array = Encoding.Default.GetBytes(text);
Console.Write("flag{");
for (int j = 0; j < 32; j++)
{
byte[] array4 = array2;
int num = j;
array4[num] ^= array[j % array.Length];
}
Console.Write(Encoding.Default.GetString(array2));
Console.Write("}");
Console.ReadLine();
}
}
}
输入,然后得到flag
flag{4645e180540ffa7a67cfa174cde105a2}
PS:其实这题可能能直接把flag输出出来,但我没试…flag出来了就行
replace replace的附件.zip
64位,无壳
这题一开始难点我预计错了…下次应该好好先把全部函数分析完先。在main可以看出是24位flag,然后sub_401550是验证输入是否为flag格式(即flag{xxxxxx})
sub_401AE7是假的加密函数,因为之后会在sub_401925再覆盖掉所需要验证的字符串
题外话:
把这个解出来之后是fakeflag,但其实出题人粗心了,就算这个题目是这个fakeflag,最后也不对,因为有一项是计算出来是f,但没在f前面加零,416f6b116549435c2c0f1143
再点进sub_401925函数,这里把IsDebuggerPresent hook了,下一次调用IsDebuggerPresent的时候会先调用sub_4015C3
加花了,去花
sub_4015C3开头把hook脱钩了,然后下面进行加密
加密后的byte_4080E0再和416f6b116549435c2c0f1143174339023d4d4c0f183e7828做比较
下面来分析一下加密流程,unk_404020是一个128位的数组,赋值给v0,v2[j] = v0[v2[j]]即换对照表,将对应的char值转成128位数组的 char值位置。for循环1到<=5,即换对照表五次
将换表完成的24个数据,分成六组数据,每一组中的每一位数据占八位bit,再用或运算拼一起,即一组数据为24+8=32bits。当中每一组数据当中的从左往右数前八位为flag[i],再数八位为flag[i+6],以此类推,循环六次。最后再将每一组合成的32bits用sprintf转成十六进制,即8个字符(两个字符占8bits,八个字符占32bits)
按照上述流程逆推即得flag,详细见如下脚本
enflag = "416f6b11 6549435c 2c0f1143 17433902 3d4d4c0f 183e7828"
#为了方便所以手动分成六组字符串,每组之间加空格
gen_dict = [
0x80,0x65,0x2F,0x34,0x12,0x37,0x7D,0x40,0x26,0x16,
0x4B,0x4D,0x55,0x43,0x5C,0x17,0x3F,0x69,0x79,0x53,
0x18,0x02,0x06,0x61,0x27,0x08,0x49,0x4A,0x64,0x23,
0x56,0x5B,0x6F,0x11,0x4F,0x14,0x04,0x1E,0x5E,0x2D,
0x2A,0x32,0x2B,0x6C,0x74,0x09,0x6E,0x42,0x70,0x5A,
0x71,0x1C,0x7B,0x2C,0x75,0x54,0x30,0x7E,0x5F,0x0E,
0x01,0x46,0x1D,0x20,0x3C,0x66,0x6B,0x76,0x63,0x47,
0x6A,0x29,0x25,0x4E,0x31,0x13,0x50,0x51,0x33,0x59,
0x1A,0x5D,0x44,0x3E,0x28,0x0F,0x19,0x2E,0x05,0x62,
0x4C,0x3A,0x21,0x45,0x1F,0x38,0x7F,0x57,0x3D,0x1B,
0x3B,0x24,0x41,0x77,0x6D,0x7A,0x52,0x73,0x07,0x10,
0x35,0x0A,0x0D,0x03,0x0B,0x48,0x67,0x15,0x78,0x0C,
0x60,0x39,0x36,0x22,0x7C,0x58,0x72,0x68
]
flag = [0 for i in range(24)]
#将字符串转成十六进制数
data = [int(e,16) for e in enflag.split(" ")]
#把合成的数据分开,并保存到相应flag位置
for i in range(6):
flag[i] = ((data[i] >> 24)&0xFF)
flag[i+6] = ((data[i] >> 16)&0xFF)
flag[i+12] = ((data[i] >> 8)&0xFF)
flag[i+18] = ((data[i])&0xFF)
#换对照表五次
flag = [gen_dict.index(f) for f in flag]
flag = [gen_dict.index(f) for f in flag]
flag = [gen_dict.index(f) for f in flag]
flag = [gen_dict.index(f) for f in flag]
flag = [chr(gen_dict.index(f)) for f in flag]
print("".join(flag))
得到flag{Sh1t_you_dec0d3_it},题目要求最后md5提交
最终输入4820904ccb9343f00fb7ddf8acc31e85提交
PS: 激动地就要猝死,最后半分钟成功提交flag