虽然闲着但是不想让自己闲着, 先刷完BUUOJ的第一页逆向吧
[BJDCTF2020]JustRE
链接: https://buuoj.cn/challenges#[BJDCTF2020]JustRE
EXE 32
IDA中 DialogFunc 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 BOOL __stdcall DialogFunc (HWND hWnd, UINT a2, WPARAM a3, LPARAM a4) { CHAR String; if ( a2 != 272 ) { if ( a2 != 273 ) return 0 ; if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 ) { sprintf (&String, aD, ++dword_4099F0); if ( dword_4099F0 == 19999 ) { sprintf (&String, aBjdDD2069a4579, 19999 , 0 ); SetWindowTextA(hWnd, &String); return 0 ; } SetWindowTextA(hWnd, &String); return 0 ; } EndDialog(hWnd, (unsigned __int16)a3); } return 1 ; }
aD = "您已经点了 %d 次"
dword_4099F0 = 0
aBjdDD2069a4579 = "BJD{%d%d2069a45792d233ac}"
当计数到达19999之后得到flag, 用19999
和0
分别替换两个%d
即为flag
flag{1999902069a45792d233ac}
rsa
链接: https://buuoj.cn/challenges#rsa
这题放错分类了吧…
使用openssl从公钥文件中提取e, n
1 2 3 4 5 6 7 8 9 openssl rsa -pubin -modulus -text -in pub.key RSA Public-Key: (256 bit) Modulus: 00:c0:33:2c:5c:64:ae:47:18:2f:6c:1c:87:6d:42: 33:69:10:54:5a:58:f7:ee:fe:fc :0b:ca:af:5a:f3: 41:cc:dd Exponent: 65537 (0x10001) Modulus=C0332C5C64AE47182F6C1C876D42336910545A58F7EEFEFC0BCAAF5AF341CCDD writing RSA key
e = 0x10001
n = 0xC0332C5C64AE47182F6C1C876D42336910545A58F7EEFEFC0BCAAF5AF341CCDD
http://www.factordb.com/index.php
在这个网站分解n得到两个素数
p = 285960468890451637935629440372639283459
q = 304008741604601924494328155975272418463
1 2 3 4 5 6 7 8 9 10 11 12 13 from Crypto.Util.number import *p = 285960468890451637935629440372639283459 q = 304008741604601924494328155975272418463 e = 65537 n = p * q phi = (p - 1 )*(q - 1 ) d = inverse(e, phi) import rsakey = rsa.PrivateKey(n,e,d,p,q) c = open ('flag.enc' ,'rb' ).read() print(rsa.decrypt(c,key))
flag{decrypt_256}
CrackRTF
链接: https://buuoj.cn/challenges#CrackRTF
EXE 32
IDA中 _main_0 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 int __cdecl main_0 () { DWORD v0; DWORD v1; CHAR String; int v4; CHAR String1; BYTE pbData; memset (&pbData, 0 , 0x104 u); memset (&String1, 0 , 0x104 u); v4 = 0 ; printf ("pls input the first passwd(1): " ); scanf ("%s" , &pbData); if ( strlen ((const char *)&pbData) != 6 ) { printf ("Must be 6 characters!\n" ); ExitProcess(0 ); } v4 = atoi((const char *)&pbData); if ( v4 < 100000 ) ExitProcess(0 ); strcat ((char *)&pbData, "@DBApp" ); v0 = strlen ((const char *)&pbData); sub_40100A(&pbData, v0, &String1); if ( !_strcmpi(&String1, "6E32D0943418C2C33385BC35A1470250DD8923A9" ) ) { printf ("continue...\n\n" ); printf ("pls input the first passwd(2): " ); memset (&String, 0 , 0x104 u); scanf ("%s" , &String); if ( strlen (&String) != 6 ) { printf ("Must be 6 characters!\n" ); ExitProcess(0 ); } strcat (&String, (const char *)&pbData); memset (&String1, 0 , 0x104 u); 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 ; }
输入的第一个密码要求为六位字符
函数atoi
则是将字符串转为数字
则第一个密码需要输入六位数字
跟进到函数sub_401230
使用函数CryptCreateHash
第二个参数为所使用的加密算法
https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id
通过这个文档可以得知0x8003
为md5, 0x8004
为sha1
所以可以爆破第一段hash
1 2 3 4 5 6 7 8 import hashlibpasswd = b"" for _ in range (100000 , 1000000 ): test = str (_).encode()+b"@DBApp" if hashlib.sha1(test).hexdigest() == "6e32d0943418c2c33385bc35a1470250dd8923a9" : print(passwd) break
123321@DBApp
而对于第二段的md5加密结果, 没有作输入字符集的限定, 比较难以爆破
https://www.somd5.com/
通过这个链接可以得到md5的明文
~!3a@0123321@DBApp
其实完全没必要爆破sha1, 直接在somd5查明文就能解题了
而后续的RTF操作有些复杂, 这里可以通过运行程序输入密码来得到flag
flag{N0_M0re_Free_Bugs}
[ACTF新生赛2020]easyre
链接: https://buuoj.cn/challenges#[ACTF新生赛2020]easyre
EXE 32
存在UPX壳, 脱壳后IDA中 _main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 int __cdecl main (int argc, const char **argv, const char **envp) { char v4; char v5; char v6; char v7; char v8; char v9; char v10; char v11; char v12; char v13; char v14; char v15; int v16; int v17; int v18; __int16 v19; char v20; char v21; char v22; int v23; int v24; int v25; char v26; int i; __main(); v4 = '*' ; v5 = 'F' ; v6 = '\'' ; v7 = '"' ; v8 = 'N' ; v9 = ',' ; v10 = '"' ; v11 = '(' ; v12 = 'I' ; v13 = '?' ; v14 = '+' ; v15 = '@' ; printf ("Please input:" ); scanf ("%s" , &v19); if ( (_BYTE)v19 != 'A' || HIBYTE(v19) != 'C' || v20 != 'T' || v21 != 'F' || v22 != '{' || v26 != '}' ) return 0 ; v16 = v23; v17 = v24; v18 = v25; for ( i = 0 ; i <= 11 ; ++i ) { if ( *(&v4 + i) != _data_start__[*((char *)&v16 + i) - 1 ] ) return 0 ; } printf ("You are correct!" ); return 0 ; }
这里值得一提的是, char c2[18]
中插入了一个int i2[3]
,
而int使用4Byte, char使用1Byte, 弄清楚内存大小对应关系即可
v16,v17,v18对应v23,v24,v25, 即flag[5:18]
*((char *)&v16 + i)
意为用char指针去访问v16的内存, 每次读取一个字节, 实际上是读取flag[5:18]
1 _data_start__ == "~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$# !\""
1 2 3 4 5 6 7 8 data = "~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$# !\"" string1 = "*F'\"N,\"(I?+@" flag = "flag{" for _ in string1: flag += chr (data.index(_)+1 ) flag += "}" print(flag)
flag{U9X_1S_W6@T?}
[2019红帽杯]easyRE
链接: https://buuoj.cn/challenges#[2019红帽杯]easyRE
ELF 64
IDA中函数 sub_4009C6 的反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 signed __int64 sub_4009C6 () { signed __int64 result; __int64 v1; __int64 v2; __int64 v3; __int64 v4; __int64 v5; __int64 v6; __int64 v7; __int64 v8; __int64 v9; __int64 v10; int i; char v12; char v13; char v14; char v15; char v16; char v17; char v18; char v19; char v20; char v21; char v22; char v23; char v24; char v25; char v26; char v27; char v28; char v29; char v30; char v31; char v32; char v33; char v34; char v35; char v36; char v37; char v38; char v39; char v40; char v41; char v42; char v43; char v44; char v45; char v46; char v47; char v48[32 ]; int v49; char v50; char v51; char v52; char v53; unsigned __int64 v54; v54 = __readfsqword(0x28 u); v12 = 73 ; v13 = 111 ; v14 = 100 ; v15 = 108 ; v16 = 62 ; v17 = 81 ; v18 = 110 ; v19 = 98 ; v20 = 40 ; v21 = 111 ; v22 = 99 ; v23 = 121 ; v24 = 127 ; v25 = 121 ; v26 = 46 ; v27 = 105 ; v28 = 127 ; v29 = 100 ; v30 = 96 ; v31 = 51 ; v32 = 119 ; v33 = 125 ; v34 = 119 ; v35 = 101 ; v36 = 107 ; v37 = 57 ; v38 = 123 ; v39 = 105 ; v40 = 121 ; v41 = 61 ; v42 = 126 ; v43 = 121 ; v44 = 76 ; v45 = 64 ; v46 = 69 ; v47 = 67 ; memset (v48, 0 , sizeof (v48)); v49 = 0 ; v50 = 0 ; sub_4406E0(0LL , (__int64)v48); v50 = 0 ; if ( sub_424BA0(v48) == 36 ) { for ( i = 0 ; i < (unsigned __int64)sub_424BA0(v48); ++i ) { if ( (unsigned __int8)(v48[i] ^ i) != *(&v12 + i) ) { result = 4294967294LL ; goto LABEL_13; } } sub_410CC0((__int64)"continue!" ); memset (&v51, 0 , 0x40 uLL); v53 = 0 ; sub_4406E0(0LL , (__int64)&v51); v52 = 0 ; if ( sub_424BA0(&v51) == 39 ) { v1 = sub_400E44((__int64)&v51); v2 = sub_400E44(v1); v3 = sub_400E44(v2); v4 = sub_400E44(v3); v5 = sub_400E44(v4); v6 = sub_400E44(v5); v7 = sub_400E44(v6); v8 = sub_400E44(v7); v9 = sub_400E44(v8); v10 = sub_400E44(v9); if ( !(unsigned int )sub_400360(v10, (__int64)off_6CC090) ) { sub_410CC0((__int64)"You found me!!!" ); sub_410CC0((__int64)"bye bye~" ); } result = 0LL ; } else { result = 0xFFFFFFFD LL; } } else { result = 0xFFFFFFFF LL; } LABEL_13: if ( __readfsqword(0x28 u) != v54 ) sub_444020(); return result; }
可以看到v10
和off_6CC090
进行比较, 相同则输出字符串
而off_6CC090为一段Base64编码
1 Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ==
结合前面的代码, 大概可以理解为输入的字符串进行10次Base64编码之后与这段Base64编码相比较
得到的内容为https://bbs.pediy.com/thread-254172.htm
显然这不是flag
1 2 3 4 5 6 7 8 for ( i = 0 ; i < (unsigned __int64)sub_424BA0(v48); ++i ) { if ( (unsigned __int8)(v48[i] ^ i) != *(&v12 + i) ) { result = 4294967294LL ; goto LABEL_13; } }
这段代码则将输入值与下标异或之后与一个固定值相比较, 则可以逆向得出正确输入值
1 2 3 4 5 6 string = [73 , 111 , 100 , 108 , 62 , 81 , 110 , 98 , 40 , 111 , 99 , 121 , 127 , 121 , 46 , 105 , 127 , 100 , 96 , 51 , 119 , 125 , 119 , 101 , 107 , 57 , 123 , 105 , 121 , 61 , 126 , 121 , 76 , 64 , 69 , 67 ] flag = "" for _ in range (len (string)): flag += chr (string[_]^_) print(flag)
Info:The first four chars are flag
而在内存分布中off_6CC090 的下面存在另外一段字符串qword_6CC0A0
1 2 3 4 5 6 qword_6CC0A0 = [ 0x40 , 0x35 , 0x20 , 0x56 , 0x5D , 0x18 , 0x22 , 0x45 , 0x17 , 0x2F , 0x24 , 0x6E , 0x62 , 0x3C , 0x27 , 0x54 , 0x48 , 0x6C , 0x24 , 0x6E , 0x72 , 0x3C , 0x32 , 0x45 , 0x5B ]
IDA中右键->Xrefs graph to...
, 得到函数sub_400D35
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 __int64 sub_400D35 () { __int64 result; unsigned __int64 v1; unsigned int v2; signed int i; signed int j; unsigned int v5; unsigned __int64 v6; v6 = __readfsqword(0x28 u); v2 = sub_43FD20() - qword_6CEE38; for ( i = 0 ; i <= 1233 ; ++i ) { sub_40F790(v2); sub_40FE60(v2); sub_40FE60(v2); v2 = (unsigned __int64)sub_40FE60(v2) ^ 0x98765432 ; } v5 = v2; if ( ((unsigned __int8)v2 ^ qword_6CC0A0[0 ]) == 'f' && (HIBYTE(v5) ^ qword_6CC0A0[3 ]) == 'g' ) { for ( j = 0 ; j <= 24 ; ++j ) sub_410E90(qword_6CC0A0[j] ^ *((_BYTE *)&v5 + j % 4 )); } v1 = __readfsqword(0x28 u); result = v1 ^ v6; if ( v1 != v6 ) sub_444020(); return result; }
而flag前四字节为"flag", v5
长度也为4字节
即可通过已知明文部分来得出v5
, 再通过v5
得出flag
1 2 3 4 5 6 7 8 9 string1 = [0x40 , 0x35 , 0x20 , 0x56 , 0x5D , 0x18 , 0x22 , 0x45 , 0x17 , 0x2F , 0x24 , 0x6E , 0x62 , 0x3C , 0x27 , 0x54 , 0x48 , 0x6C , 0x24 , 0x6E , 0x72 , 0x3C , 0x32 , 0x45 , 0x5B ] string2 = "flag" key = [] flag = "" for _ in range (len (string2)): key.append(ord (string2[_])^string1[_]) for _ in range (len (string1)): flag += chr (string1[_]^key[_%4 ]) print(flag)
flag{Act1ve_Defen5e_Test}
[ACTF新生赛2020]rome
链接: https://buuoj.cn/challenges#[ACTF新生赛2020]rome
EXE 32
IDA中函数 func 的反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 int func () { int result; int v1; int v2; int v3; int v4; unsigned __int8 v5; unsigned __int8 v6; unsigned __int8 v7; unsigned __int8 v8; unsigned __int8 v9; int v10; int v11; int v12; int v13; unsigned __int8 v14; char v15; char v16; char v17; char v18; char v19; char v20; char v21; char v22; char v23; char v24; char v25; char v26; char v27; char v28; char v29; char v30; char v31; int i; v15 = 81 ; v16 = 115 ; v17 = 119 ; v18 = 51 ; v19 = 115 ; v20 = 106 ; v21 = 95 ; v22 = 108 ; v23 = 122 ; v24 = 52 ; v25 = 95 ; v26 = 85 ; v27 = 106 ; v28 = 119 ; v29 = 64 ; v30 = 108 ; v31 = 0 ; printf ("Please input:" ); scanf ("%s" , &v5); result = v5; if ( v5 == 'A' ) { result = v6; if ( v6 == 'C' ) { result = v7; if ( v7 == 'T' ) { result = v8; if ( v8 == 'F' ) { result = v9; if ( v9 == '{' ) { result = v14; if ( v14 == '}' ) { v1 = v10; v2 = v11; v3 = v12; v4 = v13; for ( i = 0 ; i <= 15 ; ++i ) { if ( *((_BYTE *)&v1 + i) > 64 && *((_BYTE *)&v1 + i) <= 90 ) *((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 51 ) % 26 + 65 ; if ( *((_BYTE *)&v1 + i) > 96 && *((_BYTE *)&v1 + i) <= 122 ) *((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 79 ) % 26 + 97 ; } for ( i = 0 ; i <= 15 ; ++i ) { result = (unsigned __int8)*(&v15 + i); if ( *((_BYTE *)&v1 + i) != (_BYTE)result ) return result; } result = printf ("You are correct!" ); } } } } } } return result; }
1 2 3 4 5 6 7 8 for ( i = 0 ; i <= 15 ; ++i ){ if ( *((_BYTE *)&v1 + i) > 64 && *((_BYTE *)&v1 + i) <= 90 ) *((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 51 ) % 26 + 65 ; if ( *((_BYTE *)&v1 + i) > 96 && *((_BYTE *)&v1 + i) <= 122 ) *((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 79 ) % 26 + 97 ; }
重点看这段代码
*((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 51) % 26 + 65;
与下面的写法等效
*((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 65 + 14) % 26 + 65;
那么显而易见得就是凯撒密码了
1 2 3 4 5 6 7 8 9 10 11 12 cipher = [81 , 115 , 119 , 51 , 115 , 106 , 95 , 108 , 122 , 52 , 95 , 85 , 106 , 119 , 64 , 108 ] flag = "flag{" for _ in cipher: if _ in range (65 , 91 ): flag += chr (((_-65 -14 )%26 )+65 ) elif _ in range (97 , 123 ): flag += chr (((_-97 -18 )%26 )+97 ) else : flag += chr (_) flag += "}" print(flag)
flag{Cae3ar_th4_Gre@t}
Youngter-drive
链接: https://buuoj.cn/challenges#Youngter-drive
EXE 32
存在UPX壳, 脱壳后IDA中 _main_0 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int main_0 () { HANDLE v2; HANDLE hObject; sub_4110FF(); ::hObject = CreateMutexW(0 , 0 , 0 ); j_strcpy(Dest, Source); hObject = CreateThread(0 , 0 , (LPTHREAD_START_ROUTINE)StartAddress, 0 , 0 , 0 ); v2 = CreateThread(0 , 0 , (LPTHREAD_START_ROUTINE)sub_41119F, 0 , 0 , 0 ); CloseHandle(hObject); CloseHandle(v2); while ( dword_418008 != -1 ) ; sub_411190(); CloseHandle(::hObject); return 0 ; }
这里可以看到程序使用了多线程
分别跟进两个线程中所运行的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void __stdcall StartAddress_0 (int a1) { while ( 1 ) { WaitForSingleObject(hObject, 0xFFFFFFFF ); if ( dword_418008 > -1 ) { sub_41112C((int )Source, dword_418008); --dword_418008; Sleep(0x64 u); } ReleaseMutex(hObject); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 void __stdcall sub_411B10 (int a1) { while ( 1 ) { WaitForSingleObject(hObject, 0xFFFFFFFF ); if ( dword_418008 > -1 ) { Sleep(0x64 u); --dword_418008; } ReleaseMutex(hObject); } }
hObject 可以理解为一个标识, 先运行StartAddress_0 , 此时hObject 为空闲状态
WaitForSingleObject 在hObject 为空闲状态 时可以运行后续的代码, 并将hObject 设置为繁忙状态 , 直到后续代码运行结束之后再运行函数ReleaseMutex , 将hObject 为空闲状态 设置为空闲状态
而当hObject 为空闲状态 之前, 函数sub_411B10
都在运行WaitForSingleObject , 即等待hObject 变为空闲状态
那么函数StartAddress_0
, sub_411B10
将交替运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 char *__cdecl sub_411940 (int a1, int a2) { char *result; char v3; v3 = *(_BYTE *)(a2 + a1); if ( (v3 < 97 || v3 > 122 ) && (v3 < 65 || v3 > 90 ) ) exit (0 ); if ( v3 < 97 || v3 > 122 ) { result = off_418000[0 ]; *(_BYTE *)(a2 + a1) = off_418000[0 ][*(char *)(a2 + a1) - 38 ]; } else { result = off_418000[0 ]; *(_BYTE *)(a2 + a1) = off_418000[0 ][*(char *)(a2 + a1) - 96 ]; } return result; }
函数sub_411940
用于替换所输入的Source字符串
off_418000[0][*(char *)(a2 + a1) - 38]
表面上是个二维数组, 但是第一维只有一个值, 所以当一维数组处理即可
dword_418008 = 29
off_418000 = “QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm”
最后运行函数sub_411880()
, 用于校验被替换之后的flag是否正确
1 2 3 4 5 6 7 8 9 10 11 int sub_411880 () { int i; for ( i = 0 ; i < 29 ; ++i ) { if ( Source[i] != off_418004[i] ) exit (0 ); } return printf ("\nflag{%s}\n\n" , Dest); }
off_418004 = “TOiZiZtOrYaToUwPnToBsOaOapsyS”
1 2 3 4 5 6 7 8 9 10 11 12 13 source = "TOiZiZtOrYaToUwPnToBsOaOapsyS" off = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm" flag = "flag{" for _ in range (len (source)): if _%2 == 0 : flag += source[_] elif ord (source[_]) < 97 : flag += chr (off.index(source[_])+96 ) else : flag += chr (off.index(source[_])+38 ) flag += "}" print(flag)
由于未知原因, 需要加上一个'E'
才能正确提交flag
另外一点, dword_418008 = 29
, 而字符串长度也为29
在访问off_418004[29]时理论上是访问到\x00
那么替换时, 会被off_418000[0][-38]所替换
flag{ThisisthreadofwindowshahaIsESE}
[FlareOn4]login
链接: https://buuoj.cn/challenges#[FlareOn4]login
Javascript
1 2 3 4 5 6 7 8 9 document .getElementById("prompt" ).onclick = function ( ) { var flag = document .getElementById("flag" ).value; var rotFlag = flag.replace(/[a-zA-Z]/g , function (c ) {return String .fromCharCode((c <= "Z" ? 90 : 122 ) >= (c = c.charCodeAt(0 ) + 13 ) ? c : c - 26 );}); if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) { alert("Correct flag!" ); } else { alert("Incorrect flag, rot again" ); } }
由于不熟悉JS, 只能通过在浏览器Console中运行JS来判断加密方法
1 2 3 4 5 flag = "abcdefghjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~!@#$%^&*()_+-=" "abcdefghjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~!@#$%^&*()_+-=" ------ flag.replace(/[a-zA-Z]/g , function (c ) {return String .fromCharCode((c <= "Z" ? 90 : 122 ) >= (c = c.charCodeAt(0 ) + 13 ) ? c : c - 26 );}); "nopqrstuwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM`~!@#$%^&*()_+-="
rot13
1 2 3 4 5 6 7 8 9 10 11 cipher = "PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" flag = "" for _ in cipher: if 65 <=ord (_)<=90 : flag += chr ((ord (_)-65 +13 )%26 +65 ) elif 97 <=ord (_)<=122 : flag += chr ((ord (_)-97 +13 )%26 +97 ) else : flag += _ print(flag)
flag{ClientSideLoginsAreEasy@flare-on.com}
[GUET-CTF2019]re
链接: https://buuoj.cn/challenges#[GUET-CTF2019]re
ELF 64
UPX脱壳
字符串aInputYourFlag
-> sub_400E28
-> sub_4009AE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 _BOOL8 __fastcall sub_4009AE (char *a1) { if ( 1629056 * *a1 != 166163712 ) return 0LL ; if ( 6771600 * a1[1 ] != 731332800 ) return 0LL ; if ( 3682944 * a1[2 ] != 357245568 ) return 0LL ; if ( 10431000 * a1[3 ] != 1074393000 ) return 0LL ; if ( 3977328 * a1[4 ] != 489211344 ) return 0LL ; if ( 5138336 * a1[5 ] != 518971936 ) return 0LL ; if ( 7532250 * a1[7 ] != 406741500 ) return 0LL ; if ( 5551632 * a1[8 ] != 294236496 ) return 0LL ; if ( 3409728 * a1[9 ] != 177305856 ) return 0LL ; if ( 13013670 * a1[10 ] != 650683500 ) return 0LL ; if ( 6088797 * a1[11 ] != 298351053 ) return 0LL ; if ( 7884663 * a1[12 ] != 386348487 ) return 0LL ; if ( 8944053 * a1[13 ] != 438258597 ) return 0LL ; if ( 5198490 * a1[14 ] != 249527520 ) return 0LL ; if ( 4544518 * a1[15 ] != 445362764 ) return 0LL ; if ( 3645600 * a1[17 ] != 174988800 ) return 0LL ; if ( 10115280 * a1[16 ] != 981182160 ) return 0LL ; if ( 9667504 * a1[18 ] != 493042704 ) return 0LL ; if ( 5364450 * a1[19 ] != 257493600 ) return 0LL ; if ( 13464540 * a1[20 ] != 767478780 ) return 0LL ; if ( 5488432 * a1[21 ] != 312840624 ) return 0LL ; if ( 14479500 * a1[22 ] != 1404511500 ) return 0LL ; if ( 6451830 * a1[23 ] != 316139670 ) return 0LL ; if ( 6252576 * a1[24 ] != 619005024 ) return 0LL ; if ( 7763364 * a1[25 ] != 372641472 ) return 0LL ; if ( 7327320 * a1[26 ] != 373693320 ) return 0LL ; if ( 8741520 * a1[27 ] != 498266640 ) return 0LL ; if ( 8871876 * a1[28 ] != 452465676 ) return 0LL ; if ( 4086720 * a1[29 ] != 208422720 ) return 0LL ; if ( 9374400 * a1[30 ] == 515592000 ) return 5759124 * a1[31 ] == 719890500 ; return 0LL ; }
两个坑
没有给出a1[6]
a[17]和a[16]调换顺序
1 2 3 4 5 6 7 num1 = [1629056 , 6771600 , 3682944 , 10431000 , 3977328 , 5138336 , 7532250 , 5551632 , 3409728 , 13013670 , 6088797 , 7884663 , 8944053 , 5198490 , 4544518 , 10115280 , 3645600 , 9667504 , 5364450 , 13464540 , 5488432 , 14479500 , 6451830 , 6252576 , 7763364 , 7327320 , 8741520 , 8871876 , 4086720 , 9374400 , 5759124 ] num2 = [166163712 , 731332800 , 357245568 , 1074393000 , 489211344 , 518971936 , 406741500 , 294236496 , 177305856 , 650683500 , 298351053 , 386348487 , 438258597 , 249527520 , 445362764 , 981182160 , 174988800 , 493042704 , 257493600 , 767478780 , 312840624 , 1404511500 , 316139670 , 619005024 , 372641472 , 373693320 , 498266640 , 452465676 , 208422720 , 515592000 , 719890500 ] flag = "" for _ in range (len (num1)): flag += chr (num2[_] // num1[_]) print(flag)
最后需要爆破flag[6]
flag{e165421110ba03099a1c039337}
[SUCTF2019]SignIn
链接: https://buuoj.cn/challenges#[SUCTF2019]SignIn
ELF 64
IDA中 main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { char v4; char v5; char v6; char v7; char v8; char v9; unsigned __int64 v10; v10 = __readfsqword(0x28 u); puts ("[sign in]" ); printf ("[input your flag]: " , a2); __isoc99_scanf("%99s" , &v8); sub_96A(&v8, (__int64)&v9); __gmpz_init_set_str((__int64)&v7, (__int64)"ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35" , 16LL ); __gmpz_init_set_str((__int64)&v6, (__int64)&v9, 16LL ); __gmpz_init_set_str( (__int64)&v4, (__int64)"103461035900816914121390101299049044413950405173712170434161686539878160984549" , 10LL ); __gmpz_init_set_str((__int64)&v5, (__int64)"65537" , 10LL ); __gmpz_powm((__int64)&v6, (__int64)&v6, (__int64)&v5, (__int64)&v4); if ( (unsigned int )__gmpz_cmp(&v6, &v7) ) puts ("GG!" ); else puts ("TTTTTTTTTTql!" ); return 0LL ; }
函数__gmpz_powm((__int64)&v6, (__int64)&v6, (__int64)&v5, (__int64)&v4)
意为
v6 = (v6**v5)%v4
最后比较v6与v7
所以很明显就是RSA
1 2 3 4 5 6 7 8 9 10 11 12 from Crypto.Util.number import *p = 282164587459512124844245113950593348271 q = 366669102002966856876605669837014229419 e = 65537 c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35 n = p * q phi = (p - 1 )*(q - 1 ) d = inverse(e, phi) m = pow (c, d, n) flag = long_to_bytes(m) print(flag)
flag{Pwn_@_hundred_years}
相册
链接: https://buuoj.cn/challenges#相册
Android
下载得到一个APk包, 提取class.dex文件转jar之后也没看到什么有价值的信息
提取libcore.so到IDA中看看
看到一段Base64字符串MTgyMTg0NjUxMjVAMTYzLmNvbQ==
解码之后为18218465125@163.com
即为flag
flag{18218465125@163.com}
[BJDCTF2020]easy
链接: https://buuoj.cn/challenges#[BJDCTF2020]easy
EXE 32
IDA中 _main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 int __cdecl main (int argc, const char **argv, const char **envp) { time_t v4; struct tm *v5 ; __main(); time(&v4); v5 = localtime(&v4); puts ("Can you find me?\n" ); system("pause" ); return 0 ; }
没有得到关于flag的信息
继续查看其他函数
函数 _ques 有数据以及输出
修改一下反编译之后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #define SHIDWORD(x) (*((__int32*)&(x)+1)) #define LODWORD(x) (*((__int32*)&(x))) #define HIDWORD(x) (*((__int32*)&(x)+1)) #include <stdio.h> int main (void ) { int v0; int result; int v2[50 ]; int v3[10 ] = {2147122737 , 140540 , 2286567993 , 141956 , 139457077 , 262023 , 2286043699 , 143749 , 2118271985 , 143868 }; int j; __int64 v14; int v15; int v16; int i; for ( i = 0 ; i <= 4 ; ++i ) { memset (v2, 0 , sizeof (v2)); v16 = 0 ; v15 = 0 ; v0 = *(&v3[1 ] + 2 * i); LODWORD(v14) = *(&v3[0 ] + 2 * i); HIDWORD(v14) = v0; while ( SHIDWORD(v14) > 0 || v14 >= 0 && (__int32)v14 ) { v2[v16++] = ((SHIDWORD(v14) >> 31 ) ^ (((unsigned __int8)(SHIDWORD(v14) >> 31 ) ^ (unsigned __int8)v14) - (unsigned __int8)(SHIDWORD(v14) >> 31 )) & 1 ) - (SHIDWORD(v14) >> 31 ); v14 /= 2LL ; } for ( j = 50 ; j >= 0 ; --j ) { if ( v2[j] ) { if ( v2[j] == 1 ) { putchar (42 ); ++v15; } } else { putchar (32 ); ++v15; } if ( !(v15 % 5 ) ) putchar (32 ); } result = putchar (10 ); } return 0 ; }
运行即可得到flag
IDA Pro反编译代码类型转换参考
flag{HACKIT4FUN}
[ACTF新生赛2020]usualCrypt
链接: https://buuoj.cn/challenges#[ACTF新生赛2020]usualCrypt
EXE 32
IDA中 _main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; int result; int v5; int v6; int v7; __int16 v8; char v9; char v10; sub_403CF8(&unk_40E140); scanf (aS, &v10); v5 = 0 ; v6 = 0 ; v7 = 0 ; v8 = 0 ; v9 = 0 ; sub_401080((int )&v10, strlen (&v10), (int )&v5); v3 = 0 ; while ( *((_BYTE *)&v5 + v3) == byte_40E0E4[v3] ) { if ( ++v3 > strlen ((const char *)&v5) ) goto LABEL_6; } sub_403CF8(aError); LABEL_6: if ( v3 - 1 == strlen (byte_40E0E4) ) result = sub_403CF8(aAreYouHappyYes); else result = sub_403CF8(aAreYouHappyNo); return result; }
函数 sub_401080 对输入的内容进行变换
继续浏览信息可以看到两个字符串
1 2 char byte_40E0A0[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;char *byte_40E0AA = &byte_40E0A0[10 ];
两个字符串的关系大概是这样
很明显是在进行Base64编码的操作
但是在编码前有函数 sub_401000 , 编码后有函数 sub_401030
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 signed int sub_401000 () { signed int result; char v1; result = 6 ; do { v1 = byte_40E0AA[result]; byte_40E0AA[result] = byte_40E0A0[result]; byte_40E0A0[result++] = v1; } while ( result < 15 ); return result; }
这个函数的话大概就是在调换Base64编码表, 弄清楚两个字符串的关系即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int __cdecl sub_401030 (const char *a1) { __int64 v1; char v2; v1 = 0 i64; if ( strlen (a1) != 0 ) { do { v2 = a1[HIDWORD(v1)]; if ( v2 < 97 || v2 > 122 ) { if ( v2 < 65 || v2 > 90 ) goto LABEL_9; LOBYTE(v1) = v2 + 32 ; } else { LOBYTE(v1) = v2 - 32 ; } a1[HIDWORD(v1)] = v1; LABEL_9: LODWORD(v1) = 0 ; ++HIDWORD(v1); } while ( HIDWORD(v1) < strlen (a1) ); } return v1; }
大小写转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import base64cipher1 = "zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9" cipher2 = cipher1.swapcase() cipher3 = "" table1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" table_lst = [] for _ in table1: table_lst.append(_) offset = 10 for i in range (6 , 15 ): tmp = table_lst[i+offset] table_lst[i+offset] = table_lst[i] table_lst[i] = tmp table2 = "" .join(table_lst) for _ in cipher2: cipher3 += table1[table2.index(_)] flag = base64.b64decode(cipher3) print(flag)
flag{bAse64_h2s_a_Surprise}
链接: https://buuoj.cn/challenges#[MRCTF2020]Transform
EXE 64
IDA中 main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v3; __int64 v4; char v6[104 ]; int j; int i; sub_402230(argc, argv, envp); sub_40E640(argc, (__int64)argv, v3, (__int64)"Give me your code:\n" ); sub_40E5F0(argc, (__int64)argv, (__int64)v6, (unsigned __int64)"%s" ); if ( strlen (*(const char **)&argc) != 33 ) { sub_40E640(argc, (__int64)argv, v4, (__int64)"Wrong!\n" ); system(*(const char **)&argc); exit (argc); } for ( i = 0 ; i <= 32 ; ++i ) { byte_414040[i] = v6[dword_40F040[i]]; v4 = i; byte_414040[i] ^= LOBYTE(dword_40F040[i]); } for ( j = 0 ; j <= 32 ; ++j ) { v4 = j; if ( byte_40F0E0[j] != byte_414040[j] ) { sub_40E640(argc, (__int64)argv, j, (__int64)"Wrong!\n" ); system(*(const char **)&argc); exit (argc); } } sub_40E640(argc, (__int64)argv, v4, (__int64)"Right!Good Job!\n" ); sub_40E640(argc, (__int64)argv, (__int64)v6, (__int64)"Here is your flag: %s\n" ); system(*(const char **)&argc); return 0 ; }
v6
为输入的值, 即flag
先将flag字符串打乱顺序, 再进行异或处理
这里值得注意的一点为
dword_40F040[32] = 0x00000000
由于后续还有三个连续的0x00000000
, 可能会误以为dword_40F040的长度只有31
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 _40F040 = [ 0x09 , 0x0A , 0x0F , 0x17 , 0x07 , 0x18 , 0x0C , 0x06 , 0x01 , 0x10 , 0x03 , 0x11 , 0x20 , 0x1D , 0x0B , 0x1E , 0x1B , 0x16 , 0x04 , 0x0D , 0x13 , 0x14 , 0x15 , 0x02 , 0x19 , 0x05 , 0x1F , 0x08 , 0x12 , 0x1A , 0x1C , 0x0E ,0x00 ]cipher = [ 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 ]for _ in range (len (cipher)): cipher[_] = cipher[_]^_40F040[_] plain = [0 for _ in range (33 )] for _ in range (len (plain)): plain[_40F040[_]] = chr (cipher[_]) flag = '' .join(plain) print(flag)
flag{Tr4nsp0sltiON_Clph3r_1s_3z}
[WUSTCTF2020]level1
链接: https://buuoj.cn/challenges#[WUSTCTF2020]level1
ELF 64
IDA中 main 函数反编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl main (int argc, const char **argv, const char **envp) { FILE *stream; signed int i; char ptr[24 ]; unsigned __int64 v7; v7 = __readfsqword(0x28 u); stream = fopen("flag" , "r" ); fread(ptr, 1uLL , 20uLL , stream); fclose(stream); for ( i = 1 ; i <= 19 ; ++i ) { if ( i & 1 ) printf ("%ld\n" , (unsigned int )(ptr[i] << i)); else printf ("%ld\n" , (unsigned int )(i * ptr[i])); } return 0 ; }
没有给出flag[0]
所以解密时也需要填充一个flag[0], 以保证下标对齐
1 2 3 4 5 6 7 8 9 cipher = [" " , 198 , 232 , 816 , 200 , 1536 , 300 , 6144 , 984 , 51200 , 570 , 92160 , 1200 , 565248 , 756 , 1474560 , 800 , 6291456 , 1782 , 65536000 ] for _ in range (1 , 20 ): if _ & 1 : cipher[_] = chr (cipher[_]>>_) else : cipher[_] = chr (cipher[_]//_) flag = "" .join(cipher) print(flag)
flag{d9-dE6-20c}
[WUSTCTF2020]level2
链接: https://buuoj.cn/challenges#[WUSTCTF2020]level2
ELF 32
UPX脱壳即可用IDA在main 函数中看到flag
flag{Just_upx_-d}
EOF