• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
M1nercy
博客园    首页    新随笔    联系   管理    订阅  订阅
2021.03.24_Reverse_xCTF_Reversing-x64Elf-100_WriteUp
 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。

posted on 2021-03-24 20:45  M1nercy  阅读(107)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3