upx2023
原创2024年4月7日大约 5 分钟
upx2023
参考文章:i春秋2023冬季赛 RE upx2023 WP-CSDN博客
int __fastcall main(int argc, const char **argv, const char **envp)
{
std::ostream *v3; // rax
char *v4; // rax
int v6[44]; // [rsp+20h] [rbp-60h] BYREF
char v7[16]; // [rsp+D0h] [rbp+50h] BYREF
char v8[16]; // [rsp+E0h] [rbp+60h] BYREF
char v9[20]; // [rsp+F0h] [rbp+70h] BYREF
int v10; // [rsp+104h] [rbp+84h]
unsigned int Seed; // [rsp+108h] [rbp+88h]
int i; // [rsp+10Ch] [rbp+8Ch]
_main();
Seed = time(0i64); // 利用时间戳弄种子
srand(Seed); // 利用种子产生随机数
std::string::string((std::string *)v7);
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, Str);
std::operator>><char>((std::istream *)&std::cin, (std::string *)v7);// v7是输入的Flag
std::string::string((std::string *)v9, (const std::string *)v7);
change(v8, v9);
std::string::operator=(v7, v8); // 将v8指向的字符串内容赋值给v7指向的字符串对象。
std::string::~string((std::string *)v8);
std::string::~string((std::string *)v9);
if ( std::string::length((std::string *)v7) != 42 )// flag是42的长度
{
v3 = (std::ostream *)std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "len error");
std::endl<char,std::char_traits<char>>(v3);
exit(0);
}
qmemcpy(v6, &unk_46A020, 0xA8ui64);
for ( i = 0; i <= 41; ++i )
{
v10 = rand() % 255;
v4 = (char *)std::string::operator[](v7, i);// 它返回v7指向的字符串中索引为i的字符。相当于v7[i]
if ( (v10 ^ *v4) != v6[i] ) // v6是知道的,然后v4就是v7。可以得到v10,v10又是时间戳种子得来的,所以就是要对比
exit(0);
}
std::string::~string((std::string *)v7); // 销毁v7指向的字符串对象。
return 0;
}
std::string::string((std::string *)v9, (const std::string *)v7);
这行代码是C++中的一个构造函数调用,它的作用是创建一个新的std::string
对象v9
,并用另一个已存在的std::string
对象v7
的内容来初始化它。简单来说,这是一个复制构造函数,它会生成一个与v7
内容相同的新字符串v9
。
change(v8, v9);
把v9变化了,输出v8然后我们找出变化后的v8就好了。
比如输入的
flag{0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ}
然后动调获得变化后的flag。
下俩个operator函数之前的断点。
输入的:flag{0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ}
输出的:f{37BFJNRVZlg02468ACEGIKMOQSUWY}a159DHLPTX
然后找时间。弄出时间戳。
根据创建时间得到时间戳:1685759157
647aa4b5
0x09, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0xD9, 0x00,
0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0xDD, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x4C, 0x00,
0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0x65, 0x00,
0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0xED, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0x0B, 0x00,
0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00,
0xE5, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5D, 0x00,
0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
0x41, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x52, 0x00,
0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFA, 0x00,
0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00,
0x89, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x7E, 0x00,
0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00,
0x13
思路就是以时间戳产生的随机数跟变化后输入的flag异或后对比上面这一串数据是否相等。
根据我们输入和输出的作比较,可以看出就是变换了一下顺序,并没有改变值。
所以不变的肯定是flag{ }。
我们把变化后的flag(就是change函数后的flag看一眼)
f{37BFJNRVZlg02468ACEGIKMOQSUWY}a159DHLPTX
f在第一位 ,**{**在第二位 l在12位 g在第十三位 **}**在33位。
f^0x09=种子的第一位。以此类推写代码。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int seed, i, key;
for (seed = 0x647aa4b5; seed >0; seed++)
{
srand(seed);
int right = 1;
for (i = 0; i <= 32; i++)
{
key = rand() % 255;
switch (i)
{
case 0:
if (key != ('f'^0x09)) //102^9=111(0x6F)
{
right = 0;
}
break;
case 1:
if (key != ('{'^0x63)) //123^99=24(0x18)
{
right = 0;
}
break;
case 11:
if (key != ('l'^0xC6)) //108^198=170(0xAA)
{
right = 0;
}
break;
case 12:
if (key != ('g'^0x65)) //103^101=2(0x02)
{
right = 0;
}
break;
case 32:
if (key != ('a'^0xFA)) //97^250=155(0x9B)
{
right = 0;
}
break;
default:
break;
}
if (right == 0)
{
break;
}
}
if (right == 1)
{
printf("%u\n", seed);
break;
}
}
return 0;
}
发现出不来 那我们就减减
for (seed = 0x647aa4b5; seed >0; seed--)
就出来了
1682145110
种子出来后
写代码异或出结果
#include<stdio.h>
#include<stdlib.h>
int main()
{
int seed, i, key;
for (seed = 0x647aa4b5; seed >0; seed--)
{
srand(seed);
int right = 1;
for (i = 0; i <= 32; i++)
{
key = rand() % 255;
switch (i)
{
case 0:
if (key != ('f'^0x09)) //102^9=111(0x6F)
{
right = 0;
}
break;
case 1:
if (key != ('{'^0x63)) //123^99=24(0x18)
{
right = 0;
}
break;
case 11:
if (key != ('l'^0xC6)) //108^198=170(0xAA)
{
right = 0;
}
break;
case 12:
if (key != ('g'^0x65)) //103^101=2(0x02)
{
right = 0;
}
break;
case 32:
if (key != ('a'^0xFA)) //97^250=155(0x9B)
{
right = 0;
}
break;
default:
break;
}
if (right == 0)
{
break;
}
}
if (right == 1)
{
printf("%u\n", seed);
break;
}
}
srand(seed);
int v10;
int a[161]={
0x09, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0xD9, 0x00,
0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0xDD, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x4C, 0x00,
0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x98, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0x65, 0x00,
0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0xED, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0x0B, 0x00,
0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00,
0xE5, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5D, 0x00,
0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
0x41, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x52, 0x00,
0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFA, 0x00,
0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00,
0x89, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x7E, 0x00,
0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00,
0x13
};
for (int i = 0; i <= 40; i++) {
v10 = rand()%255;
int b = a[4*i] ^ v10;
char c = (char)b; // 将整数转换为字符
printf("%c", c);
}
}
然后再调换顺序调换回去;
f{52bgb-281lg00ff-46f7-ca009c8e}a381-b719
发现少一位,
一看下面有个8看漏了,看的像0。
补上就好了。
这回就对了
f{52bgb-281lg00ff-46f7-ca009c8e}a381-b7191
然后调换顺序,
最后始终不知道怎么调换回去,就去问了gpt。
好吧gpt啥也问不出来。
只能看参考文章的代码了
#upx2023 wp
example="flag{0123456789zbcdexQhijkymnABCDEFGHIJKL}"
back="f{37bxjnDHLlg02468zceQikmACEGIK}a159dhyBFJ"
crypto="f{52bgb-281lg00ff-46f7-ca009c8e}a381-b7191"
for i in example:
print(crypto[back.index(i)],end="")
#flag{0305f8f2-14b6-fg7b-bc7a-010299c881e1}
总结
- 解题代码不能全靠gpt,稍微复杂一点就问不出来。(最后写代码问了得有半小时)
- 有时候变化可以不用静态分析,直接给个数据,动调拿变化后的数据。写变化代码。
- 有时候条件不足,得到部分数据就可以往爆破的方向解题。