安全矩阵

 找回密码
 立即注册
搜索
查看: 827|回复: 0

比赛中遇到的一些简单的逆向题目

[复制链接]

252

主题

252

帖子

1307

积分

金牌会员

Rank: 6Rank: 6

积分
1307
发表于 2022-10-11 21:40:34 | 显示全部楼层 |阅读模式
原文链接:比赛中遇到的一些简单的逆向题目


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}



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-28 16:32 , Processed in 0.021429 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表