re随笔8
polarctf逆向easyre1
直接放进ida主函数中找到


可以发现最后的flag是个加密过的内容,在主函数中存在一个enkey函数,猜测把输入的flag进行加密操作,然后和最终的密文进行对比,所以进行分析enkey函数
`int enkey()
{
int i; // [esp+Ch] [ebp-4h]
for ( i = 0; i <= 31; ++i )
*(_BYTE *)(i + 134520992) ^= *(_BYTE *)(i + 134520896);
return 0;
这里面是个异或的操作,*(_BYTE *)的意思是取后面地址的东西,并且BYTE是unsigned char,main程序主要逻辑是用户输入,然后把用户输入的内容复制到这个地址中,然后进行下面异或和reduce函数的操作,最后和密文进行对比.
接下来到134520896这个地址看看异或的key是什么
首先进行地址转换134520896 = 0x0804A000
按住G然后跳转到此地址,因为有空字符占位的原因,所以需要往下滑,最后找到key

简单写个脚本
`a='dXSAozQPUWOBU[VQOATZSE@AZZVOF'
key="5055045045055045055045055045055"
flag=''
for i in range(31):
flag+=chr((ord(a[i])+1)^ord(key[i]))
print(flag)得到flag  逆向babyRE main后发现很乱  扔给ai大概用等价c语言写一下int main(int argc, const char **argv, const char **envp)
{
_main(); // 一些初始化 / 预处理
endoce(); // 对 flag 做编码 / 解码 / 混淆
std::string input;
std::cin >> input;
// flag 是一个全局 std::string 对象:
// std::string flag;
if (input == flag) {
std::cout << "Ok" << std::endl;
} else {
std::cout << "Err" << std::endl;
}
return 0;
}
看的出来就是输入和flag进行对比,跟进endoce函数__int64 endoce(void)
{
__int64 result; // rax
__int64 v1; // [rsp+20h] [rbp-20h] BYREF
__int64 v2; // [rsp+28h] [rbp-18h] BYREF
_BYTE *v3; // [rsp+30h] [rbp-10h]
void *v4; // [rsp+38h] [rbp-8h]
v4 = &flag[abi:cxx11];
v2 = std::string::begin(&flag[abi:cxx11]);
v1 = std::string::end(&flag[abi:cxx11]);
while ( 1 )
{
result = __gnu_cxx::operator!=<char *,std::string>(&v2, &v1);
if ( !(_BYTE)result )
break;
v3 = (_BYTE *)__gnu_cxx::__normal_iterator<char ,std::string>::operator(&v2);
*v3 += 2;
__gnu_cxx::__normal_iterator<char *,std::string>::operator++(&v2);
}
return result;
}这段代码也比较抽象,其实意思就是给输入的每个字符+2,很简单可以得到flag的逻辑 然后点击flag,按x跟进交叉引用,有四个地方,最终找到flag的初始值  写一个简单脚本出flaga="asdfgcvbnmjgtlop"
flag=""
for i in range(len(a)):
flag+=(chr(ord(a[i])+2))
print("flag{"+flag+'}')得到flag  逆向c^ 进入主函数// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-Ah] [ebp-60h]
int v5; // [esp-6h] [ebp-5Ch]
_BYTE v6[48]; // [esp-2h] [ebp-58h] BYREF
int v7; // [esp+2Eh] [ebp-28h]
size_t v8; // [esp+32h] [ebp-24h]
int v9; // [esp+36h] [ebp-20h]
int v10; // [esp+3Ah] [ebp-1Ch]
int *p_argc; // [esp+4Ah] [ebp-Ch]
p_argc = &argc;
init();
*(_DWORD *)&v6[2] = 0;
v7 = 0;
memset(&v6[4], 0, 4 * (((&v6[2] - &v6[4] + 50) & 0xFFFFFFFC) >> 2));
v10 = 0;
v9 = 0;
puts("Please enter flag\n");
((void (__stdcall *)(const char *, _BYTE *, int, int, _DWORD))__isoc99_scanf)("%s", &v6[2], v4, v5, *(_DWORD *)v6);
v8 = strlen(&v6[2]);
fun1(&v6[2], v8);
if ( check(&v6[2], v8) )
puts("RIGHT");
else
printf("Try again");
return 0;
} 基本有两步fun1和check 分别跟进 fun1int __cdecl fun1(int a1, int a2)
{
int i; // [esp+Ch] [ebp-4h]
for ( i = 0; i < a2; ++i )
*(_BYTE *)(i + a1) ^= 1u;
return 0;
} 这是一个异或操作,这里的u是无符号整型, checkint __cdecl check(int a1, int a2)
{
char s[10]; // [esp+8h] [ebp-20h] BYREF
__int16 v4; // [esp+12h] [ebp-16h]
int v5; // [esp+14h] [ebp-14h]
int v6; // [esp+18h] [ebp-10h]
int i; // [esp+1Ch] [ebp-Ch]
strcpy(s, "shfiu777");
s[9] = 0;
v4 = 0;
v5 = 0;
v6 = 0;
if ( strlen(s) != a2 )
return 0;
for ( i = 0; i < a2; ++i )
{
if ( *(_BYTE *)(i + a1) != s[i] )
return 0;
}
return 1;
}所以写一个脚本让s[i]异或1得到的就是答案a="shfiu777"
flag=""
for i in range(len(a)):
flag+=(chr(ord(a[i])^1))
print("flag{"+flag+'}')输出为right666,md5后提交 一个flag劈三瓣儿 进去之后直接看见flag函数,点进去直接是三段flag,直接拼接提交  EasyCPP2int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rax
int v5; // ebx
_BYTE v7[40]; // [rsp+0h] [rbp-30h] BYREF
encode();
std::string::basic_string(v7, argv);
std::operator>>
if ( (unsigned __int8)std::operator==
{
v3 = std::ostream::operator<<(&_bss_start, &std::endl<char,std::char_traits
v4 = std::operator<<<std::char_traits
std::ostream::operator<<(v4, &std::endl<char,std::char_traits
v5 = 0;
}
else
{
v5 = 1;
}
std::string::~string(v7);
return v5;
}跟进encode__int64 encode(void)
{
__int64 result; // rax
int i; // [rsp+0h] [rbp-4h]
for ( i = 0; i <= 15; ++i )
{
flag[i] += 3;
result = i;
flag[i] ^= 1u;
}
return result;
}
这里是写了flag的加密步骤,找到flag的初始值,写一个简单脚本得到flaga="qisngksofhuivvmg"
flag=""
for i in range(len(a)):
flag+=(chr((ord(a[i])+3)^1))
print("flag{"+flag+'}') 逆向crc 进入主函数int __fastcall main(int argc, const char **argv, const char **envp)
{
const char *v3; // rax
const char *v4; // rax
const char *v5; // rax
const char *v6; // rax
const char *v7; // rax
const char *v8; // rax
char v10[16]; // [rsp+0h] [rbp-30h] BYREF
char v11[8]; // [rsp+10h] [rbp-20h] BYREF
__int64 v12; // [rsp+18h] [rbp-18h]
int v13; // [rsp+20h] [rbp-10h]
unsigned __int64 v14; // [rsp+28h] [rbp-8h]
v14 = __readfsqword(0x28u);
*(_QWORD *)v11 = 0LL;
v12 = 0LL;
v13 = 0;
scanf("%s", v11);
strmncpy(v11, 0, 4, v10);
v3 = (const char *)magic(v10);
if ( !strcmp(v3, "d1f4eb9a") )
{
v4 = (const char *)magic(v10);
if ( !strcmp(v4, "15d54739") )
{
strmncpy(v11, 5, 4, v10);
v5 = (const char *)magic(v10);
if ( !strcmp(v5, "540bbb08") )
{
strmncpy(v11, 9, 2, v10);
v6 = (const char *)magic(v10);
if ( !strcmp(v6, "3fcbd242") )
{
strmncpy(v11, 11, 4, v10);
v7 = (const char *)magic(v10);
if ( !strcmp(v7, "2479c623") )
{
strmncpy(v11, 15, 1, v10);
v8 = (const char *)magic(v10);
if ( !strcmp(v8, "fcb6e20c") )
printf("Very nice!");
}
}
}
}
}
return 0;
}magic跟进后发现是crc32,这些代码的意思是选取输入字符串的特定字段进行加密,得到结果和密文进行比较,通过进行下一步 例如strmncpy(v11, 4, 1, v10);的意思是从v11中取从第四个字符开始的一个长度的字符, 让ai给出一个脚本来爆破import binascii
import itertools
import string
可以根据题目实际情况缩小 / 放大字符集(越小越快)
CHARSET = string.ascii_letters + string.digits + "_{}@!-$"
这里假设 magic 使用的是标准 zlib CRC32,init=0,无最终异或
def crc32_u32(data: bytes) -> int:
return binascii.crc32(data) & 0xFFFFFFFF
def brute_segment(length: int, target_crc: int, charset=CHARSET, stop_first=True):
"""
暴力破解长度为 length 的字符串,使得 CRC32 等于 target_crc
stop_first=True 时,找到第一个就返回;否则会继续找所有满足的结果
"""
print(f"[+] Brute length={length}, target_crc=0x{target_crc:08x}")
for tup in itertools.product(charset, repeat=length):
s = ''.join(tup)
c = crc32_u32(s.encode())
if c == target_crc:
print(f" [*] hit: {s!r}")
if stop_first:
return [s]
print(" [-] no result found (with current charset / crc setting)")
return []
def main():
# 按照 main 里的分段顺序:长度 + 对应 magic 返回的 hex 值
segments = [
(4, 0xD1F4EB9A), # v11[0..3]
(1, 0x15D54739), # v11[4]
(4, 0x540BBB08), # v11[5..8]
(2, 0x3FCBD242), # v11[9..10]
(4, 0x2479C623), # v11[11..14]
(1, 0xFCB6E20C), # v11[15]
]
results = []
for idx, (length, crc) in enumerate(segments):
res_list = brute_segment(length, crc)
if not res_list:
print(f"[!] segment {idx} failed,后面 flag 无法拼完整了。")
return
# 默认只拿第一个
results.append(res_list[0])
flag = ''.join(results)
print("\n==============================")
print(f"[+] Recover flag (16 bytes) : {flag}")
print("==============================")
if name == "main":
main()
得到flag  box box.txt提示获得三个key拼接就是flag 主函数也是开门见山直接给出三个key函数,挨个跟进分析 key1__int64 key1()
{
int k; // [rsp+0h] [rbp-Ch]
int j; // [rsp+4h] [rbp-8h]
int i; // [rsp+8h] [rbp-4h]
for ( i = 1; i <= 21; ++i )
{
for ( j = 1; j <= 1; ++j )
{
c += i;
d += c;
f = d + c - 1;
}
}
for ( k = 33; k > 0; --k )
{
c = f * d / 10;
key += f * d / 10;
}
return 0LL;
} 这里面出现了几个全局变量,c,d,f,key,双击每个之后发现是初始值为0 其实这里相当于给了初始代码,稍加修改直接运行可以得到key1#include <stdio.h>
int main() {
int i, j, k;
long long c = 0, d = 0, f = 0, key = 0;
for (i = 1; i <= 21; ++i) {
for (j = 1; j <= 1; ++j) {
c += i;
d += c;
f = d + c - 1;
}
}
for (k = 33; k > 0; --k) {
c = f * d / 10;
key += f * d / 10;
}
printf("c = %lld\n", c);
printf("d = %lld\n", d);
printf("f = %lld\n", f);
printf("key = %lld\n", key);
return 0;
}
 key1 = 11694441 key2__int64 key2()
{
printf(format);
fgets(str2, 100, stdin);
str1[strcspn(str1, "\n")] = 0;
str2[strcspn(str2, "\n")] = 0;
if ( !strcmp(str1, str2) )
puts("wow,you find the key2!!!");
else
puts("try once again!!!");
return 0LL;
}这里从stdin读取一段字符串str2和下面的str1对比,说明要输入的str2就是str1,直接跟进 key2=that_ok key3__int64 key3()
{
puts("wow,,you're not far fro success~");
puts("NNSXS===");
return 0LL;
}base32解码后得到 key3=key 所以flag{md5(11694441that_okkey)} HowTo_Login 运行一下  查壳发现有壳,脱壳后扔进ida 打卡主函数  跟进DialogFuncINT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
HMODULE ModuleHandleW; // eax
HICON IconW; // eax
HMODULE v7; // eax
HWND DlgItem; // eax
HCURSOR CursorW; // [esp-4h] [ebp-34Ch]
CHAR String[256]; // [esp+8h] [ebp-340h] BYREF
CHAR v11[256]; // [esp+108h] [ebp-240h] BYREF
CHAR Text[256]; // [esp+208h] [ebp-140h] BYREF
char Source[36]; // [esp+308h] [ebp-40h] BYREF
char v14[24]; // [esp+32Ch] [ebp-1Ch] BYREF
if ( a2 == 16 )
{
EndDialog(hDlg, 0);
return 0;
}
if ( a2 == 272 )
{
ModuleHandleW = GetModuleHandleW(0);
IconW = LoadIconW(ModuleHandleW, (LPCWSTR)0x67);
SetClassLongA(hDlg, -14, (LONG)IconW);
v7 = GetModuleHandleW(0);
CursorW = LoadCursorW(v7, (LPCWSTR)0x66);
DlgItem = GetDlgItem(hDlg, 1);
SetClassLongA(DlgItem, -12, (LONG)CursorW);
return 1;
}
if ( a2 != 273 || (unsigned __int16)a3 != 1 )
return 0;
memset(String, (unsigned __int16)a3 - 1, sizeof(String));
memset(v11, 0, sizeof(v11));
memset(Text, 0, sizeof(Text));
GetDlgItemTextA(hDlg, 1001, String, 256);
GetDlgItemTextA(hDlg, 1002, v11, 256);
if ( strstr(String, "@") && strstr(String, ".") && strstr(String, ".")[1] && strstr(String, "@")[1] != 46 )
{
strcpy(v14, "Registration failure.");
strcpy(Source, "Registration Success!\nYour pawd is:");
if ( strlen(v11) == 16
&& v11[0] == 67
&& v11[15] == 88
&& v11[1] == 90
&& v11[14] == 65
&& v11[2] == 57
&& v11[13] == 98
&& v11[3] == 100
&& v11[12] == 55
&& v11[4] == 109
&& v11[11] == 71
&& v11[5] == 113
&& v11[10] == 57
&& v11[6] == 52
&& v11[9] == 103
&& v11[7] == 99
&& v11[8] == 56 )
{
strcpy_s(Text, 0x100u, Source);
strcat_s(Text, 0x100u, v11);
}
else
{
strcpy_s(Text, 0x100u, v14);
}
}
else
{
strcpy_s(Text, 0x100u, "Your E-mail address in not valid.");
}
MessageBoxA(hDlg, Text, "Registeration", 0x40u);
return 1;
}`
提示是这样说的:最终密码要进行32位md5加密哦
GetDlgItemTextA(hDlg, 1002, v11, 256);这行的意思是从GUI输入框读取ID=1002的文本,写入v11不超过256字节
所以最后的密码就是v11数组
即
所以flag就是flag{MD5(CZ9dmq4c8g9G7bAX)}
浙公网安备 33010602011771号