在本次XYCTF中 我们的 凌晨人才战队 最终排名第4
RE附件:XYCTF
Reverse
聪明的信使
下载附件,IDA打开:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str1[100]; // [esp+18h] [ebp-68h] BYREF
int v5; // [esp+7Ch] [ebp-4h]
__main();
v5 = 9;
printf("Input your flag:");
scanf("%s", Str1);
encrypt(Str1, v5);
if ( !strcmp(Str1, "oujp{H0d_TwXf_Lahyc0_14_e3ah_Rvy0ac@wc!}") )
printf("Good job!");
else
printf("Try again!");
return 0;
}
发现一个字符串,猜测是位移密码,进入加密函数验证一下:
int __cdecl encrypt(char *a1, int a2)
{
int result; // eax
char v3; // [esp+Bh] [ebp-5h]
int i; // [esp+Ch] [ebp-4h]
for ( i = 0; ; ++i )
{
result = a1[i];
if ( !result )
break;
v3 = a1[i];
if ( v3 <= 96 || v3 > 122 )
{
if ( v3 > 64 && v3 <= 90 )
v3 = (v3 + a2 - 65) % 26 + 65;
}
else
{
v3 = (v3 + a2 - 97) % 26 + 97;
}
a1[i] = v3;
}
return result;
}
又知道main函数中v5传入加密函数的参数为9,加密函数对于大小写字母进行位移
找个工具直接梭了:
flag{Y0u_KnOw_Crypt0_14_v3ry_Imp0rt@nt!}
给阿姨倒一杯卡布奇诺
用IDA打开之后可以看见很明显的程序逻辑
int __fastcall main(int argc, const char **argv, const char **envp)
{
uint32_t temp[2]; // [rsp+28h] [rbp-78h] BYREF
char inp[33]; // [rsp+30h] [rbp-70h] BYREF
uint32_t key[4]; // [rsp+60h] [rbp-40h] BYREF
uint32_t array[8]; // [rsp+70h] [rbp-30h]
int i; // [rsp+9Ch] [rbp-4h]
_main(argc, argv, envp);
array[0] = -1691816635;
array[1] = 341755625;
array[2] = 1529325251;
array[3] = -442599979;
array[4] = -399760128;
array[5] = -1541333614;
array[6] = -846574750;
array[7] = -1503071168;
key[0] = 1702259047;
key[1] = 1970239839;
key[2] = 1886741343;
key[3] = 1634038879;
memset(inp, 0, sizeof(inp));
puts("please input your flag: ");
scanf("%s", inp);
if ( strlen(inp) != 32 )
{
puts("length error!!");
exit(0);
}
for ( i = 0; i <= 7; i += 2 )
{
temp[0] = *&inp[4 * i];
temp[1] = *&inp[4 * i + 4];
encrypt(temp, key);
if ( temp[0] != array[i] || temp[1] != array[i + 1] )
{
printf("sorry, your flag is wrong!");
exit(0);
}
}
printf("success!!your flag is flag{your input}");
return 0;
}
给出了一些初始化,然后输入的flag拆分,两两一组,通过for循环放入encrypt加密函数
进入加密函数:
void __cdecl encrypt(uint32_t *v, uint32_t *k)
{
uint32_t i; // [rsp+20h] [rbp-10h]
uint32_t sum; // [rsp+24h] [rbp-Ch]
uint32_t v1a; // [rsp+28h] [rbp-8h]
uint32_t v1; // [rsp+28h] [rbp-8h]
uint32_t v0; // [rsp+2Ch] [rbp-4h]
v1a = v[1];
sum = 0;
data1 ^= *v;
data2 ^= v1a;
v0 = data1;
v1 = data2;
for ( i = 0; i <= 0x1F; ++i )
{
sum += 1853174124;
v0 += ((v1 >> 5) + k[1]) ^ (v1 + sum) ^ (*k + 16 * v1) ^ (sum + i);
v1 += ((v0 >> 5) + k[3]) ^ (v0 + sum) ^ (k[2] + 16 * v0) ^ (sum + i);
}
data1 = v0;
data2 = v1;
*v = v0;
v[1] = v1;
}
发现是TEA加密
把数据提出来
sum:
发现delta并没有魔改,我放进标准TEA脚本跑出来发现是错的
由于我对TEA不太了解,最开始并没有发现这个被魔改了,后面才发现的
这里可以看见对初始值进行了异或,还有后面的sum+i
接下来就可以搓搓搓搓搓脚本了:
#include <iostream>
#include <cstdint>
#include <vector>
using namespace std;
vector<uint32_t> decrypt(const vector<uint32_t>& v, const vector<uint32_t>& k, const vector<uint32_t>& d) {
uint32_t v0 = v[0], v1 = v[1];
const uint32_t delta = 0x6E75316C;
uint32_t sum1 = delta * 32;
for (int i = 0; i < 32; ++i) {
v1 -= (((v0 << 4) + k[2]) ^ (v0 + sum1) ^ ((v0 >> 5) + k[3]) ^ (sum1 + (31 - i)));
v0 -= (((v1 << 4) + k[0]) ^ (v1 + sum1) ^ ((v1 >> 5) + k[1]) ^ (sum1 + (31 - i)));
sum1 -= delta;
}
v0 ^= d[0];
v1 ^= d[1];
return { v0, v1 };
}
string n2s(uint32_t num) {
string result;
for (int i = 0; i < 4; ++i) {
char ch = (num >> (i * 8)) & 0xFF;
result += ch; // 后置添加,不进行反转
}
return result;
}
int main() {
vector<uint32_t> c = { 0x5F797274, 0x64726168, 0x9b28ed45, 0x145ec6e9, 0x5b27a6c3, 0xe59e75d5, 0xe82c2500, 0xa4211d92, 0xcd8a4b62, 0xa668f440 };
vector<uint32_t> key = { 0x65766967, 0x756f795f, 0x7075635f, 0x6165745f };
string flag;
for (int i = 1; i < 5; ++i) {
vector<uint32_t> data = decrypt({ c[i * 2], c[i * 2 + 1] }, key, { c[i * 2 - 2], c[i * 2 - 1] });
for (int j = 0; j < 2; ++j) {
flag += n2s(data[j]);
}
}
cout << "XYCTF{" << flag << "}" << endl;
return 0;
}
何须相思煮余年
下载下来,过只有一个txt,里面是一堆16进制和一个enc数组
推测16进制是机器码,现在我所需要做的就是将机器码转换成汇编语言,先将机器码整理一下:
55 8B EC 81 EC A8 00 00 00 A1 00 40 41 00 33 C5 89 45 FC 68 9C 00 00 00 6A 00 8D 85 60 FF FF FF 50 E8 7A 0C 00 00 83 C4 0C C7 85 58 FF FF FF 27 00 00 00 C7 85 5C FF FF FF 00 00 00 00 EB 0F 8B 8D 5C FF FF FF 83 C1 01 89 8D 5C FF FF FF 83 BD 5C FF FF FF 27 0F 8D ED 00 00 00 8B 95 5C FF FF FF 81 E2 03 00 00 80 79 05 4A 83 CA FC 42 85 D2 75 25 8B 85 5C FF FF FF 8B 8C 85 60 FF FF FF 03 8D 5C FF FF FF 8B 95 5C FF FF FF 89 8C 95 60 FF FF FF E9 AC 00 00 00 8B 85 5C FF FF FF 25 03 00 00 80 79 05 48 83 C8 FC 40 83 F8 01 75 22 8B 8D 5C FF FF FF 8B 94 8D 60 FF FF FF 2B 95 5C FF FF FF 8B 85 5C FF FF FF 89 94 85 60 FF FF FF EB 73 8B 8D 5C FF FF FF 81 E1 03 00 00 80 79 05 49 83 C9 FC 41 83 F9 02 75 23 8B 95 5C FF FF FF 8B 84 95 60 FF FF FF 0F AF 85 5C FF FF FF 8B 8D 5C FF FF FF 89 84 8D 60 FF FF FF EB 38 8B 95 5C FF FF FF 81 E2 03 00 00 80 79 05 4A 83 CA FC 42 83 FA 03 75 20 8B 85 5C FF FF FF 8B 8C 85 60 FF FF FF 33 8D 5C FF FF FF 8B 95 5C FF FF FF 89 8C 95 60 FF FF FF E9 F7 FE FF FF 33 C0 8B 4D FC 33 CD E8 04 00 00 00 8B E5 5D C3
我根据个人喜好将其整理成了连续的大写16进制,又在看雪上找了一个帖子,给出了一个将机器码转成汇编语言的在线网站:https://defuse.ca/online-x86-assembler.htm
转出来:
0: 55 push ebp
1: 8b ec mov ebp,esp
3: 81 ec a8 00 00 00 sub esp,0xa8
9: a1 00 40 41 00 mov eax,ds:0x414000
e: 33 c5 xor eax,ebp
10: 89 45 fc mov DWORD PTR [ebp-0x4],eax
13: 68 9c 00 00 00 push 0x9c
18: 6a 00 push 0x0
1a: 8d 85 60 ff ff ff lea eax,[ebp-0xa0]
20: 50 push eax
21: e8 7a 0c 00 00 call 0xca0
26: 83 c4 0c add esp,0xc
29: c7 85 58 ff ff ff 27 mov DWORD PTR [ebp-0xa8],0x27
30: 00 00 00
33: c7 85 5c ff ff ff 00 mov DWORD PTR [ebp-0xa4],0x0
3a: 00 00 00
3d: eb 0f jmp 0x4e
3f: 8b 8d 5c ff ff ff mov ecx,DWORD PTR [ebp-0xa4]
45: 83 c1 01 add ecx,0x1
48: 89 8d 5c ff ff ff mov DWORD PTR [ebp-0xa4],ecx
4e: 83 bd 5c ff ff ff 27 cmp DWORD PTR [ebp-0xa4],0x27
55: 0f 8d ed 00 00 00 jge 0x148
5b: 8b 95 5c ff ff ff mov edx,DWORD PTR [ebp-0xa4]
61: 81 e2 03 00 00 80 and edx,0x80000003
67: 79 05 jns 0x6e
69: 4a dec edx
6a: 83 ca fc or edx,0xfffffffc
6d: 42 inc edx
6e: 85 d2 test edx,edx
70: 75 25 jne 0x97
72: 8b 85 5c ff ff ff mov eax,DWORD PTR [ebp-0xa4]
78: 8b 8c 85 60 ff ff ff mov ecx,DWORD PTR [ebp+eax*4-0xa0]
7f: 03 8d 5c ff ff ff add ecx,DWORD PTR [ebp-0xa4]
85: 8b 95 5c ff ff ff mov edx,DWORD PTR [ebp-0xa4]
8b: 89 8c 95 60 ff ff ff mov DWORD PTR [ebp+edx*4-0xa0],ecx
92: e9 ac 00 00 00 jmp 0x143
97: 8b 85 5c ff ff ff mov eax,DWORD PTR [ebp-0xa4]
9d: 25 03 00 00 80 and eax,0x80000003
a2: 79 05 jns 0xa9
a4: 48 dec eax
a5: 83 c8 fc or eax,0xfffffffc
a8: 40 inc eax
a9: 83 f8 01 cmp eax,0x1
ac: 75 22 jne 0xd0
ae: 8b 8d 5c ff ff ff mov ecx,DWORD PTR [ebp-0xa4]
b4: 8b 94 8d 60 ff ff ff mov edx,DWORD PTR [ebp+ecx*4-0xa0]
bb: 2b 95 5c ff ff ff sub edx,DWORD PTR [ebp-0xa4]
c1: 8b 85 5c ff ff ff mov eax,DWORD PTR [ebp-0xa4]
c7: 89 94 85 60 ff ff ff mov DWORD PTR [ebp+eax*4-0xa0],edx
ce: eb 73 jmp 0x143
d0: 8b 8d 5c ff ff ff mov ecx,DWORD PTR [ebp-0xa4]
d6: 81 e1 03 00 00 80 and ecx,0x80000003
dc: 79 05 jns 0xe3
de: 49 dec ecx
df: 83 c9 fc or ecx,0xfffffffc
e2: 41 inc ecx
e3: 83 f9 02 cmp ecx,0x2
e6: 75 23 jne 0x10b
e8: 8b 95 5c ff ff ff mov edx,DWORD PTR [ebp-0xa4]
ee: 8b 84 95 60 ff ff ff mov eax,DWORD PTR [ebp+edx*4-0xa0]
f5: 0f af 85 5c ff ff ff imul eax,DWORD PTR [ebp-0xa4]
fc: 8b 8d 5c ff ff ff mov ecx,DWORD PTR [ebp-0xa4]
102: 89 84 8d 60 ff ff ff mov DWORD PTR [ebp+ecx*4-0xa0],eax
109: eb 38 jmp 0x143
10b: 8b 95 5c ff ff ff mov edx,DWORD PTR [ebp-0xa4]
111: 81 e2 03 00 00 80 and edx,0x80000003
117: 79 05 jns 0x11e
119: 4a dec edx
11a: 83 ca fc or edx,0xfffffffc
11d: 42 inc edx
11e: 83 fa 03 cmp edx,0x3
121: 75 20 jne 0x143
123: 8b 85 5c ff ff ff mov eax,DWORD PTR [ebp-0xa4]
129: 8b 8c 85 60 ff ff ff mov ecx,DWORD PTR [ebp+eax*4-0xa0]
130: 33 8d 5c ff ff ff xor ecx,DWORD PTR [ebp-0xa4]
136: 8b 95 5c ff ff ff mov edx,DWORD PTR [ebp-0xa4]
13c: 89 8c 95 60 ff ff ff mov DWORD PTR [ebp+edx*4-0xa0],ecx
143: e9 f7 fe ff ff jmp 0x3f
148: 33 c0 xor eax,eax
14a: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
14d: 33 cd xor ecx,ebp
14f: e8 04 00 00 00 call 0x158
154: 8b e5 mov esp,ebp
156: 5d pop ebp
157: c3 ret
后来发现要是真这样看的话眼睛不都得看瞎,还不一定记得住每一条汇编指令,于是想找一个在线网站将其转化为高级语言,不过没找到
转念一想,IDA不是有这种功能吗,于是我创建了一个.txt文件,将.txt后缀删去,然后使用010 Editor
打开,把上面的字节码全部复制到空白文件里,保存之后使用IDA打开
这样就能更好的看汇编了,但是这还是不够的,因为无法按空格键查看控制流,也就是无法查看伪C代码,学过逆向的人都知道,在IDA中选中一段汇编之后按P可以定义一段函数,于是我选中了所有的汇编代码,将他们定义为一段函数,这样我们就可以使用F5大法了
没想到逻辑居然能这么清晰,我还以为代码会很丑很丑,红色的地方不管它就是了
写个脚本就完事了:
def reverse_operations(enc):
v2 = [0] * 39
for i in range(39):
if i % 4 == 0:
v2[i] = enc[i] - i
elif i % 4 == 1:
v2[i] = enc[i] + i
elif i % 4 == 2:
v2[i] = enc[i] // i
elif i % 4 == 3:
v2[i] = enc[i] ^ i
return v2
flag=""
enc = [88,88,134,87,74,118,318,101,59,92,480,60,65,41,770,110,73,31,918,39,120,27,1188,47,77,24,1352,44,81,23,1680,46,85,15,1870,66,91,16,4750]
v2 = reverse_operations(enc)
for i in range(len(v2)):
flag+=chr(v2[i])
print(flag)
#XYCTF{5b3e07567a9034d06851475481507a75}
馒头
打开IDA,有关于哈夫曼树的提示,还原哈夫曼树的节点就行了
#include <iostream>
#include <vector>
using namespace std;
struct htNode {
char data;
int weight;
htNode* parent;
htNode* lch;
htNode* rch;
// 构造函数初始化结点
htNode(char d = 0, int w = 0, htNode* p = nullptr, htNode* l = nullptr, htNode* r = nullptr)
: data(d), weight(w), parent(p), lch(l), rch(r) {}
};
void recoverHuffmanTree(htNode*& HT, int enc[], int& index, vector<int>& flag) {
htNode* lch = new htNode();
htNode* rch = new htNode();
HT->lch = lch;
HT->rch = rch;
lch->parent = rch->parent = HT;
lch->lch = lch->rch = rch->lch = rch->rch = nullptr;
int get = enc[index];
int nextget = enc[index + 1];
if (get <= 24) {
HT->data = get;
HT->weight = HT->parent->weight - HT->parent->lch->weight;
flag[HT->data - 1] = HT->weight;
index++;
return;
}
else {
if (nextget <= 24) {
HT->weight = get;
HT->data = nextget;
flag[HT->data - 1] = HT->weight;
index += 2;
return;
}
else {
HT->weight = get;
HT->data = 0;
index++;
recoverHuffmanTree(lch, enc, index, flag);
recoverHuffmanTree(rch, enc, index, flag);
}
}
}
int main() {
int enc[] = { 2270,917,446,217,106,51,20,15,17,229,114,16,11,471,233,116,
14,13,238,118,12,7,1353,557,248,123,6,24,309,137,67,3,5,172,84,4,1,796,383,
186,89,2,8,197,97,48,23,10,21,413,203,101,22,9,210,104,19,18 };
vector<int> flag(24, 0);
int index = 0;
htNode* HT = new htNode();
recoverHuffmanTree(HT, enc, index, flag);
// 输出字符权重
for (int i = 0; i < 24; i++)
cout << char(flag[i]);
return 0;
}
简爱
这题……可能我太菜了,有点迷惑
出现了不知道有什么作用的TEA,我菜菜
总之知道是个VM就行了,既然是个VM的话,程序又不复杂,我们把把vm逆过来写就可以了
用idapython提取数据,提取出vm的操作码:
import idaapi
def extract_dword_from_address(address):
#获取4个字节的数据
dword_bytes = idaapi.get_bytes(address, 4)
if dword_bytes is None:
print("Failed to read bytes at address {:X}".format(address))
return None
#将字节数据转换为DWORD
dword_value = int.from_bytes(dword_bytes, byteorder='little')
return dword_value
#指定起始地址
start_address = 0x7FFDF8422ED0
#指定提取次数
num_extract = 1029
#存储提取的DWORD数据的列表
dword_list = []
#循环提取DWORD数据
for i in range(num_extract):
address = start_address + i * 4
dword_value = extract_dword_from_address(address)
if dword_value is not None:
dword_list.append(dword_value)
print("Extracted {} DWord values starting from address {:X}".format(len(dword_list),
start_address))
print("DWord values list:", dword_list)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2,
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 4, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3
opcode = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
1, 1, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 4, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3]
def VMfunction(data):
opcodeidx = 0
dataidx = 0
while True :
if opcode[opcodeidx] == 0:
data[dataidx] += 1
opcodeidx += 1
if opcode[opcodeidx] == 1:
data[dataidx] -= 1
opcodeidx += 1
if opcode[opcodeidx] == 2:
#print("data[{}] = {}".format(dataidx,data[dataidx]))
opcodeidx += 1
dataidx += 1
if opcode[opcodeidx] == 3:
#print("data[{}] = {}".format(dataidx,data[dataidx]))
break
if opcode[opcodeidx] == 4:
data[dataidx] = data[dataidx] + data[dataidx+1] - 70
opcodeidx += 1
if opcode[opcodeidx] == 5:
data[dataidx] = data[dataidx] - data[dataidx+1] + 70
opcodeidx += 1
return data
def ReVMfunction(data):
opcodeidx = len(opcode)-1
dataidx = len(data)-1
while opcodeidx != -1 :
if opcode[opcodeidx] == 0:
data[dataidx] -= 1
opcodeidx -= 1
elif opcode[opcodeidx] == 1:
data[dataidx] += 1
opcodeidx -= 1
elif opcode[opcodeidx] == 2:
opcodeidx -= 1
dataidx -= 1
elif opcode[opcodeidx] == 3:
print("start!")
opcodeidx -= 1
elif opcode[opcodeidx] == 4:
data[dataidx] = data[dataidx] - data[dataidx+1] + 70
opcodeidx -= 1
elif opcode[opcodeidx] == 5:
data[dataidx] = data[dataidx] + data[dataidx+1] - 70
opcodeidx -= 1
return data
flag=[ord(x) for x in "flag{Love_is_not_one_sided_Love}"]
flag = ReVMfunction(flag)
print(flag)
for ch1 in flag:
print(chr(ch1),end="")
#FLAG{vm_is_A_3ecreT_l0Ve_revers}
还可以用z3,但是我懒得写了
今夕是何年
真的,运行就有flag
肯定没这么简单,于是我就先去查了一下这个文件的架构
不是 哥们,怎么还能查出未知啊,但是qemu肯定能跑,我们需要做的便是安装qemu库,详见TCPL
装好了之后直接跑就出来了:
喵喵喵的flag碎了一地
这题……我纯纯没看懂英文浪费了一堆时间,后面把英文翻译之后就随便写了
下载附件,使用IDA载入:
主函数给了三条提示,叫AI翻译一下:
int __fastcall main(int argc, const char **argv, const char **envp)
{
_main(argc, argv, envp);
puts("Hint:");
puts("1. Open in IDA and Learn about `Strings` to find the first part of the flag");
puts("2. Learn about `Functions` to find the second part of the flag which is the name of a function");
puts("3. The hint for the last part is in the function you found in the second part");
return 0;
}
按照hint先shift+F12查找一下字符串:
双击就能看见第一部分flag:
flag{My_fl@g_h4s_
再根据第二条提示查看函数窗口(ctrl+F可以查找函数,不过我没用上):
得到第二部分
br0ken_4parT_
后跟着第三条hint,进入br0ken_4parT_
函数:
int br0ken_4parT_()
{
return puts("Learn about `Xref` and Find out which function refers me to get the last part of the flag!");
}
又是一段英文,再翻译一下:学习关于Xref
并找出哪个函数引用了我来获取最后一部分的标志
Xref的意思是交叉引用,在IDA pro中可以通过ctrl+X实现,于是我们可以对于br0ken_4parT_
函数进行交叉引用:
OK了,双击上面函数的进入fun718:
在text界面就能看见剩下的flag
可以得到Bu7_Y0u_c@n_f1x_1t!}
拼接起来就是flag{My_fl@g_h4s_br0ken_4parT_Bu7_Y0u_c@n_f1x_1t!}
你真的是大学生吗
最开始没看懂为什么IDA不太好看,于是用010看了一下,发现没有PE头,我还以为是要修PE头,后来发现这是个16位程序,这里附上程序信息:
由于IDA不能正常看,而且IDA中的汇编也不太好看,我就去ghidra看了一下:
也挺抽象的,但至少比IDA好多了,在右侧可以跳转到我们需要异或的数据,而且能看见明显的异或逻辑
写出脚本:
str=[0x76, 0x0E, 0x77, 0x14, 0x60, 0x06, 0x7D, 0x04, 0x6B, 0x1E,
0x41, 0x2A, 0x44, 0x2B, 0x5C, 0x03, 0x3B, 0x0B, 0x33, 0x05,
0x15]
flag=""
for i in range(1,len(str)):
flag+=chr((str[i])^str[i-1])
print(flag)
不过这段代码发生了数组越界,会影响flag最后一位的结果,我们运行之后可以得到
xyctf{you_know_8086
最后一位必为”}“,于是我便懒得修正了
砸核桃
下载附件,查看一下文件信息:
最开始不知道打包工具是什么意思,直接用IDA看了这个程序,发现跟未脱壳的UPX打开很像,我才意识到NsPacK是个没见过的壳,于是我去网上搜了一下,准备跟着教程进行脱壳
然后发现手脱脱一半死了,xdbg闪红不知道怎么解决
实在没办法,去看雪找了个脱壳机,脱完壳之后就到了喜闻乐见的IDA环节
进去就是main函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // eax
char Buffer[52]; // [esp+4h] [ebp-38h] BYREF
memset(Buffer, 0, 50);
printf("Please Input Flag:");
gets_s(Buffer, 0x2Cu);
if ( strlen(Buffer) == 42 )
{
v4 = 0;
while ( (Buffer[v4] ^ aThisIsNotFlag[v4 % 16]) == dword_402150[v4] )
{
if ( ++v4 >= 42 )
{
printf("right!\n");
return 0;
}
}
printf("error!\n");
return 0;
}
else
{
printf("error!\n");
return -1;
}
}
可以发现这里做了异或,而两组数据我们都有,写脚本就完事了:
dword_402150 = [0x12,0x04,0x08,0x14,0x24,0x5C,0x4A,0x3D,0x56,0x0A,0x10,0x67,0x00,0x41,0x00,0x01,0x46,0x5A,0x44,0x42,0x6E,0x0C,0x44,0x72,0x0C,0x0D,0x40,0x3E,0x4B,0x5F,0x02,0x01,0x4C,0x5E,0x5B,0x17,0x6E,0x0C,0x16,0x68,0x5B,0x12,0x02,0x48,0x0E]
aThisIsNotFlag = "this_is_not_flag"
#通过异或操作来解密Buffer
Buffer = ""
for i in range(42):
Buffer += chr(ord(aThisIsNotFlag[i % 16]) ^ dword_402150[i])
print("Decrypted Buffer:", Buffer)
#Decrypted Buffer: flag{59b8ed8f-af22-11e7-bb4a-3cf862d1ee75}
记忆的时光机
被jmp了,还去除不了,无所谓,我会看汇编
.text:00005602D3177570 loc_5602D3177570:; CODE XREF: sub_5602D3177560+31↓j
.text:00005602D3177570 movzx edi, byte ptr [r12+rbx] ; 取出输入的一个字符
.text:00005602D3177575 mov esi, ebx
.text:00005602D3177577 call enc ; 进行加密
.text:00005602D317757C cmp [r13+rbx+0], al ; 进行验证
.text:00005602D3177581 setz al
.text:00005602D3177584 add rbx, 1; 序号加1
.text:00005602D3177588 movzx eax, al
.text:00005602D317758B and ebp, eax
.text:00005602D317758D cmp rbx, 30h ; '0'; 是否已经验证0x30个字符
.text:00005602D3177591 jnz short loc_5602D3177570 ; 取出输入的一个字符
.text:00005602D3177593 jmp loc_5602D31774FE
.text:00005602D3177593 sub_5602D3177560
endp
找到数据加密和验证的位置,提取出目标数据
.text:00005602D3177418 sub_5602D3177418 proc near ; DATA XREF: enc+4D↑o
.text:00005602D3177418 endbr64
.text:00005602D317741C mov rcx, [rsp+rax*8+0]
.text:00005602D3177420 add edx, 1
.text:00005602D3177423 movzx r9d, byte ptr [r11+r10] ; 取出一个key值
.text:00005602D3177428 lea eax, [rdx-1]
.text:00005602D317742B jmp rcx
.text:00005602D317742B sub_5602D3177418 endp
.text:00005602D317742B
.text:00005602D317742B ;
-----------------------------------------------------------------------
.text:00005602D317742D align 10h
.text:00005602D3177430
.text:00005602D3177430 ;
=============== S U B R O U T I N E=======================================
.text:00005602D3177430
.text:00005602D3177430
.text:00005602D3177430 sub_5602D3177430 proc near ; DATA XREF: enc+3A↑o
.text:00005602D3177430 endbr64
.text:00005602D3177434 mov rcx, [rsp+rax*8+0]
.text:00005602D3177438 add edx, 1
.text:00005602D317743B sub r8d, 6
.text:00005602D317743F lea eax, [rdx-1]
.text:00005602D3177442 jmp rcx
.text:00005602D3177442 sub_5602D3177430 endp
.text:00005602D3177442
.text:00005602D3177442 ;
-----------------------------------------------------------------------
.text:00005602D3177444 align 8
.text:00005602D3177448
.text:00005602D3177448 ;
=============== S U B R O U T I N E=======================================
.text:00005602D3177448
.text:00005602D3177448
.text:00005602D3177448 sub_5602D3177448 proc near ; DATA XREF: enc+27↑o
.text:00005602D3177448 endbr64
.text:00005602D317744C mov rcx, [rsp+rax*8+0]
.text:00005602D3177450 xor r8d, esi ; 6异或上输入
.text:00005602D3177453 add edx, 1
.text:00005602D3177456 xor r8d, 66h ; 再异或上0x66
.text:00005602D317745A lea eax, [rdx-1]
.text:00005602D317745D jmp rcx
可以看上文的汇编后面的注释:
先将输入异或上idx+6
再异或上0x66
得到的值-6
再将得到的值异或key,就是加密结果
我们提取出key和密文写python脚本:
target = [0x69, 0x58, 0x61, 0x63, 0x67, 0x4C, 0x4D, 0x32, 0x98, 0x20, 0x4D, 0x51,
0x7B, 0x25, 0x75, 0x51, 0xA3, 0x58, 0x60, 0x72, 0x42, 0x62, 0x67, 0x66, 0x37, 0x6C,
0x30, 0x46, 0x66, 0x4F, 0x5D, 0x03, 0x5D, 0xA4, 0x66, 0x01, 0x43, 0x68, 0x7D, 0x7C,
0x55, 0x4F, 0x7A, 0x3F, 0x6C, 0x12, 0x21, 0x09]
key = [ 0x69, 0x5F, 0x68, 0x61, 0x76, 0x65, 0x5F, 0x67, 0x65, 0x74, 0x5F, 0x73,
0x68, 0x65, 0x6C, 0x6C, 0x5F, 0x62, 0x75, 0x74, 0x5F, 0x77, 0x68, 0x65, 0x72, 0x65,
0x5F, 0x69, 0x73, 0x5F, 0x79, 0x6F, 0x75, 0x5F, 0x6D, 0x79, 0x5F, 0x64, 0x65, 0x61,
0x72, 0x5F, 0x62, 0x61, 0x62, 0x79, 0x21, 0x21]
flag = ""
v1 = 6
for i in range(len(target)):
ch = target[i] ^ key[i]
ch = ( ch + 6 ) &0xff
ch = ch ^ v1 ^ 0x66
flag += chr(ch)
v1 += 1
print(flag)
#flag{Br0k3n_m3m0r1es_for3v3r_Sh1n@_1n_The_H3@$T}
相逢已是上上签
查看文件信息:
最开始以为是16位程序,但是IDA居然也打不开(?
查看一下文件头:
发现这并不是一个16位的程序,他有标准的PE头的
不知道哪里被魔改了,打开一个正常的PE文件看一下:
可以看见30h行的C位00被改成了10,我们改回来就行了
这时候再看就正常了
打开IDA:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+8h] [ebp-50h]
int i; // [esp+Ch] [ebp-4Ch]
int v6[8]; // [esp+10h] [ebp-48h]
char Str[4]; // [esp+30h] [ebp-28h] BYREF
int v8; // [esp+34h] [ebp-24h]
int v9; // [esp+38h] [ebp-20h]
int v10; // [esp+3Ch] [ebp-1Ch]
int v11; // [esp+40h] [ebp-18h]
int v12; // [esp+44h] [ebp-14h]
int v13; // [esp+48h] [ebp-10h]
int v14; // [esp+4Ch] [ebp-Ch]
char v15; // [esp+50h] [ebp-8h]
*Str = 0;
v8 = 0;
v9 = 0;
v10 = 0;
v11 = 0;
v12 = 0;
v13 = 0;
v14 = 0;
v15 = 0;
v6[0] = 1718186609;
v6[1] = -1989270907;
v6[2] = -988247013;
v6[3] = 1924988163;
v6[4] = 1400902090;
v6[5] = 1302415020;
v6[6] = -2040328853;
v6[7] = -124282896;
printf("Please enter your key:");
scanf("%s", &byte_422918);
if ( 532 * *(&byte_422918 + 5)
+ 829 * *(&byte_422918 + 4)
+ 258 * *(&byte_422918 + 3)
+ 811 * *(&byte_422918 + 2)
+ 997 * *(&byte_422918 + 1)
+ 593 * byte_422918 == 292512
&& 576 * *(&byte_422918 + 5)
+ 695 * *(&byte_422918 + 4)
+ 602 * *(&byte_422918 + 3)
+ 328 * *(&byte_422918 + 2)
+ 686 * *(&byte_422918 + 1)
+ 605 * byte_422918 == 254496
&& 580 * *(&byte_422918 + 5)
+ 448 * *(&byte_422918 + 4)
+ 756 * *(&byte_422918 + 3)
+ 449 * *(&byte_422918 + 2)
+ (*(&byte_422918 + 1) << 9)
+ 373 * byte_422918 == 222479
&& 597 * *(&byte_422918 + 5)
+ 855 * *(&byte_422918 + 4)
+ 971 * *(&byte_422918 + 3)
+ 422 * *(&byte_422918 + 2)
+ 635 * *(&byte_422918 + 1)
+ 560 * byte_422918 == 295184
&& 524 * *(&byte_422918 + 5)
+ 324 * *(&byte_422918 + 4)
+ 925 * *(&byte_422918 + 3)
+ 388 * *(&byte_422918 + 2)
+ 507 * *(&byte_422918 + 1)
+ 717 * byte_422918 == 251887
&& 414 * *(&byte_422918 + 5)
+ 495 * *(&byte_422918 + 4)
+ 518 * *(&byte_422918 + 3)
+ 884 * *(&byte_422918 + 2)
+ 368 * *(&byte_422918 + 1)
+ 312 * byte_422918 == 211260 )
{
printf(&unk_41B19C);
}
else
{
_loaddll(0);
}
printf("Please enter your flag:");
scanf("%s", Str);
if ( strlen(Str) != 32 )
{
printf("Wrong length\n");
_loaddll(0);
}
v4 = strlen(Str) / 4;
sub_401000(Str, v4);
for ( i = 0; i < v4; ++i )
{
if ( v6[i] != *&Str[4 * i] )
{
printf("Wrong!!!\n");
_loaddll(0);
}
}
printf("congratulations\n");
system("pause");
return 0;
}
初始化了一些数据,byte_422918(key)需要用z3解一下,然后经过了sub_401000函数加密:
int __cdecl sub_401000(_DWORD *a1, int a2)
{
int v2; // ecx
int v3; // eax
int v4; // edx
int result; // eax
int v6; // [esp+4h] [ebp-1Ch]
int v7; // [esp+Ch] [ebp-14h]
unsigned int v8; // [esp+10h] [ebp-10h]
unsigned int v9; // [esp+18h] [ebp-8h]
unsigned int i; // [esp+1Ch] [ebp-4h]
if ( a2 > 1 )
{
v7 = 52 / a2 + 6;
v8 = 0;
v9 = a1[a2 - 1];
do
{
v8 -= 1640531527;
v6 = (v8 >> 2) & 5;
for ( i = 0; i < a2 - 1; ++i )
{
v2 = ((v9 ^ byte_422918[v6 ^ i & 5]) + (a1[i + 1] ^ v8)) ^ (((16 * v9) ^ (a1[i + 1] >> 3))
+ ((4 * a1[i + 1]) ^ (v9 >> 5)));
v3 = a1[i];
a1[i] = v2 + v3;
v9 = v2 + v3;
}
v4 = (((v9 ^ byte_422918[v6 ^ i & 5]) + (*a1 ^ v8)) ^ (((16 * v9) ^ (*a1 >> 3)) + ((4 * *a1) ^ (v9 >> 5))))
+ a1[a2 - 1];
a1[a2 - 1] = v4;
result = v4;
v9 = v4;
--v7;
}
while ( v7 );
}
return result;
}
先Z3求key:
from z3 import *
byte_422918 = [BitVec(f'byte_422918_{i}', 8) for i in range(6)]
s = Solver()
s.add(532 * byte_422918[5] +
829 * byte_422918[4] +
258 * byte_422918[3] +
811 * byte_422918[2] +
997 * byte_422918[1] +
593 * byte_422918[0] == 292512)
s.add(576 * byte_422918[5] +
695 * byte_422918[4] +
602 * byte_422918[3] +
328 * byte_422918[2] +
686 * byte_422918[1] +
605 * byte_422918[0] == 254496)
s.add(580 * byte_422918[5] +
448 * byte_422918[4] +
756 * byte_422918[3] +
449 * byte_422918[2] +
(byte_422918[1] << 9) +
373 * byte_422918[0] == 222479)
s.add(597 * byte_422918[5] +
855 * byte_422918[4] +
971 * byte_422918[3] +
422 * byte_422918[2] +
635 * byte_422918[1] +
560 * byte_422918[0] == 295184)
s.add(524 * byte_422918[5] +
324 * byte_422918[4] +
925 * byte_422918[3] +
388 * byte_422918[2] +
507 * byte_422918[1] +
717 * byte_422918[0] == 251887)
s.add(414 * byte_422918[5] +
495 * byte_422918[4] +
518 * byte_422918[3] +
884 * byte_422918[2] +
368 * byte_422918[1] +
312 * byte_422918[0] == 211260)
if s.check() == sat:
m = s.model()
for i in range(6):
print(f"byte_422918[{i}] = {m[byte_422918[i]]}")
else:
print("No solution found.")
key为"XYCTF!“
再套XXTEA:
#include <iostream>
#include <cstdint>
using namespace std;
#define DELTA 0x9E3779B9
#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 5) ^ e] ^ z)))
void btea(uint32_t* v, int n, const uint32_t key[6]) {
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) {
rounds = 6 + 52 / n;
sum = 0;
z = v[n - 1];
do {
sum += DELTA;
e = (sum >> 2) & 5;
for (p = 0; p < n - 1; p++) {
y = v[p + 1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
}
else if (n < -1) {
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do {
e = (sum >> 2) & 5;
for (p = n - 1; p > 0; p--) {
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}
int main() {
unsigned char ida_chars[] = {
113, 114, 105, 102, 133, 34, 110, 137, 27, 140,
24, 197, 3, 253, 188, 114, 202, 17, 128, 83,
172, 70, 161, 77, 107, 13, 99, 134, 240, 151,
151, 248
};
uint32_t key[] = {
88, 89, 67, 84, 70, 33
};
uint32_t* p = reinterpret_cast<uint32_t*>(ida_chars);
btea(p, -8, key);
for (int i = 0; i < 32; i++) {
cout << ida_chars[i];
}
cout << endl;
return 0;
}
//XYCTF{XXTEA_AND_Z3_1s_S0_easy!!}
baby unity
下载附件之后进去能看见il2cpp,然后去网上搜了一下,虽然没有特别好的文章,但是也算是知道要去下一个Il2CppDumper.exe,这是一个在github上开源了的项目,能够dump出il2cpp中隐藏的文件们,链接如下:
https://github.com/Perfare/Il2CppDumper/
在项目中也说明了Il2CppDumper该如何使用,这里就不过多赘述了,作为没见过的新unity题型,我也只能做到边学边做
下一步便是使用Il2CppDumper提取文件,网上只看见提出apk中的libil2cpp.so文件,相当于apk的dll,但我们需要解决的是exe,暂时卡住了,后面经过我的搜索,得到了GameAssembly.dll文件就相当于apk的那个.so文件,接下来就可以进行dump了
随后我跟着网上的教程,却出现了报错:
又卡住了,唉
后来发现那个该死的dll加了一个壳,出题人泥……我去厨房给你拿点钱
是个4.21的UPX壳,由于之前用低版本UPX工具去脱高版本UPX壳时出现过问题,这次我使用对应版本的UPX-d对其进行脱壳:
至此,我们就可以使用Il2cppDumper进行正常dump了
这里就可以看出来我们dump成功了,但由于我没有指定dump出来的路径,因此我们需要进入Il2CppDumper.exe所在的文件夹去寻找出来的文件:
不出意外是能看见这些文件的,我将它们打包到同一个文件夹里面了,而且在DummyDll里面有一个熟悉的文件Assembly-CSharp.dll,见过unity逆向的哥们应该都知道,我们平常的unity都是通过这个文件进行逆向的,但是这次我使用dnSpy打开却没有发现任何东西,只有ChackFlag这些函数名,没有见到任何的密文和函数逻辑,线索到这里又断了……
直到后来,我找到了一篇帖子:[原创]IL2CPP 逆向初探-软件逆向-看雪-安全社区|安全招聘|kanxue.com
没想到是PZ大佬写的,而且巨详细,我决定,跟着这个帖子一步一步往下做
结果又又又又卡住了,不知道怎么导入文件,后来自己找了一下才知道(我alt+F7没反应)
用IDA打开GameAssembly.dll,因为函数比较多的缘故,这需要加载一会
file这一栏有Script file选项,正好对应着Alt+F7
我们点开这个,选择ida_with_struct_py3.py文件
再选择script.json文件
再导入il2cpp.h
最后等待一段时间让IDA加载好函数名就行了
这样就算是恢复符号表了
这时候我们就用dnSpy查看一下Assembly-CSharp.dll里面有哪些函数
我们去IDA中找到对应的函数
进入CheckkkkkkkkkkFlag函数
这一段就是传入我们的input的,然后再加密和比对,我们查看StringLiteral_4850
提取出字符串:XIcKYJU8Buh:UeV:BKN{U[JvUL??VuZ?CXJ;AX^{Ae]gA[]gUecb@K]ei^22
我们再去看看加密函数EEEEEEEEEEEEEncrypt
不难看出,传入的字符串首先进行了base64编码,然后与0xF进行了异或,我们据此可以写出脚本
import base64
enc=""
flag=""
str="XIcKYJU8Buh:UeV:BKN{U[JvUL??VuZ?CXJ;AX^{Ae]gA[]gUecb@K]ei^22"
for i in range(len(str)) :
enc+=chr(ord(str[i])^0xF)
print(enc)
#WFlDVEZ7Mzg5ZjY5MDAtZTEyZC00YzU0LWE4NWQtNjRhNTRhZjlmODRjfQ==
flag=base64.b64decode(enc)
print(flag)
#XYCTF{389f6900-e12d-4c54-a85d-64a54af9f84c}
当然,我们还可以使用赛博厨子直接烤了
至此,解出flag为XYCTF{389f6900-e12d-4c54-a85d-64a54af9f84c}
DebugMe
之前一直没试过安卓动调,这次正好学一下,在网上找了篇帖子跟着做
先安装一下附件,打开看看什么成分
click就会出现”flag呢“
然后我再用jadx反编译了一下:
有很多奇怪的东西,反正我是毛也看不出
就按照题目提示去动调吧,这边我用的是雷电9
将ADB的路径填上去
然后运行程序就行了
这时候我发现,Click就会出现flag了,好神奇
此外,我们还可以通过修改smali代码过调试检测,更改其中的if判断就行了,各位可以自行探究
ez_cube
题目提示给了说是魔方,当然,我进去之后也能知道,先用IDA打开
直接能看见main函数jmp到main_0函数,这就是主要逻辑所在地:
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
int i; // [rsp+44h] [rbp+24h]
char v5; // [rsp+64h] [rbp+44h]
int v6; // [rsp+84h] [rbp+64h]
j___CheckForDebuggerJustMyCode(&unk_7FF73E4E40A2, argv, envp);
for ( i = 0; i < 9; ++i )
{
qword_7FF73E4DFB60[i] = &aRed;
qword_7FF73E4DFB00[i] = "Blue";
qword_7FF73E4DFAA0[i] = "Green";
qword_7FF73E4DFA40[i] = "Orange";
qword_7FF73E4DF9E0[i] = "Yellow";
qword_7FF73E4DF980[i] = "White";
}
qword_7FF73E4DFB00[1] = &aRed;
qword_7FF73E4DFB60[1] = "Green";
qword_7FF73E4DFAA0[1] = "Blue";
while ( 1 )
{
do
v5 = getchar();
while ( v5 == 10 );
switch ( v5 )
{
case 'R':
sub_7FF73E4D1375();
break;
case 'U':
sub_7FF73E4D13BB();
break;
case 'r':
sub_7FF73E4D1366();
break;
case 'u':
sub_7FF73E4D115E();
break;
}
++dword_7FF73E4DF1C0;
v6 = sub_7FF73E4D1389();
if ( v6 == 1 )
break;
if ( v6 == 2 )
goto LABEL_19;
}
sub_7FF73E4D119F(&unk_7FF73E4DCCA0);
LABEL_19:
system("pause");
return 0;
}
怎么说呢,挺丑的,先分析程序吧:
第一步先初始化六个面与九个方块,每个面颜色都相同,也就是一个完整的,未被打乱的魔方
接下来我们更换了三个块,分别是将蓝色面第一行最中间的方块换成了红色,将红色面最中间的方块换成了绿色,将绿色面最中间的方块换成了蓝色,会玩魔方的都知道,这是一个标准的三棱换
在魔方中这通常是最后一步,只需要用到R U' R U R U R U' R' U' R' R'
这个公式就可以还原魔方,当然,我还是进去看了判断的地方,它说不能大于12步,这个公式正好12步
继续观察上述程序,发现下面只有有R U r u
四种还原方式,我有点懒得进去看了,就猜测U和R代表着正常的U和R,u和r则代表U‘和R’,试试看吧,如果真是按照我所说的,则路径为RuRURURururr
否则则为rUrururURURR
嗯……果然是flag{RuRURURururr}
看来出题人跟我学的魔方公式一样,嘿嘿,幸好我是魔方糕手!
真的是,搞得我一边玩魔方一边写题QAQ
此外,我们还可以爆破魔方步骤,这里附上一个爆破脚本:
from itertools import *
red = [''] * 9
blue = [''] * 9
green = [''] * 9
orange = [''] * 9
yellow = [''] * 9
white = [''] * 9
def init_cube():
for i in range(9):
red[i] = "red"
blue[i] = "Blue"
green[i] = "Green"
orange[i] = "Orange"
yellow[i] = "Yellow"
white[i] = "White"
blue[1] = "red"
red[1] = "Green"
green[1] = "Blue"
def MOVE_R():
v1 = red[2]
v2 = red[5]
v3 = red[8]
red[2] = white[2]
red[5] = white[5]
red[8] = white[8]
white[2] = orange[6]
white[5] = orange[3]
white[8] = orange[0]
orange[0] = yellow[8]
orange[3] = yellow[5]
orange[6] = yellow[2]
yellow[2] = v1
yellow[5] = v2
yellow[8] = v3
v4 = green[1]
green[1] = green[3]
green[3] = green[7]
green[7] = green[5]
green[5] = v4
v5 = green[0]
green[0] = green[6]
green[6] = green[8]
green[8] = green[2]
green[2] = v5
def MOVE_U():
v1 = red[0]
v2 = red[1]
v3 = red[2]
red[0] = green[0]
red[1] = green[1]
red[2] = green[2]
green[0] = orange[0]
green[1] = orange[1]
green[2] = orange[2]
orange[0] = blue[0]
orange[1] = blue[1]
orange[2] = blue[2]
blue[0] = v1
blue[1] = v2
blue[2] = v3
v4 = yellow[1]
yellow[1] = yellow[3]
yellow[3] = yellow[7]
yellow[7] = yellow[5]
yellow[5] = v4
v5 = yellow[0]
yellow[0] = yellow[6]
yellow[6] = yellow[8]
yellow[8] = yellow[2]
yellow[2] = v5
def MOVE_r():
v1 = red[2]
v2 = red[5]
v3 = red[8]
red[2] = yellow[2]
red[5] = yellow[5]
red[8] = yellow[8]
yellow[2] = orange[6]
yellow[5] = orange[3]
yellow[8] = orange[0]
orange[0] = white[8]
orange[3] = white[5]
orange[6] = white[2]
white[2] = v1
white[5] = v2
white[8] = v3
v4 = green[1]
green[1] = green[5]
green[5] = green[7]
green[7] = green[3]
green[3] = v4
v5 = green[0]
green[0] = green[2]
green[2] = green[8]
green[8] = green[6]
green[6] = v5
def MOVE_u():
v1 = red[0]
v2 = red[1]
v3 = red[2]
red[0] = blue[0]
red[1] = blue[1]
red[2] = blue[2]
blue[0] = orange[0]
blue[1] = orange[1]
blue[2] = orange[2]
orange[0] = green[0]
orange[1] = green[1]
orange[2] = green[2]
green[0] = v1
green[1] = v2
green[2] = v3
v4 = yellow[1]
yellow[1] = yellow[5]
yellow[5] = yellow[7]
yellow[7] = yellow[3]
yellow[3] = v4
v5 = yellow[0]
yellow[0] = yellow[2]
yellow[2] = yellow[8]
yellow[8] = yellow[6]
yellow[6] = v5
def Is_right():
Count = 0
for i in range(9):
if red[i] == "red":
Count += 1
if blue[i] == "Blue":
Count += 1
if green[i] == "Green":
Count += 1
if orange[i] == "Orange":
Count += 1
if yellow[i] == "Yellow":
Count += 1
if white[i] == "White":
Count += 1
#print(Count)
if Count != 54:
return False
return True
def main(flag):
#print(flag)
init_cube()
for i in flag:
if i == "R":
MOVE_R()
if i == "U":
MOVE_U()
if i == 'r':
MOVE_r()
if i == 'u':
MOVE_u()
if Is_right():
return flag
def get_flag():
table = "RrUu"
for string in product(table, repeat=12):
flag = "".join(string)
ret = main(flag)
if ret != None:
print(ret)
return
get_flag()
ez_rand
这个题目提示还挺明显的,rand就是随机数的意思,这应该是一道有关伪随机数的题
文件信息如下:
用IDA看一下,进去就是main函数,直接F5就行了:
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned __int64 v3; // rbx
unsigned __int16 v4; // ax
int v5; // edi
__int64 v6; // rsi
int v7; // eax
int v9[7]; // [rsp+20h] [rbp-50h]
char v10; // [rsp+3Ch] [rbp-34h]
__int16 v11; // [rsp+3Dh] [rbp-33h]
__int128 v12; // [rsp+40h] [rbp-30h]
__int64 v13; // [rsp+50h] [rbp-20h]
int v14; // [rsp+58h] [rbp-18h]
__int16 v15; // [rsp+5Ch] [rbp-14h]
char v16; // [rsp+5Eh] [rbp-12h]
v13 = 0i64;
v12 = 0i64;
v14 = 0;
v15 = 0;
v16 = 0;
printf("请输入flag:");
scanf_s("%s");
v9[0] = -362017699;
v11 = 0;
v3 = -1i64;
v9[1] = 888936774;
v9[2] = 119759538;
v9[3] = -76668318;
v9[4] = -1443698508;
v9[5] = -2044652911;
v9[6] = 1139379931;
v10 = 77;
do
++v3;
while ( *(&v12 + v3) );
v4 = time64(0i64);
srand(v4);
v5 = 0;
if ( v3 )
{
v6 = 0i64;
do
{
v7 = rand();
if ( (*(&v12 + v6) ^ (v7
+ ((((2155905153i64 * v7) >> 32) & 0x80000000) != 0i64)
+ (((2155905153i64 * v7) >> 32) >> 7))) != *(v9 + v6) )
{
printf("Error???\n");
exit(0);
}
++v5;
++v6;
}
while ( v5 < v3 );
}
printf("Right???\n");
system("pause");
return 0;
}
大体逻辑就是定义了v9数组的数据,然后生成了一个等同于v9元素个数的随机数v7,使其与v9数组进行异或运算,我们反着异或出来就是flag了
但是既然是随机数我们应该怎么对它进行数据提取呢?这时候我们可以看见他的随机数种子是通过time来取的,C语言中的srand(time)是伪随机,我们需要对这个随机数种子进行爆破,题目描述给出了flag头为"XYCTF",我们可以根据这个信息去爆破随机数种子,即我们将v9的前5位与生成的前五位随机数做异或,如果结果与“XYCTF”相同,则那个随机数种子就是我们需要求的结果
这里v9不能直接访问,为了省事我启动动态调试提取v9的数据:
前五位则分别是0x5D, 0x0C, 0x6C, 0xEA, 0x46,我们根据这个去爆破随机数
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
unsigned char str[5] = { 0x5D, 0x0C, 0x6C, 0xEA, 0x46 };
unsigned char random[6] = { 0 };
unsigned char flag[6] = { 'X', 'Y', 'C', 'T', 'F', '\0' };
for (int i = 0xFFFF; i >= 0; i--) {
srand(i);
for (int j = 0; j < 5; j++) {
random[j] = rand() % 0xFF;
}
bool found = true;
for (int j = 0; j < 5; j++) {
if ((random[j] ^ str[j]) != flag[j]) {
found = false;
break;
}
}
if (found) {
cout << "找到了!是:" << i << endl;
break;
}
else
cout << "不是" << i << "捏" << endl;
}
return 0;
}
爆破出来随机数是21308
,然后我们再用这个随机数种子生成等同于flag长度(即29)的个数的随机数
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
srand(21308);
for (int i = 0; i < 29; i++) {
int num = rand();
cout << num << ",";
}
}
得到随机数后写出Python脚本求解flag发现是一堆乱码,由于上文的v9是固定的,肯定是生成的随机数不对,这才发现随机数有点太大了,由于都是2位一组的16进制数进行异或,我们的rand()应该对0xFF取模
下面是更正之后的脚本:
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
srand(21308);
for (int i = 0; i < 29; i++) {
int num = rand() % 0xFF ;
cout << num << ",";
}
}
得到该随机数种子生成的2位16进制范围内的随机数,然后就可以写出python脚本了:
random=[5,85,47,190,0,98,174,116,220,6,124,54,17,125,61,203,235,187,194,246,194,34,126,227,186,253,144,98,48]
str=[0x5D, 0x0C, 0x6C, 0xEA, 0x46, 0x19, 0xFC, 0x34, 0xB2, 0x62,
0x23, 0x07, 0x62, 0x22, 0x6E, 0xFB, 0xB4, 0xE8, 0xF2, 0xA9,
0x91, 0x12, 0x21, 0x86, 0xDB, 0x8E, 0xE9, 0x43, 0x4D]
flag=""
aaa=""
for i in range(len(str)):
flag+=chr(str[i]^random[i])
print(flag)
#XYCTF{R@nd_1s_S0_S0_S0_easy!}
trustme
安装好程序发现是一个登录系统
将下载下来的.apk使用jadx反编译出来就是这样
package com.swdd.trustme;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
public static byte[] RC4(byte[] arr_b, byte[] arr_b1) {
int[] arr_v = new int[0x100];
byte[] arr_b2 = new byte[0x100];
byte[] arr_b3 = new byte[arr_b.length];
int v = 0;
int v1;
for(v1 = 0; v1 < 0x100; ++v1) {
arr_v[v1] = v1;
arr_b2[v1] = arr_b1[v1 % arr_b1.length];
}
int v2 = 0;
int v3 = 0;
while(v2 < 0x100) {
int v4 = arr_v[v2];
v3 = v3 + v4 + arr_b2[v2] & 0xFF;
arr_v[v2] = arr_v[v3];
arr_v[v3] = v4;
++v2;
}
int v5 = 0;
while(v < arr_b.length) {
v5 = v5 + 1 & 0xFF;
int v6 = arr_v[v5];
v3 = v3 + v6 & 0xFF;
arr_v[v5] = arr_v[v3];
arr_v[v3] = v6;
arr_b3[v] = (byte)(arr_v[arr_v[v5] + v6 & 0xFF] ^ arr_b[v]);
++v;
}
return arr_b3;
}
public static String bytesToHex(byte[] arr_b) {
StringBuilder stringBuilder0 = new StringBuilder();
int v;
for(v = 0; v < arr_b.length; ++v) {
String s = Integer.toHexString(arr_b[v] & 0xFF);
if(s.length() == 1) {
stringBuilder0.append('0');
}
stringBuilder0.append(s);
}
return stringBuilder0.toString();
}
public void onClick(View view0) {
TextView textView0 = (TextView)this.findViewById(id.username);
TextView textView1 = (TextView)this.findViewById(id.password);
textView0.getText().toString();
if(MainActivity.bytesToHex(MainActivity.RC4(textView1.getText().toString().getBytes(), "XYCTF".getBytes())).equals("5a3c46e0228b444decc7651c8a7ca93ba4cb35a46f7eb589bef4")) {
Toast.makeText(this, "成功!", 0);
}
}
@Override // androidx.fragment.app.FragmentActivity
protected void onCreate(Bundle bundle0) {
super.onCreate(bundle0);
this.setContentView(layout.activity_main);
}
}
其中RC4的密文和密钥都给出来了,解密一下试试
得到了username,不过剩下的就没什么思路了
再找找看,发现了一个新的.apk
根据.apk名称猜测,这是个被加了壳的.apk,显然我是不会脱壳的,于是寻找别的方法
这里找到了一个.db文件,首先我们得知道.db文件是什么,在安卓开发中,我们常用SQLite数据库,因为它轻巧,而.db文件就是它的数据库文件,于是我想导出这个.db文件查看一下,当我双击.db的时候,jadx爆炸了……直接卡住死机,我也不知道为什么
唉,用JEB打开吧
导出这个文件,用010打开看看
发现了这个文件大部分地方都是被FF填充的
而事实却是:
.db文件通常是指 SQLite 数据库文件。SQLite 是一种轻量级的关系型数据库管理系统,广泛用于移动设备、嵌入式系统以及桌面应用程序中。SQLite 数据库存储在单个文件中,并使用以.db作为文件扩展名的格式。
这些.db文件包含了表、索引、视图等数据库对象的定义,以及实际存储的数据。SQLite 是一个自包含的、零配置的、服务器不间断的数据库引擎,不需要单独的服务器进程来管理数据库。这使得它非常适合嵌入式设备或需要简单数据库解决方案的应用程序。
在 SQLite 的.db文件中,未存放数据的部分通常会使用零填充。这意味着在文件被创建或者扩展时,未使用的部分会被填充为零字节,这有助于确保文件的完整性并占据磁盘空间以满足文件系统的分配需求。
如果你查看一个.db文件的十六进制表示,你可能会看到一系列的零字节填充,直到遇到存储了数据的部分。SQLite 会在文件中动态分配空间以存储数据,因此未使用的部分会保持零填充状态。
众所周知,一个16进制数异或本身等于0,因此我们只需要将这个.db文件异或0xFF就可以得到真实的数据了,这时候我们可以通过010 Editor的工具对文件整体进行异或了
这样文件头就出现了SQLite字样,再往下看看,存有字符串的地方很少,大部分都是用00填充的
我们在第三段数据中找到了flag
XYCTF{And0r1d_15_V3ryEasy}
what' this
function Xor(num1, num2)
local tmp1 = num1
local tmp2 = num2
local str = ""
repeat
local s1 = tmp1 % 2
local s2 = tmp2 % 2
if s1 == s2 then
str = "0" .. str
else
str = "1" .. str
end
tmp1 = math.modf(tmp1 / 2)
tmp2 = math.modf(tmp2 / 2)
until tmp1 == 0 and tmp2 == 0
return tonumber(str, 2)
end
value = ""
output = ""
i = 1
while true do
local temp = string.byte(flag, i)
temp = string.char(Xor(temp, 8) % 256)
value = value .. temp
i = i + 1
if i > string.len(flag) then
break
end
end
for _ = 1, 1000 do
x = 3
y = x * 3
z = y / 4
w = z - 5
if w == 0 then
print("This line will never be executed")
end
end
for i = 1, string.len(flag) do
temp = string.byte(value, i)
temp = string.char(temp + 3)
output = output .. temp
end
result = output:rep(10)
invalid_list = {
1,
2,
3
}
for _ = 1, 20 do
table.insert(invalid_list, 4)
end
for _ = 1, 50 do
result = result .. "A"
table.insert(invalid_list, 4)
end
for i = 1, string.len(output) do
temp = string.byte(output, i)
temp = string.char(temp - 1)
end
for _ = 1, 30 do
result = result .. string.lower(output)
end
for _ = 1, 950 do
x = 3
y = x * 3
z = y / 4
w = z - 5
if w == 0 then
print("This line will never be executed")
end
end
for _ = 1, 50 do
x = -1
y = x * 4
z = y / 2
w = z - 3
if w == 0 then
print("This line will also never be executed")
end
end
require("base64")
obfuscated_output = to_base64(output)
obfuscated_output = string.reverse(obfuscated_output)
obfuscated_output = string.gsub(obfuscated_output, "g", "3")
obfuscated_output = string.gsub(obfuscated_output, "H", "4")
obfuscated_output = string.gsub(obfuscated_output, "W", "6")
invalid_variable = obfuscated_output:rep(5)
if obfuscated_output == "==AeuFEcwxGPuJ0PBNzbC16ctFnPB5DPzI0bwx6bu9GQ2F1XOR1U" then
print("You get the flag.")
else
print("F**k!")
end
这是反编译出来的关键部分代码,我在52上找了个.jar代码反编译lua程序,反编译出来1.5w多个字符的代码,而且全是混淆,不过还好,关键代码都在代码的最后部分
分析一下:最后面先是base64,然后反转编码,再进行字符替换
最开始我以为只有这些,但是处理完之后没有什么用,base64解不出来
只解出STN_Qv@onmlpoB3<>A>qmqmBo3A?Bn<lppAnx
所以还有别的操作,我们主要一个寻找代码中对output和flag等字样的操作
发现这两个地方先异或8后+3
可以写解密脚本了:
str = "STN_Qv@onmlpoB3<>A>qmqmBo3A?Bn<lppAnx"
flag = ""
for i in range(len(str)):
flag += chr((ord(str[i]) - 3) ^ 8)
print(flag)
#XYCTF{5dcbaed781363fbfb7d8647c1aee6c}
easy language
从图标就可以看出来是一个易语言程序
正常用IDA是包看不了的,我们可以使用易语言插件IDA易语言反编译插件E-Decompiler - 『逆向资源区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn以及IDA7.5支持中文函数命名的办法 - 『逆向资源区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
再用IDA反编译出来可以看见主逻辑,发现是AES-ECB和basse64
再去动态调试拿去数据
密文
key:"welcometoxyctf!!"
附上脚本:
from Crypto.Cipher import AES
import base64
#根据动态调试找到要对比的目标数据!
target = b'RZy/zVEWMFxaCbzChAg8x26XZYr51rNVnM+zBoBp3gya93L9QQXpFRin1JE33vyx'
base64_decoded = base64.b64decode(target)
print("解密出来的数据:",base64_decoded)
#给定的字节串
byte_string =b'E\x9c\xbf\xcdQ\x160\\Z\t\xbc\xc2\x84\x08<\xc7n\x97e\x8a\xf9\xd6\xb3U\x9c\xcf\xb3\x06\x80i\xde\x0c\x9a\xf7r\xfdA\x05\xe9\x15\x18\xa7\xd4\x917\xde\xfc\xb1'
#将字节串转换为整数
integer_value = int.from_bytes(byte_string, byteorder='big')
#将整数拆分为两个部分,每部分包含16个字节
first_part_integer = integer_value >> 128*2
second_part_integer = (integer_value >> 128) & ((1 << 128) - 1)
third_part_integer = (integer_value) & ((1 << 128) - 1)
#将每个部分转换回字节串
first_part_bytes = first_part_integer.to_bytes(16, byteorder='big')
second_part_bytes = second_part_integer.to_bytes(16, byteorder='big')
third_part_bytes = third_part_integer.to_bytes(16, byteorder='big')
#打印结果
print("第一部分:", first_part_bytes)
print("第二部分:", second_part_bytes)
print("第三部分:", third_part_bytes)
key = b'welcometoxyctf!!' #动调得到的密钥
cipher = AES.new(key, AES.MODE_ECB) #动态调试知道填充模式是PKCS5Padding
part1 = cipher.decrypt(first_part_bytes)
part2 = cipher.decrypt(second_part_bytes)
part3 = cipher.decrypt(third_part_bytes)
print("成功解出flag 第一部分:",part1)
print("成功解出flag 第二部分:",part2)
print("成功解出flag 第三部分:",part3)
print("flag是:",part1+part2+part3)
ez_enc
不是 哥们
IDA看主逻辑:
for ( i = 0; i < (j_strlen(Str) - 1); ++i )
Str[i] = aImouto[i % 6] ^ (Str[i + 1] + Str[i] % 20);
for ( j = 0; j < j_strlen(Str); ++j )
{
if ( Str[j] != byte_14001E008[j] )
{
sub_1400111A4("Wrong");
return 0;
}
}
sub_1400111A4("Right,but where is my Imouto?\n");
return 0;
}
单字节比对加简单加密,直接爆爆爆爆爆爆爆
深搜递归爆破:
target = [0x27, 0x24, 0x17, 0x0B, 0x50, 0x03, 0xC8, 0x0C, 0x1F, 0x17, 0x36, 0x55, 0xCB, 0x2D, 0xE9, 0x32, 0x0E, 0x11, 0x26, 0x02, 0x0C, 0x07, 0xFC, 0x27, 0x3D, 0x2D, 0xED, 0x35, 0x59, 0xEB, 0x3C, 0x3E, 0xE4, 0x7D ]
aImouto =[0x49, 0x4D, 0x6F, 0x75, 0x74, 0x6F]
flag = [0]*len(target)
flag[len(target)-1] = target[len(target)-1]
print(flag)
def checkflag(idx,reslut):
if(idx == -1):
print("flag爆破完成!")
for i in range(len(reslut)):
print(chr(reslut[i]),end="")
print("")
return
for ch1 in range(128):
if(target[idx] == aImouto[idx%6]^(reslut[idx+1]+ch1%20)):
reslut[idx] = ch1
checkflag(idx-1,reslut)
if(ch1 == 128 and reslut[idx] == 0):
return
checkflag(32,flag)
由于加密方式的问题,从后往前的,第一位是会有多解的,我们看见flag开头的就行了
flag{!_r3ea11y_w4nt_@_cu7e_s1$ter}
ez_math
PyInstaller打包的exe,版本好像还挺新的,去github下一个最新版的pyinstxtractor解包
我的python3.12对某些老版本的pyinstxtractor不太兼容,所以用了最新版的
再把里面的pyc反编译一下:
#uncompyle6 version 3.9.1
#Python bytecode version base 3.8.0 (3413)
#Decompiled from: Python 3.6.12 (default, Feb 9 2021, 09:19:15)
#[GCC 8.3.0]
#Embedded file name: ezmath.py
flag = [ord(i) for i in input("flag:")]
if len(flag) == 32:
if sum([flag[23] for _ in range(flag[23])]) + sum([flag[12] for _ in range(flag[12])]) + sum([flag[1] for _ in range(flag[1])]) - sum([flag[24] for _ in range(222)]) + sum([flag[22] for _ in range(flag[22])]) + sum([flag[31] for _ in range(flag[31])]) + sum([flag[26] for _ in range(flag[26])]) - sum([flag[9] for _ in range(178)]) - sum([flag[29] for _ in range(232)]) + sum([flag[17] for _ in range(flag[17])]) - sum([flag[23] for _ in range(150)]) - sum([flag[6] for _ in range(226)]) - sum([flag[7] for _ in range(110)]) + sum([flag[19] for _ in range(flag[19])]) + sum([flag[2] for _ in range(flag[2])]) - sum([flag[0] for _ in range(176)]) + sum([flag[10] for _ in range(flag[10])]) - sum([flag[12] for _ in range(198)]) + sum([flag[24] for _ in range(flag[24])]) + sum([flag[9] for _ in range(flag[9])]) - sum([flag[3] for _ in range(168)]) + sum([flag[8] for _ in range(flag[8])]) - sum([flag[2] for _ in range(134)]) + sum([flag[14] for _ in range(flag[14])]) - sum([flag[13] for _ in range(170)]) + sum([flag[4] for _ in range(flag[4])]) - sum([flag[10] for _ in range(142)]) + sum([flag[27] for _ in range(flag[27])]) + sum([flag[15] for _ in range(flag[15])]) - sum([flag[15] for _ in range(224)]) + sum([flag[16] for _ in range(flag[16])]) - sum([flag[11] for _ in range(230)]) - sum([flag[1] for _ in range(178)]) + sum([flag[28] for _ in range(flag[28])]) - sum([flag[5] for _ in range(246)]) - sum([flag[17] for _ in range(168)]) + sum([flag[30] for _ in range(flag[30])]) - sum([flag[21] for _ in range(220)]) - sum([flag[22] for _ in range(212)]) - sum([flag[16] for _ in range(232)]) + sum([flag[25] for _ in range(flag[25])]) - sum([flag[4] for _ in range(140)]) - sum([flag[31] for _ in range(250)]) - sum([flag[28] for _ in range(150)]) + sum([flag[11] for _ in range(flag[11])]) + sum([flag[13] for _ in range(flag[13])]) - sum([flag[14] for _ in range(234)]) + sum([flag[7] for _ in range(flag[7])]) - sum([flag[8] for _ in range(174)]) + sum([flag[3] for _ in range(flag[3])]) - sum([flag[25] for _ in range(242)]) + sum([flag[29] for _ in range(flag[29])]) + sum([flag[5] for _ in range(flag[5])]) - sum([flag[30] for _ in range(142)]) - sum([flag[26] for _ in range(170)]) - sum([flag[19] for _ in range(176)]) + sum([flag[0] for _ in range(flag[0])]) - sum([flag[27] for _ in range(168)]) + sum([flag[20] for _ in range(flag[20])]) - sum([flag[20] for _ in range(212)]) + sum([flag[21] for _ in range(flag[21])]) + sum([flag[6] for _ in range(flag[6])]) + sum([flag[18] for _ in range(flag[18])]) - sum([flag[18] for _ in range(178)]) + 297412 == 0:
print("yes")
有点脑洞,不知道怎么写,z3跑不了
后面给了题目提示,平方,我们可以猜测一下:
flag = [0]*32
flag[24]=222// 2
flag[9]=178// 2
flag[29]=232// 2
flag[23]=150// 2
flag[6]=226// 2
flag[7]=110// 2
flag[0]=176// 2
flag[12]=198// 2
flag[3]=168// 2
flag[2]=134// 2
flag[13]=170// 2
flag[10]=142// 2
flag[15]=224// 2
flag[11]=230// 2
flag[1]=178// 2
flag[5]=246// 2
flag[17]=168// 2
flag[21]=220// 2
flag[22]=212// 2
flag[16]=232// 2
flag[4]=140// 2
flag[31]=250// 2
flag[28]=150// 2
flag[14]=234// 2
flag[8]=174// 2
flag[25]=242// 2
flag[30]=142// 2
flag[26]=170// 2
flag[19]=176// 2
flag[27]=168// 2
flag[20]=212// 2
flag[18]=178// 2
for i in range(32):
print(chr(flag[i]),end="")
#XYCTF{q7WYGscUuptTYXjnjKoyUTKtG}
Findme
可恶,居然有三个文件
die查看文件信息,发现4是一个可执行文件,IDA启动:
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
size_t v3; // rax
int v5; // eax
FILE *Stream; // [rsp+28h] [rbp+8h]
FILE *v7; // [rsp+48h] [rbp+28h]
int v8; // [rsp+64h] [rbp+44h]
int v9; // [rsp+84h] [rbp+64h]
int v10; // [rsp+A4h] [rbp+84h]
int v11; // [rsp+C4h] [rbp+A4h]
unsigned __int8 v12; // [rsp+104h] [rbp+E4h]
unsigned __int8 v13; // [rsp+144h] [rbp+124h]
int i; // [rsp+164h] [rbp+144h]
j___CheckForDebuggerJustMyCode(&unk_1400230A2, argv, envp);
sub_1400110E1(); //生成512个数
v8 = 0;
v10 = 0;
v11 = 0;
j_memset(FileName, 0, 0x100ui64);
j_memset(byte_14001D7A0, 0, 0x100ui64);
GetCurrentDirectoryA(0x100u, FileName); // 获取当前目录路径
v3 = j_strlen(FileName);
j_memcpy(byte_14001D7A0, FileName, v3);
j_strcat(FileName, "\\Doraemon3"); // 输入文件
j_strcat(byte_14001D7A0, "\\Doraemon1"); // 输出文件
Stream = fopen(FileName, "rb");
if ( Stream )
{
v7 = fopen(byte_14001D7A0, "wb"); // 输出是Doraemon1文件
while ( !feof(Stream) ) // 是否读取到eof
{
v10 = (v10 + 1) % 512;
v11 = (byte_14001D960[v10] + v11) % 512;
byte_14001D201 = byte_14001D960[v10]; // 交换数据
byte_14001D960[v10] = byte_14001D960[v11];
byte_14001D960[v11] = byte_14001D201;
v13 = byte_14001D960[((byte_14001D960[v11] + byte_14001D960[v10]) % 512)];
v12 = v13 ^ fgetc(Stream); // 获取输入
fputc(v12, v7); // 输出
srand(byte_14001D960[v8 % 512]);
v9 = rand() % 4;
for ( i = 0; i < v9; ++i )
{
v5 = rand();
fputc(v5 % 256, v7);
}
++v8;
}
sub_1400111B8("end");
fclose(Stream);
fclose(v7);
system("pause");
return 0;
}
else
{
sub_1400111B8("No Such File\n");
return 0;
}
}
这个的逻辑很简单通过start_1400110E1(); 生成一个512字节的数组
Doraemon3是我们输入的文件,发现下面的运算只有简单的异或运算和伪随机数保护
一眼流密码,魔改RC4
fputc(ch1, FileIO); 就相当于解密出一个字节的数据:
先把s盒提出来:
0xF3, 0x75, 0xC9, 0xB4, 0x2A, 0x3A, 0x9A, 0x90, 0xBE, 0x43, 0x65, 0x33, 0x39, 0xD3,
0xF0, 0x46, 0xA5, 0x32, 0xCE, 0x4B, 0x8A, 0x6C, 0x60, 0xC7, 0x70, 0x55, 0xEF, 0x96,
0xB2, 0x08, 0xC7, 0x68, 0x53, 0x6E, 0xD9, 0x0D, 0xD4, 0x69, 0xCD, 0x87, 0x45, 0x01,
0xE9, 0x93, 0x7B, 0x21, 0x65, 0xDE, 0x8E, 0x24, 0x26, 0xA6, 0xC8, 0x94, 0x7E, 0xFD,
0x4F, 0xFD, 0xAD, 0x2B, 0x51, 0x28, 0x0A, 0x5C, 0xA1, 0x0E, 0x11, 0x45, 0x25, 0x6D,
0x6B, 0x9F, 0x75, 0x5D, 0x3E, 0x20, 0xFA, 0xDC, 0x07, 0xA3, 0x77, 0xC6, 0x8C, 0xEC,
0x8B, 0x3C, 0xCE, 0x2D, 0x18, 0xE3, 0xBA, 0xBD, 0xBC, 0xCA, 0xB7, 0xB4, 0x03, 0x5B,
0xF0, 0x4D, 0x4C, 0xF2, 0x3B, 0x34, 0x42, 0xB3, 0x39, 0x91, 0x67, 0x23, 0x16, 0xEA,
0x88, 0x05, 0x08, 0x19, 0xDA, 0xDF, 0xD0, 0xF5, 0x09, 0x23, 0x59, 0x6D, 0x62, 0x13,
0x85, 0xBD, 0x3D, 0x7E, 0x92, 0xE4, 0x82, 0x06, 0xBB, 0x7B, 0x6A, 0x47, 0xD9, 0xF6,
0x1E, 0x09, 0x58, 0x1A, 0xD8, 0xFE, 0x29, 0x8C, 0xBF, 0x54, 0xAF, 0xAE, 0xA2, 0x8F,
0xD6, 0xE7, 0xBB, 0x24, 0x97, 0x7A, 0xD7, 0x7F, 0xCB, 0x40, 0x3F, 0x49, 0x00, 0xDC,
0xE0, 0x5E, 0xC9, 0xE0, 0x95, 0x4E, 0xC4, 0x90, 0xEB, 0x74, 0x6B, 0xA0, 0x9D, 0xCD,
0xDE, 0xA2, 0x87, 0x1A, 0xD1, 0x12, 0xC8, 0x1B, 0x80, 0xE2, 0x4A, 0x10, 0x60, 0x79,
0x37, 0x29, 0x25, 0xBA, 0xAE, 0x04, 0x1B, 0xDB, 0xD5, 0x48, 0xFE, 0x51, 0x05, 0x83,
0x15, 0x64, 0xC4, 0x76, 0x34, 0xB5, 0xF2, 0xC5, 0x78, 0x6F, 0xC6, 0x10, 0x5F, 0x53,
0x81, 0xFB, 0x8D, 0x40, 0xE6, 0x71, 0xA8, 0x57, 0xB7, 0x99, 0x20, 0x98, 0x56, 0xF4,
0xD8, 0x70, 0xB9, 0xF8, 0xE4, 0xB5, 0x7A, 0xAA, 0xFA, 0x3C, 0x73, 0x77, 0xE8, 0xF9,
0x12, 0x83, 0x2A, 0xB1, 0xC1, 0x9F, 0xF5, 0x5E, 0xF1, 0xF6, 0xD7, 0x89, 0x30, 0x63,
0xF4, 0x68, 0xA9, 0x0B, 0x36, 0x85, 0xF8, 0xB3, 0x95, 0x64, 0x79, 0x56, 0x97, 0x19,
0x5F, 0xA8, 0x6C, 0x4C, 0x52, 0x69, 0xB6, 0x5A, 0x54, 0x63, 0x58, 0x16, 0x86, 0x46,
0xBE, 0x31, 0x1D, 0xCF, 0x42, 0x31, 0x59, 0xEE, 0xEA, 0x0F, 0x28, 0x57, 0x3B, 0x7F,
0xD0, 0xB9, 0x8D, 0xED, 0x44, 0x30, 0xA7, 0xC1, 0x5B, 0x04, 0x33, 0xAC, 0x02, 0x73,
0xDB, 0xFF, 0x01, 0x3D, 0xB1, 0x36, 0x9C, 0xA0, 0x4D, 0x9C, 0x3E, 0x72, 0xF1, 0x1F,
0x88, 0xE5, 0xAD, 0x00, 0x49, 0x0E, 0x3A, 0xE6, 0xD2, 0xE1, 0xE9, 0x44, 0x27, 0x52,
0x99, 0xEC, 0xBC, 0x47, 0xCC, 0xA6, 0x9E, 0xD2, 0x7C, 0xFB, 0x72, 0xDA, 0xA7, 0x9A,
0x86, 0x55, 0x8A, 0x76, 0x9B, 0xF3, 0x7C, 0x8F, 0x14, 0x7D, 0xC5, 0x94, 0x17, 0x8B,
0xAB, 0x15, 0xBF, 0x2E, 0xDD, 0x2C, 0xB0, 0x62, 0x89, 0x71, 0x92, 0x21, 0x9D, 0x0C,
0xEF, 0x9E, 0xD1, 0x2B, 0x06, 0xF7, 0x4F, 0xC3, 0xCF, 0xFF, 0x6E, 0xE5, 0xEB, 0x96,
0xF9, 0xDF, 0xCA, 0x07, 0xD4, 0xA3, 0x84, 0xE3, 0x1F, 0x66, 0x1D, 0x18, 0x35, 0x41,
0x2F, 0x02, 0x66, 0x2E, 0x6F, 0x61, 0xD5, 0x3F, 0x7D, 0x78, 0x1C, 0x32, 0xAB, 0xA4,
0x67, 0xC2, 0xC0, 0x1C, 0x11, 0xE2, 0x2C, 0x38, 0x8E, 0xB2, 0x48, 0xE1, 0x0A, 0x22,
0xD3, 0x41, 0xD6, 0x91, 0x0D, 0x03, 0xFC, 0xFC, 0x38, 0xAC, 0xA9, 0x98, 0xAA, 0x14,
0xCB, 0xCC, 0x4B, 0x81, 0x2D, 0x5C, 0xB8, 0x0F, 0x1E, 0xAF, 0x93, 0xB6, 0x50, 0x50,
0xE7, 0x35, 0x4A, 0xC2, 0xA5, 0x37, 0x43, 0x9B, 0x22, 0x80, 0xC3, 0xDD, 0xED, 0x5A,
0x5D, 0x0C, 0x0B, 0x6A, 0x27, 0x2F, 0x74, 0xEE, 0xF7, 0x26, 0x82, 0x84, 0xB8, 0xE8,
0x61, 0xA4, 0xB0, 0xC0, 0x13, 0x4E, 0xA1, 0x17
还是搓脚本:
import ctypes
import os
#加载 C 库
if os.name == 'nt': # 如果是 Windows 系统
libc = ctypes.CDLL('msvcrt.dll')
else: # 如果是其他系统
libc = ctypes.CDLL('libc.so.6')
#声明 srand 和 rand 函数的签名
libc.srand.argtypes = [ctypes.c_uint]
libc.rand.restype = ctypes.c_int
#定义 Python 封装函数
def set_seed(seed):
libc.srand(seed)
def generate_random():
return libc.rand()
data = [0xF3, 0x75, 0xC9, 0xB4, 0x2A, 0x3A, 0x9A, 0x90, 0xBE, 0x43, 0x65, 0x33,
0x39, 0xD3, 0xF0, 0x46, 0xA5, 0x32, 0xCE, 0x4B, 0x8A, 0x6C, 0x60, 0xC7, 0x70, 0x55,
0xEF, 0x96, 0xB2, 0x08, 0xC7, 0x68, 0x53, 0x6E, 0xD9, 0x0D, 0xD4, 0x69, 0xCD, 0x87,
0x45, 0x01, 0xE9, 0x93, 0x7B, 0x21, 0x65, 0xDE, 0x8E, 0x24, 0x26, 0xA6, 0xC8, 0x94,
0x7E, 0xFD, 0x4F, 0xFD, 0xAD, 0x2B, 0x51, 0x28, 0x0A, 0x5C, 0xA1, 0x0E, 0x11, 0x45,
0x25, 0x6D, 0x6B, 0x9F, 0x75, 0x5D, 0x3E, 0x20, 0xFA, 0xDC, 0x07, 0xA3, 0x77, 0xC6,
0x8C, 0xEC, 0x8B, 0x3C, 0xCE, 0x2D, 0x18, 0xE3, 0xBA, 0xBD, 0xBC, 0xCA, 0xB7, 0xB4,
0x03, 0x5B, 0xF0, 0x4D, 0x4C, 0xF2, 0x3B, 0x34, 0x42, 0xB3, 0x39, 0x91, 0x67, 0x23,
0x16, 0xEA, 0x88, 0x05, 0x08, 0x19, 0xDA, 0xDF, 0xD0, 0xF5, 0x09, 0x23, 0x59, 0x6D,
0x62, 0x13, 0x85, 0xBD, 0x3D, 0x7E, 0x92, 0xE4, 0x82, 0x06, 0xBB, 0x7B, 0x6A, 0x47,
0xD9, 0xF6, 0x1E, 0x09, 0x58, 0x1A, 0xD8, 0xFE, 0x29, 0x8C, 0xBF, 0x54, 0xAF, 0xAE,
0xA2, 0x8F, 0xD6, 0xE7, 0xBB, 0x24, 0x97, 0x7A, 0xD7, 0x7F, 0xCB, 0x40, 0x3F, 0x49,
0x00, 0xDC, 0xE0, 0x5E, 0xC9, 0xE0, 0x95, 0x4E, 0xC4, 0x90, 0xEB, 0x74, 0x6B, 0xA0,
0x9D, 0xCD, 0xDE, 0xA2, 0x87, 0x1A, 0xD1, 0x12, 0xC8, 0x1B, 0x80, 0xE2, 0x4A, 0x10,
0x60, 0x79, 0x37, 0x29, 0x25, 0xBA, 0xAE, 0x04, 0x1B, 0xDB, 0xD5, 0x48, 0xFE, 0x51,
0x05, 0x83, 0x15, 0x64, 0xC4, 0x76, 0x34, 0xB5, 0xF2, 0xC5, 0x78, 0x6F, 0xC6, 0x10,
0x5F, 0x53, 0x81, 0xFB, 0x8D, 0x40, 0xE6, 0x71, 0xA8, 0x57, 0xB7, 0x99, 0x20, 0x98,
0x56, 0xF4, 0xD8, 0x70, 0xB9, 0xF8, 0xE4, 0xB5, 0x7A, 0xAA, 0xFA, 0x3C, 0x73, 0x77,
0xE8, 0xF9, 0x12, 0x83, 0x2A, 0xB1, 0xC1, 0x9F, 0xF5, 0x5E, 0xF1, 0xF6, 0xD7, 0x89,
0x30, 0x63, 0xF4, 0x68, 0xA9, 0x0B, 0x36, 0x85, 0xF8, 0xB3, 0x95, 0x64, 0x79, 0x56,
0x97, 0x19, 0x5F, 0xA8, 0x6C, 0x4C, 0x52, 0x69, 0xB6, 0x5A, 0x54, 0x63, 0x58, 0x16,
0x86, 0x46, 0xBE, 0x31, 0x1D, 0xCF, 0x42, 0x31, 0x59, 0xEE, 0xEA, 0x0F, 0x28, 0x57,
0x3B, 0x7F, 0xD0, 0xB9, 0x8D, 0xED, 0x44, 0x30, 0xA7, 0xC1, 0x5B, 0x04, 0x33, 0xAC,
0x02, 0x73, 0xDB, 0xFF, 0x01, 0x3D, 0xB1, 0x36, 0x9C, 0xA0, 0x4D, 0x9C, 0x3E, 0x72,
0xF1, 0x1F, 0x88, 0xE5, 0xAD, 0x00, 0x49, 0x0E, 0x3A, 0xE6, 0xD2, 0xE1, 0xE9, 0x44,
0x27, 0x52, 0x99, 0xEC, 0xBC, 0x47, 0xCC, 0xA6, 0x9E, 0xD2, 0x7C, 0xFB, 0x72, 0xDA,
0xA7, 0x9A, 0x86, 0x55, 0x8A, 0x76, 0x9B, 0xF3, 0x7C, 0x8F, 0x14, 0x7D, 0xC5, 0x94,
0x17, 0x8B, 0xAB, 0x15, 0xBF, 0x2E, 0xDD, 0x2C, 0xB0, 0x62, 0x89, 0x71, 0x92, 0x21,
0x9D, 0x0C, 0xEF, 0x9E, 0xD1, 0x2B, 0x06, 0xF7, 0x4F, 0xC3, 0xCF, 0xFF, 0x6E, 0xE5,
0xEB, 0x96, 0xF9, 0xDF, 0xCA, 0x07, 0xD4, 0xA3, 0x84, 0xE3, 0x1F, 0x66, 0x1D, 0x18,
0x35, 0x41, 0x2F, 0x02, 0x66, 0x2E, 0x6F, 0x61, 0xD5, 0x3F, 0x7D, 0x78, 0x1C, 0x32,
0xAB, 0xA4, 0x67, 0xC2, 0xC0, 0x1C, 0x11, 0xE2, 0x2C, 0x38, 0x8E, 0xB2, 0x48, 0xE1,
0x0A, 0x22, 0xD3, 0x41, 0xD6, 0x91, 0x0D, 0x03, 0xFC, 0xFC, 0x38, 0xAC, 0xA9, 0x98,
0xAA, 0x14, 0xCB, 0xCC, 0x4B, 0x81, 0x2D, 0x5C, 0xB8, 0x0F, 0x1E, 0xAF, 0x93, 0xB6,
0x50, 0x50, 0xE7, 0x35, 0x4A, 0xC2, 0xA5, 0x37, 0x43, 0x9B, 0x22, 0x80, 0xC3, 0xDD,
0xED, 0x5A, 0x5D, 0x0C, 0x0B, 0x6A, 0x27, 0x2F, 0x74, 0xEE, 0xF7, 0x26, 0x82, 0x84,
0xB8, 0xE8, 0x61, 0xA4, 0xB0, 0xC0, 0x13, 0x4E, 0xA1, 0x17]
Doraemon1 = []
#打开文件并读取字节
with open("Doraemon1", "rb") as file:
#读取文件字节并赋值给temp
Doraemon1 = list(file.read())
outfile = []
idx = 0
idx_1 = 0
idx_2 = 0
Doraemon1idx = 0
while Doraemon1idx<len(Doraemon1) :
idx_1 = (idx_1 + 1) % 512
idx_2 = (data[idx_1] + idx_2) % 512
temp = data[idx_1]
data[idx_1] = data[idx_2]
data[idx_2] = temp
data_ch = data[((data[idx_2] + data[idx_1]) % 512) & 0xff]
Stream = data_ch ^ Doraemon1[Doraemon1idx]
Doraemon1idx += 1
outfile.append(Stream.to_bytes(1, 'big'))
set_seed(data[idx % 512])
randnum = generate_random() % 4
for i in range(randnum):
Doraemon1idx += 1
idx += 1
print(outfile)
#打开文件进行写入
with open("output_file.exe", "wb") as file:
#逐字节写入文件
for byte in outfile:
file.write(byte) #将每个字节转换为字节数组并写入文件
跑出exe
继续IDA:
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
__int64 i; // rcx
size_t v5; // rax
char v7; // [rsp+20h] [rbp+0h] BYREF
char Str[114552]; // [rsp+30h] [rbp+10h] BYREF
FILE *Stream; // [rsp+1BFA8h] [rbp+1BF88h]
FILE *v10; // [rsp+1BFC8h] [rbp+1BFA8h]
int v11; // [rsp+1BFE4h] [rbp+1BFC4h]
unsigned __int8 v12; // [rsp+1C024h] [rbp+1C004h]
unsigned __int8 v13; // [rsp+1C044h] [rbp+1C024h]
int v14; // [rsp+1C654h] [rbp+1C634h]
__int64 v15; // [rsp+1C658h] [rbp+1C638h]
size_t v16; // [rsp+1C660h] [rbp+1C640h]
v3 = &v7;
for ( i = 28694i64; i; --i )
{
*(_DWORD *)v3 = -858993460;
v3 += 4;
}
j___CheckForDebuggerJustMyCode(&unk_1400230A3, argv, envp);
Stream = 0i64;
v11 = 0;
j_memset(FileName, 0, 0x100ui64);
j_memset(byte_14001D920, 0, sizeof(byte_14001D920));
GetCurrentDirectoryA(0x100u, FileName);
v5 = j_strlen(FileName);
j_memcpy(byte_14001D920, FileName, v5);
j_strcat(FileName, "\\Doraemon2");
j_strcat(byte_14001D920, "\\Here");
sub_1400111BD("你是来找谁的呀\n");
sub_1400110A5("%s", Str);
sub_1400111BD(&unk_14001AD68);
Stream = fopen(FileName, "rb");
v10 = fopen(byte_14001D920, "wb");
while ( !feof(Stream) )
{
v12 = fgetc(Stream);
v14 = v12;
v15 = v11;
v16 = j_strlen(Str);
v13 = Str[v11 % v16] ^ v12 ^ 0x14;
fputc(v13, v10);
++v11;
}
fclose(Stream);
fclose(v10);
system("pause");
return 0;
}
这个就很简单了根据提示我们就可以知道要找的是哆啦A梦,Doraemon
我们运行程序就可以获得gif动图flag 了
再改一下后缀
好耶!