1 signed __int64 __fastcall main(__int64 a1, char **a2, char **a3) 2 { 3 signed __int64 result; // rax 4 char s; // [rsp+0h] [rbp-110h] 5 unsigned __int64 v5; // [rsp+108h] [rbp-8h] 6 7 v5 = __readfsqword(0x28u); 8 printf("Enter the password: ", a2, a3); 9 if ( !fgets(&s, 255, stdin) ) 10 return 0LL; 11 if ( (unsigned int)sub_4006FD((__int64)&s) ) 12 { 13 puts("Incorrect password!"); 14 result = 1LL; 15 } 16 else 17 { 18 puts("Nice!"); 19 result = 0LL; 20 } 21 return result;
使用exeinfope看到是Ubuntu文件,简单题就懒得开虚拟机了,拖入IDA发现是64位,再丢进IDA64,找到main函数,生成c代码,发现是密码题。阅读代码:
使用fget读取键盘输入的字符串s,然后将地址传入sub_4006FD函数中,根据返回值进行输出。
问题的重点在于sub_4006FD的执行逻辑。
1 signed __int64 __fastcall sub_4006FD(__int64 a1) 2 { 3 signed int i; // [rsp+14h] [rbp-24h] 4 const char *v3; // [rsp+18h] [rbp-20h] 5 const char *v4; // [rsp+20h] [rbp-18h] 6 const char *v5; // [rsp+28h] [rbp-10h] 7 8 v3 = "Dufhbmf"; 9 v4 = "pG`imos"; 10 v5 = "ewUglpt"; 11 for ( i = 0; i <= 11; ++i ) 12 { 13 if ( (&v3)[i % 3][2 * (i / 3)] - *(char *)(i + a1) != 1 ) 14 return 1LL; 15 } 16 return 0LL; 17 }
可以看到,该循环进行了12次,可以确定flag为12位。13行的(&v3)[i % 3][2 * (i / 3)] 为一个二维数组的形式,结合v3,v4,v5连续三个变量且长度相等,可以猜测v3,v4,v5是一个长7宽3的数组。
a1即上文提到的s的地址,每轮循环s就+i,即每轮循环就读取字符串中的下一个字符,类似于s++的效果。转换成char指针,然后再取值,取回的值即你输入的s的值,很有意思的写法。
所以这个表达式的意图是:
[数组元素] - (你输入的字符的ASCII码) != 1
等于1则输出Incorrect Password,所以只要上式等于1即可。本题重点在于求出输入的字符串,让以上算法结果为1。
运用我们小学二年级学过的数学知识,将上式等号左右平移:
(你输入的字符的ASCII码)= [数组元素] - 1
python:(网上嫖的,在这里感谢csdn)
array2 = ['Dufhbmf', 'pG`imos', 'ewUglpt'] result = '' for i in range(12): result += chr(ord(array2[i%3][2*(int(i/3))]) - 1) print(result)
cpp:
#include <iostream> using namespace std; int main() { char result[13] = ""; char array[][8] = { "Dufhbmf", "pG`imos", "ewUglpt" }; int i = 0; for (i = 0; i <= 11; i++) { int temp = (int)array[i % 3][2 * (int)(i / 3)] - 1; result[i] = (char)temp; } cout << result << endl; }
(ps:fgets这个函数有知道的必要,在CTF中经常见到
库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
fgets(char *str, int n, FILE *stream)
参数
str – 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
n – 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。
)
在cpp编写中遇到的小bug,flag长度为12,没细想就把result长度设置为12,运行时发现在flag后面有一串乱码,问了旁边大佬之后得知是数组最后一位‘\0’溢出之后随机分配地址,显示的是随机分配的地址中的内容。cpp对于数组溢出挺友好的,起码让我跑起来了,感谢cpp。
浙公网安备 33010602011771号