本文主要是介绍[2019红帽杯]easyRE,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
首先ida64打开,shift+f12查看字符串,发现有you found me字段,跟进查看调用了它的函数。
发现是sub_4009C6()函数,分析
__int64 sub_4009C6()
{__int64 result; // raxint i; // [rsp+Ch] [rbp-114h]__int64 v2; // [rsp+10h] [rbp-110h]__int64 v3; // [rsp+18h] [rbp-108h]__int64 v4; // [rsp+20h] [rbp-100h]__int64 v5; // [rsp+28h] [rbp-F8h]__int64 v6; // [rsp+30h] [rbp-F0h]__int64 v7; // [rsp+38h] [rbp-E8h]__int64 v8; // [rsp+40h] [rbp-E0h]__int64 v9; // [rsp+48h] [rbp-D8h]__int64 v10; // [rsp+50h] [rbp-D0h]__int64 v11; // [rsp+58h] [rbp-C8h]char v12[13]; // [rsp+60h] [rbp-C0h] BYREFchar v13[4]; // [rsp+6Dh] [rbp-B3h] BYREFchar v14[19]; // [rsp+71h] [rbp-AFh] BYREFchar v15[32]; // [rsp+90h] [rbp-90h] BYREFint v16; // [rsp+B0h] [rbp-70h]char v17; // [rsp+B4h] [rbp-6Ch]char v18[72]; // [rsp+C0h] [rbp-60h] BYREFunsigned __int64 v19; // [rsp+108h] [rbp-18h]
v19 = __readfsqword(0x28u);qmemcpy(v12, "Iodl>Qnb(ocy", 12);v12[12] = 127;qmemcpy(v13, "y.i", 3);v13[3] = 127;qmemcpy(v14, "d`3w}wek9{iy=~yL@EC", sizeof(v14));memset(v15, 0, sizeof(v15));v16 = 0;v17 = 0;sub_4406E0(0LL, v15, 37LL);v17 = 0;if ( sub_424BA0(v15) == 36 ){for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i ){if ( (unsigned __int8)(v15[i] ^ i) != v12[i] ){result = 4294967294LL;goto LABEL_13;}}sub_410CC0("continue!");memset(v18, 0, 65);sub_4406E0(0LL, v18, 64LL);v18[39] = 0;if ( sub_424BA0(v18) == 39 ){v2 = sub_400E44(v18);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);v11 = sub_400E44(v10);if ( !(unsigned int)sub_400360(v11, off_6CC090) ){sub_410CC0("You found me!!!");sub_410CC0("bye bye~");}result = 0LL;}else{result = 4294967293LL;}}else{result = 0xFFFFFFFFLL;}
LABEL_13:if ( __readfsqword(0x28u) != v19 )sub_444020();return result;
}
首先该函数对v12,v13,v14,v15进行了初始化操作,然后sub_4406E0是一个相当于gets的函数,将用户输入的字符串保存到v15中
接下来可以看出sub_424BA0函数是一个相当于strlen的函数,判断输入的字符串长度是否等于36,如果是再进行下一步操作,后续的you found me 输出刚好是在这第一个if条件里面,所以首先flag长度为36,具体是什么再继续进行分析。
下面for循环里面的if条件是一个关键,如果v15里面有一个字符异或i之后和v12里对应的字符不同,就会进行goto 操作,然后结束这个函数。
if ( sub_424BA0(v15) == 36 ){for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i ){if ( (unsigned __int8)(v15[i] ^ i) != v12[i] ){result = 4294967294LL;goto LABEL_13;}}
LABEL_13:if ( __readfsqword(0x28u) != v19 )sub_444020();
flag要满足的条件之2就是,v15[i] ^ i == v12[i], v12已经给出
但是具体 i 的范围是0~strlen(v15), 也就是说 0<= i < 36,而v12长度只有13,但是注意到这三个数组是连续存放的,所以再访问的时候应该也是连续访问,所以实际上就是三个数组合在一起成为目标数组。
char v12[13]; // [rsp+60h] [rbp-C0h] BYREF
char v13[4]; // [rsp+6Dh] [rbp-B3h] BYREF
char v14[19]; // [rsp+71h] [rbp-AFh] BYREF
按照逻辑,可以编写python脚本:
hint = []
str1 = "Iodl>Qnb(ocy"
for i in range(len(str1)):hint.append(ord(str1[i]))
hint.append(127)
str2 = "y.i"
for i in range(len(str2)):hint.append(ord(str2[i]))
hint.append(127)
str3 = "d`3w}wek9{iy=~yL@EC"
for i in range(len(str3)):hint.append(ord(str3[i]))
str = ""
for i in range(len(hint)):str += chr(hint[i] ^ i)
print(str)
结果是
提示说flag的前四位是flag。。。。。
关于v12,v13,v14三个数组是连续的, ida+Ubuntu动态调试证明一下
首先先在调用数组前下断点
进程停留在这里
可以看出这里是连续执行了36次mov操作,将存储在v12,v13,v14中的所有字符都放在了连续的地址中
那么在循环异或的时候,当i大于了v12的数组长度时,实际上取到的值就是v13的元素,类推。
而后是sub_400E44函数被调用了十次,双击查看发现函数的功能是base64编码。
也就是v18被base64编码了10次,最终的结果储存在v11中
然后是if判断,调用了sub_400360函数,是一个类似于strcmp的函数,如果off_6CC090和v11的所有元素都相等,就输出you found me
也就是找到了flag。
然后就是写脚本解码
import base64
lis = "Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ=="
for i in range(10):lis = base64.b64decode(lis)
print(lis)
最后得到的结果是一个网址。。。。
看来是被坑了,这里面应该没有想要的flag
参考WP,发现程序在正常执行后要执行在这里的sub_400D35函数:
跟进查看:
unsigned __int64 sub_400D35() {unsigned __int64 result; // raxunsigned 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 = sub_43FD20(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 ^ byte_6CC0A0[0]) == 102 && (HIBYTE(v4) ^ (unsigned __int8)byte_6CC0A3) == 103 ){for ( j = 0; j <= 24; ++j )sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v4 + j % 4)));}result = __readfsqword(0x28u) ^ v5;if ( result )sub_444020();return result; }
关键代码区域是第二十行那里的if和for循环,第一个if判断, 可以根据flag和已知数组反向解出v1
后边的for循环可以根据已知数组和v4解出flag
这就是已知数组:
v1 = '' enc1 = 'flag' flag = '' str = [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] for i in range(4):v1 += chr(str[i] ^ ord(enc1[i])) print (v1) for i in range(len(str)):flag += chr(str[i] ^ ord(v1[i % 4])) print(flag)
str里是已知数组对应的ASCII码16进制数,变量v1中存放的就是它的地址,然后将其赋值给v4后再在for循环中进行引用。
(&v4 + j % 4也就是v4[j % 4])参考C语言运算符优先级, % > + > &
最后的输出是
也就是说最终结果为
flag{Act1ve_Defen5e_Test}
这篇关于[2019红帽杯]easyRE的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!