BUUCTF刷题记录[Reverse]
SimpleRev
64位无壳程序,重点函数为Decry函数。
0x01 小端序存储
*(_QWORD *)src = 0x534C43444ELL;
v9 = 0x776F646168LL;
IDA直接转字符串的结果是
*(_QWORD *)src = 'SLCDN';
v9 = 'wodah';
Intel的CPU是小端序存储,因此程序载入内存后,字符串顺序应该反过来,也就是
*(_QWORD *)src = 'NDCLS';
v9 = 'hadow';
0x02 大写字母转小写字母
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + 32;
++v3;
}
大写字母和小写字母的ASCII码相差32,所以该段程序是将key中的大写字母转成小写字母。
0x03 混淆算法
while ( 1 )
{
v1 = getchar();
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= 96 || v1 > 122 )
{
if ( v1 > 64 && v1 <= 90 )
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
else
{
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}
if ( !strcmp(text, str2) )
puts("Congratulation!\n");
else
puts("Try again!\n");
如果对ASCII码熟悉的话,很容易可以看出几个判断条件的目的是确保输入的v1是26的英文字母之一,大小写皆可,因此遍历26个英文字母,依次输出符合条件的字符即可。
0x04 EXP
src = "ADSFKNDCLS"
v3 = 0
v5 = len(src)
key = ""
for i in range(v5):
if (src[i] > "@" and src[i] <= "Z"):
key += (chr(ord(src[i]) + 32))
else:
key + (src[i])
#print("%s" %key)
text = "killshadow"
flag = ""
while (v3 < 10):
for i in range(65, 91):
if (ord(text[v3]) == ((i - 39 - ord(key[v3]) + 97) % 26 + 97)):
flag += chr(i)
v3 += 1
break
else:
if (i == 90):
for j in range(97, 122):
if (ord(text[v3]) == ((j - 39 - ord(key[v3]) + 97) % 26 + 97)):
flag += chr(j)
v3 += 1
break
print("flag{%s}" %flag)
flag{KLDQCUDFZO}
0x05 反思
- 大小端存储知识点不熟悉,变量赋值和载入内存时的字符串顺序转换不够熟悉。
- ASCII码不熟悉,不清楚函数的意图。
luck_guy
EXP
f1 = "GXY{do_not_"
s = [0x7F, 0x66, 0x6F, 0x60, 0x67, 0x75, 0x63, 0x69][::-1]
f2 = ""
for i in range(8):
if (i % 2 == 1):
f2 += chr(int(s[i]) - 2)
else:
f2 += chr(int(s[i]) - 1)
flag = f1 + f2
print("%s" %flag)
print("flag%s" %flag[3:])
flag{do_not_hate_me}
[BJDCTF2020]JustRE
比较直接,需要用户点击19999次弹出的框后,打印flag。
INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
CHAR String; // [esp+0h] [ebp-64h]
if ( a2 != 272 )
{
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
{
sprintf(&String, Format, ++dword_4099F0);
if ( dword_4099F0 == 19999 )
{
sprintf(&String, aBjdDD2069a4579, 19999, 0);// 打印flag
SetWindowTextA(hWnd, &String);
return 0;
}
SetWindowTextA(hWnd, &String);
return 0;
}
EndDialog(hWnd, (unsigned __int16)a3);
}
return 1;
}
直接使用ipython将19999和0填入进去即可。
flag{1999902069a45792d233ac}
刮开有奖
0x01 总体分析
32位无壳,main函数比较简单,它的参数DialogFunc比较有问题。
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
DialogBoxParamA(hInstance, (LPCSTR)0x67, 0, DialogFunc, 0);
return 0;
}
DialogBoxParamA函数(winuser.h)
从对话框模板资源创建模式对话框。 在显示对话框之前,函数会将应用程序定义的值作为WM_INITDIALOG消息的 lParam 参数传递给对话框过程。 应用程序可以使用此值初始化对话框控件。
https://learn.microsoft.com/zh-CN/windows/win32/api/winuser/nf-winuser-dialogboxparama
DialogFunc函数中重点代码如下:
if ( (_WORD)a3 == 1001 )
{
memset(&String, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, &String, 0xFFFF);
if ( strlen(&String) == 8 )
{
v7 = 'Z';
v8 = 'J';
v9 = 'S';
v10 = 'E';
v11 = 'C';
v12 = 'a';
v13 = 'N';
v14 = 'H';
v15 = '3';
v16 = 'n';
v17 = 'g';
sub_4010F0((int)&v7, 0, 10);
memset(&v26, 0, 0xFFFFu);
v26 = v23;
v28 = v25;
v27 = v24;
v4 = sub_401000((int)&v26, strlen(&v26));
memset(&v26, 0, 0xFFFFu);
v27 = v21;
v26 = v20;
v28 = v22;
v5 = sub_401