|
原文链接:比赛中遇到的一些简单的逆向题目
0x01 easycrackme
昨天小伙伴发来一个逆向题目让帮忙做一下,拿到程序是个elf文件,先放到kali里面运行一下,题目提示要输入一个key
编辑
image-20220810134953307
可以看到程序一共有6关,通过这6关拿到flag
先来看下第一关
size_t __fastcall check1(const char *a1){ size_t result; // rax@3 size_t v2; // [sp+18h] [bp-8h]@1 puts("=== 关卡 1 ==="); v2 = strlen(a1); if ( a1[v2 - 1] == 10 ) a1[v2 - 1] = 0; result = strlen(a1); if ( result != 34 ) { puts("-- 通关失败"); exit(1); } return result;}
简单分析一下,第一关只是对长度进行检查,长度应该为34才对,输入一个长度为34的字符串试试。
第一关通关成功,再来看看第二关。
int __fastcall check2(const char *a1){ int result; // eax@1 puts("=== 关卡 2 ==="); result = strncmp(a1, "flag{", 5uLL); if ( result ) { puts("-- 通关失败"); exit(1); } return result;}
第二关是对输入字符串的前5个字符进行校验,如果前5个字符为flag{就会通关
来试一下
编辑
第二关通关成功,来看下第三关
__int64 __fastcall check3(const char *a1){ __int64 result; // rax@1 puts("=== 关卡 3 ==="); result = a1[strlen(a1) - 1]; if ( (_BYTE)result != 125 ) { puts("-- 通关失败"); exit(1); } return result;}
第三关是取最后一位字符串和125进行比较,125是}的ascii码,所以第三关是判断最后一位字符串是不是}
来试一下
编辑
第三关通关成功,继续看下第四关
void __fastcall check4(const char *a1){ const char v1; // [sp+16h] [bp-2Ah]@2 const char v2; // [sp+17h] [bp-29h]@8 unsigned __int64 i; // [sp+18h] [bp-28h]@1 char *v4; // [sp+28h] [bp-18h]@1 char *s1; // [sp+38h] [bp-8h]@1 puts("=== 关卡 4 ==="); v4 = (char *)(strchr(a1, 95) - a1); // 95 == _ s1 = (char *)malloc(((unsigned __int64)(v4 - 5) >> 1) + 1); for ( i = 0LL; i < (unsigned __int64)(v4 - 5) >> 1; ++i ) { v1 = a1[2 * i + 5]; if ( v1 <= 47 || v1 > 57 ) { if ( v1 > 96 && v1 <= 102 ) v1 -= 87; } else { v1 -= 48; } v2 = a1[2 * i + 6]; if ( v2 <= 47 || v2 > 57 ) { if ( v2 > 96 && v2 <= 102 ) v2 -= 87; } else { v2 -= 48; } s1 = v2 | 16 * v1; } s1[(unsigned __int64)(v4 - 5) >> 1] = 0; if ( strcmp(s1, "olympics") ) { puts("-- 通关失败"); exit(1); } free(s1);}
第四关稍微复杂一点,整体程序前三关是对输入字符串的格式进行判断,后三关是对输入的字符串内容进行判断,后三关分别对应着三串字符串,通过_连接,拼接起来得到flag
第四关就是第一个字符串,首先程序会找到字符串中_的位置,然后根据_的位置作为循环的长度进行处理,最终经过处理的字符串和olympics进行比较,如果相等就通关,如果不相等就输出通关失败。
对字符串处理的关键代码在for循环内,简单分析一下for循环是对字符串中第5位到第20位进行处理,s1就是要比较的字符串,分别取单数字符的ascii和双数字符的ascii乘16再进行位运算,写个脚本爆破一下
str = 'olympics'for i in str: for v1 in range(33, 127): for v2 in range(33, 127): count = v1 if count <= 47 or count > 57: if count > 96 and count <= 102: count -= 87 else: count -= 48 if v2 <= 47 or v2 > 57: if v2 > 96 and v2 <= 102: v2 -= 87 else: v2 -= 48 if (v2 | 16 * count) == ord(i): print(i,ord(i),'v1=', chr(v1), v1, 'v2=', chr(v2), v2)
爆破得到符合条件的解不止一个,所以这道题目应该都多个flag,随便找一个符合条件的解运行一下看看
编辑
可以看到已经通关,继续看下第五关
void __fastcall check5(const char *a1){ char *v1; // rax@1 char v2; // si@3 unsigned __int64 i; // [sp+18h] [bp-28h]@1 signed __int64 v4; // [sp+20h] [bp-20h]@1 char *v5; // [sp+28h] [bp-18h]@1 char *s1; // [sp+38h] [bp-8h]@1 puts("=== 关卡 5 ==="); v1 = strchr(a1, 95); v4 = v1 + 1 - a1; v5 = (char *)(strchr(v1 + 1, 95) - a1); s1 = (char *)malloc((size_t)&v5[-v4 + 1]); for ( i = 0LL; i < (unsigned __int64)&v5[-v4]; ++i ) { if ( i & 1 ) v2 = 33; else v2 = 32; s1 = *(&a1 + v4) ^ v2; } s1[2 * (_QWORD)&v5[-v4]] = 0; if ( strcmp(s1, "in") ) { puts("-- 通关失败"); exit(1); } free(s1);}
第五关比较简单,就是两个字符串分别与32和33进行异或得到in,所以正确的字符串应该是IO,在这里就出现问题了,不知道我的电脑什么原因,输入正确的字符串无法通关第五关
编辑
这时我以为是我做错了,要到了别人的wp,仔细看了一下没什么问题,只是第四关解的方式不一样,可是我心想第四关也和第五关没关系啊,让朋友在他的电脑上试一下,发现是可以的,后来我又换了Ubuntu试了一下发现是可以的,不知道为啥
编辑
那继续第六关
__int64 __fastcall check6(const char *a1){ char *v1; // rax@1 char *v2; // rax@1 char *v3; // rax@1 signed __int64 v4; // rax@7 signed __int64 v5; // rcx@8 __int64 v6; // rdi@8 __int64 v7; // rsi@8 int v8; // eax@13 int v9; // eax@16 char *s; // [sp+8h] [bp-1A8h]@1 int v12; // [sp+1Ch] [bp-194h]@12 signed __int64 v13; // [sp+20h] [bp-190h]@4 signed __int64 v14; // [sp+28h] [bp-188h]@4 unsigned __int64 v15; // [sp+30h] [bp-180h]@11 __int64 v16; // [sp+38h] [bp-178h]@11 signed __int64 v17; // [sp+40h] [bp-170h]@1 signed __int64 v18; // [sp+48h] [bp-168h]@1 char *s1; // [sp+58h] [bp-158h]@8 int v20[82]; // [sp+60h] [bp-150h]@8 __int64 v21; // [sp+1A8h] [bp-8h]@1 s = (char *)a1; v21 = *MK_FP(__FS__, 40LL); puts("=== 关卡 6 ==="); v1 = strchr(a1, 95); v2 = strchr(v1 + 1, 95); v17 = v2 + 1 - a1; v3 = strchr(v2 + 1, 125); v18 = v3 - a1; if ( ((_BYTE)v3 - (_BYTE)a1 - (_BYTE)v17) & 3 ) { puts("-- 通关失败"); exit(1); } v13 = 3 * ((unsigned __int64)(v18 - v17) >> 2); v14 = v18 - v17; while ( 1 ) { v4 = v14--; if ( !v4 || *(&a1[v14] + v17) != 61 ) break; --v13; } s1 = (char *)malloc(v13 + 1); v5 = 40LL; v6 = (__int64)v20; v7 = (__int64)">"; while ( v5 ) { *(_QWORD *)v6 = *(_QWORD *)v7; v7 += 8LL; v6 += 8LL; --v5; } v15 = 0LL; v16 = 0LL; while ( v15 < v13 ) { v12 = (v20[*(&s[v15] + v17) - 43] << 6) | v20[*(&s[v17 + 1] + v15) - 43]; if ( *(&s[v17 + 2] + v15) == 61 ) v8 = v12 << 6; else v8 = (v12 << 6) | v20[*(&s[v17 + 2] + v15) - 43]; if ( *(&s[v17 + 3] + v15) == 61 ) v9 = v8 << 6; else v9 = (v8 << 6) | v20[*(&s[v17 + 3] + v15) - 43]; s1[v16] = v9 >> 16; if ( *(&s[v17 + 2] + v15) != 61 ) s1[v16 + 1] = BYTE1(v9); if ( *(&s[v17 + 3] + v15) != 61 ) s1[v16 + 2] = v9; v15 += 4LL; v16 += 3LL; } if ( strcmp(s1, "china") ) { puts("-- 通关失败"); exit(1); } free(s1); return *MK_FP(__FS__, 40LL) ^ v21;}
第六关看到字符串china,有了前两关的经验猜测是处理后的字符串和china进行比较,但是看这个代码比较复杂啊,这里说实话一开始没看到,看到别人的wp说这里有左移6和等号等字符,判断是base64的解码(看来自己的知识储备还是不够,这段代码要是猜不出来是base64解码再去分析要浪费很多时间了),所以将china进行base64编码后得到flag
题目到这已经得到了flag,应该已经结束了,但是看了其他人的wp发现第四关检查的应该是olympics的十六进制6f6c796d70696373,在上边用脚本爆破的时候就说过了第四关应该有很多个解,所以说这个题目出的很有问题,虽然不是一道很好的题目,但是还是记录一下。
0x02 freestyle
第二届网刃杯网络安全大赛题目
查看伪代码,主函数中发现两个功能函数
__int64 fun1(){ char s[24]; // [rsp+0h] [rbp-20h] BYREF unsigned __int64 v2; // [rsp+18h] [rbp-8h] v2 = __readfsqword(0x28u); puts("Welcome to Alaska!!!"); puts("please input key: "); fgets(s, 20, stdin); if ( 4 * (3 * atoi(s) / 9 - 9) != 4400 ) exit(0); puts("ok,level_1 over!\n\n"); return 1LL;}
__int64 fun2(){ char s[24]; // [rsp+0h] [rbp-20h] BYREF unsigned __int64 v2; // [rsp+18h] [rbp-8h] v2 = __readfsqword(0x28u); puts("Welcome to Paradise Lost!!!"); puts("The code value is the smallest divisible"); puts("please input key: "); fgets(s, 20, stdin); if ( 2 * (atoi(s) % 56) != 98 ) exit(0); puts("ok,level_2 over!"); return 1LL;}
分析代码得到,只是简单的数学运算,得到fun1的值为3327,fun2的值为105
题目提示flag是md5格式,也就是3327105的md5值
flag{31a364d51abd0c8304106c16779d83b1}
0x03 Re_function
第二届网刃杯网络安全大赛题目
题目比赛的时候没做出来,拿到手是个压缩吧,有密码,一开始爆破没成功,他这个其实是压缩包后面跟着一串十六进制数据,里面是压缩包的密码,这个也是后来看别人的wp才知道的,一开始也用十六进制编辑器打开看了,也发现了后面的十六进制数据,当时没多想。
编辑
image-20220425165501189
png的文件头
把数据复制出来,在线解密网站
编辑
得到一半图片,密码为3CF8
解压后得到一个exe和一个elf文件
先分析下exe运行一下是要输入一个flag,看了看伪代码,没看明白
编辑
image-20220810135105777
经过分析发现一个main函数,但是没法反编译,后来看了很多的wp直说是换表的base64,但是还不是很理解。
去OD进行动调看看
编辑
image-20220505135822643
搜索字符串直接断到输入的位置,并且根据OD给出的提示发现字符串长度为28位,F8继续调试
编辑
image-20220505135512928
找到对输入字符串进行处理的位置,这里是每隔2位,把输入的字符串和0x37进行xor,F8继续调试
编辑
image-20220505135940440
在这里可以看到处理后输入的字符串和要进行对比的28位字符串,前边判断是每隔2位和0x37进行xor,写脚本还原之前的字符串
str = [0x64, 0x71, 0x54, 0x54, 0x64, 0x78, 0x74, 0x78, 0x64, 0x41, 0x40, 0x48, 0x70, 0x6D, 0x18, 0x4A, 0x41, 0x78, 0x66, 0x72, 0x41,0x78, 0x5E, 0x4E, 0x5D, 0x52, 0x0E]for i in range(0,len(str),2): str ^= 0x37print(bytes(str))# SqcTSxCxSAwHGm/JvxQrvxiNjR9
其实到这里这个exe就已经分析完了,打开elf文件,找到换的解密表进行解密就可以了
image-20220810135152058
这里是更换的表,在线解密
编辑
也可以通过python脚本进行解密
import base64a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' #标准表b = 'FeVYKw6a0lDIOsnZQ5EAf2MvjS1GUiLWPTtH4JqRgu3dbC8hrcNo9/mxzpXBky7+' #新表c = 'SqcTSxCxSAwHGm/JvxQrvxiNjR9='trantab = c.maketrans(b, a)print(base64.b64decode(c.translate(trantab)))
flag{we1come_t0_wrb}
0x04 眼力大考验
2022年蓝贝国际创新创业大赛“数字技术+信息安全领域赛道网络攻防大赛”
拿到程序,无壳,主函数伪代码。
int __cdecl main(int argc, const char **argv, const char **envp){ const char *v3; // eax char v5; // dl char v6; // cl char v7; // bl __main(); if ( argc == 2 ) { v3 = argv[1]; if ( *v3 == 51 && v3[1] == 54 && v3[2] == byte_403005 )// 62h { v5 = v3[3]; if ( v5 == byte_403008 && v3[4] == 50 && v3[5] == 52 && v3[6] == 52 && v3[7] == 50 ) { v6 = v3[8]; if ( v6 == table ) { v7 = v3[9]; if ( v7 == byte_403007 && v7 == v3[10] && v6 == v3[11] && v3[12] == 52 && v3[13] == 57 && v3[14] == 49 && v3[15] == 48 && v3[16] == 53 && v3[17] == 51 && v3[18] == 48 && v3[19] == 50 && v7 == v3[20] && v6 == v3[21] && v5 == v3[22] && v3[23] == 56 && v5 == v3[24] && v7 == v3[25] && v3[26] == byte_403006 && v3[27] == byte_403009 && v3[28] == 50 && v3[29] == 49 && v6 == v3[30] && v3[31] == 48 ) { printf("flag{%s}\n", v3); } } } } } else { printf("Usage: %s pass", *argv); } return 0;}
很简单的代码逻辑,v3就是flag字符串,32位,根据主函数分析出字符串内容,但是v3[10]、v3[11]、v3[20]、v3[21]、v3[22]、v3[24]、v3[25]、v3[30]的值不知道。
分析出来字符串:36be2442ad 49105302 8 cf21 0
剩下的字符串只能去内存中找了,祭出OD,动调一下
编辑
image-20220715161808477
在内存中挨个找出缺少的字符串,然后拼接成flag:36be2442adda49105302dae8edcf21a0
flag{36be2442adda49105302dae8edcf21a0}
0x05 隐秘的角落
DASCTF2022.07赋能赛
拿到程序还是先运行一下
编辑
image-20220726233736856
程序是go写的,找到主函数main_main
void __cdecl main_main(){ __int64 v0; // rdi __int64 v1; // rsi __int64 v2; // r8 __int64 v3; // r9 __int64 v4; // [rsp+8h] [rbp-88h] _QWORD *v5; // [rsp+8h] [rbp-88h] _QWORD *v6; // [rsp+50h] [rbp-40h] _QWORD v7[2]; // [rsp+58h] [rbp-38h] BYREF _QWORD v8[2]; // [rsp+68h] [rbp-28h] BYREF __int64 v9[2]; // [rsp+78h] [rbp-18h] BYREF sync___ptr_WaitGroup__Add((__int64)&main_wg, 1LL); runtime_newobject((__int64)&unk_4B0DA0, v4); v6 = v5; v9[0] = (__int64)&unk_4B0DA0; v9[1] = (__int64)&off_4E9BB0; // hi,ctfer. give me a flag: fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v9, 1LL); v8[0] = &unk_4AE9C0; v8[1] = v6; fmt_Fscanf( v0, v1, (const char *)&go_itab__os_File_io_Reader, (__int64)v8, v2, v3, (__int64)&go_itab__os_File_io_Reader, os_Stdin, (__int64)"%s", 2LL, (__int64)v8, 1LL, 1); runtime_newproc(0x10u, (char)&checkflag, *v6); v7[0] = &unk_4B0DA0; v7[1] = &off_4E9BC0; // Who am I? where am I? what am I doing? fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v7, 1LL); sync___ptr_WaitGroup__Wait((__int64)&main_wg);}
针对go中的一些函数不太清楚,但是这个程序的主函数并不复杂,通过刚才运行程序时所出现的字符串加上主函数进行分析,应该关键点就在第36行checkflag函数中,跟进看一下
void __golang main_checkflag(__int64 a1, __int64 a2){ char v2; // al __int64 v3; // [rsp+18h] [rbp-70h] char v4; // [rsp+18h] [rbp-70h] __int64 v5; // [rsp+20h] [rbp-68h] __int64 v6; // [rsp+28h] [rbp-60h] __int64 v7; // [rsp+30h] [rbp-58h] char v8[32]; // [rsp+40h] [rbp-48h] BYREF _QWORD v9[2]; // [rsp+60h] [rbp-28h] BYREF _QWORD v10[2]; // [rsp+70h] [rbp-18h] BYREF v3 = runtime_stringtoslicebyte((__int64)v8, a1, a2); main_Myencode(v3); if ( v5 == byte_55EA78 ) { runtime_memequal((__int64)main_enc, v3, byte_55EA78, v3); v2 = v4; } else { v2 = 0; } if ( v2 ) { v10[0] = &unk_4B0DA0; v10[1] = &off_4E9B90; // Yes,flag is: DASCTF{md5(Input)} fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v10, 1LL, 1LL, v6, v7); } else { v9[0] = &unk_4B0DA0; v9[1] = &off_4E9BA0; // No,Did you find me? fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v9, 1LL, 1LL, v6, v7); } sync___ptr_WaitGroup__Add((__int64)&main_wg, -1LL);}
跟进继续分析发现,flag的值为输入值的md5
checkflag函数的内容也比较简单,关键点在于第14行main_Myencode函数,这个函数当时我在做这道题目的时候就简单看了看,没太注意,导致漏掉了关键的地方,跟进main_Myencode函数
__int64 __usercall main_Myencode@<rax>(__int64 a1, __int64 a2){ __int64 v3; // [rsp+18h] [rbp-50h] __int64 v4; // [rsp+20h] [rbp-48h] char v5[32]; // [rsp+38h] [rbp-30h] BYREF __int64 v6; // [rsp+58h] [rbp-10h] v6 = runtime_makeslice((__int64)&unk_4B0EE0, a2, a2); v3 = runtime_stringtoslicebyte((__int64)v5, (__int64)main_enc_key, qword_55E898); crypto_rc4_NewCipher(v3, v4); crypto_rc4___ptr_Cipher__XORKeyStream(v3, v6, a2, a2, a1, a2); return a2;}
从里面调用的函数名可以知道这是RC4算法,那么我们就需要找到密文和key,key在main_Myencode函数中第六行main_enc_key,找到key的值为thisiskkk
编辑
image-20220810135312238
这里有两种方式找到密文,第一种就是通过静态分析:
可以通过checkflag函数找到main_enc
编辑
image-20220810135344191
跟进unk_54df80,但是这里的数据还不是真正的密文,是加密前的数据
编辑
真正的密文要跟进main_inti_0函数,查看加密算法
signed __int64 __usercall main_init_0@<rax>(){ _BYTE *v0; // rdx signed __int64 v1; // rbx signed __int64 result; // rax v0 = main_enc; v1 = *(_QWORD *)&byte_55EA78; for ( result = 0LL; result < v1; ++result ) { if ( (unsigned __int64)result >= *(_QWORD *)&byte_55EA78 ) runtime_panicIndex(); *((_BYTE *)main_enc + result) = v0[result] ^ 0x23; } return result;}
可以看到密文是和0x23进行xor后的数据,直接写脚本xor一下得到密文。
第二种方式是直接通过动调得到密文,密文所在位置是0x54df80
可以直接用gdb,在下一条命令的位置下断点,然后跳到0x54df80的位置查看内存信息
image-20220810105504148
运行到断点处,jump一下
image-20220810105558348
得到密文
编辑
image-20220810105616179
这里知道了密文和key,网上找了个解密脚本改了改
key = 'thisiskkk'data = [0xFB, 0xC6, 0xA6, 0x9D, 0xC4, 0xDB, 0x7B, 0x56, 0xB6, 0x46,0xA6, 0xC0, 0x85, 0x64, 0x7A, 0x9A, 0x37, 0x4C, 0x10, 0x96,0xE9, 0xA7, 0x28, 0xC4, 0xB1, 0x2D, 0xF1, 0xDE, 0x47, 0x3B,0xB5, 0xF3, 0x2C, 0x7D, 0x67, 0x1D]s = [0] * 256for i in range(256) : s = iprint(s)j = 0for i in range(256) : j = (j + s + ord(key[i % len(key)])) % 256 print(j) s, s[j] = s[j], si = 0j = 0res = ""for c in data : i = (i + 1) % 256 j = (j + s) % 256 s, s[j] = s[j], s res = res + chr(c ^ s[(s + s[j]) % 256])print(res)#56e83694-f976-11eb-b343-faffc201c8e0
编辑
image-20220810111540060
后开大佬给说了一下在线解密网站也可以解出
编辑
image-20220810132128630
DASCTF{9e1963bbbb1285b993c862a5a6f12604}
|
|