寒夜破晓,冬至终章 题目:TreRe

发布于:2025-09-08 ⋅ 阅读:(26) ⋅ 点赞:(0)

分析如下

int __fastcall main(int argc, const char **argv, const char **envp)
{
  unsigned __int64 index; // rbx
  __int64 userInputLen_and_count; // rax
  char *Malloc_61uLL; // rax
  char *ptr_user_input_index1; // r10
  __int64 v7; // r11
  char *ptr_Malloc_61ull; // r8
  _BYTE *res; // r9
  unsigned __int8 user_input_first_element; // dl
  unsigned __int64 v11; // rax
  int v12; // ecx
  __int64 v13; // rax
  unsigned int v14; // edx
  __int64 v15; // rdx
  int toXorIndex; // r10d
  __int64 v17; // rax
  unsigned __int64 check_count; // rax
  const char *v19; // rdx
  char str_arry[64]; // [rsp+20h] [rbp-E0h] BYREF
  char user_input[256]; // [rsp+60h] [rbp-A0h] BYREF

  strcpy(str_arry, "M@ASL3MF`uL3ICT2IhUgKSD2IeDsICH7Hd26HhQgKSQhNCX7TVL3UFMeHi2?");
  Out_Microsoft("input your flag:", argv, envp);
  scanf_Microsoft_0("%s", user_input);
  index = -1LL;
  userInputLen_and_count = -1LL;
  do
    ++userInputLen_and_count;
  while ( user_input[userInputLen_and_count] );
  if ( userInputLen_and_count == 44 )
  {
    Malloc_61uLL = (char *)malloc(61uLL);
    ptr_user_input_index1 = &user_input[1];
    v7 = 15LL;
    ptr_Malloc_61ull = Malloc_61uLL;
    Malloc_61uLL[60] = 0;
    res = Malloc_61uLL + 2;
    do
    {
      user_input_first_element = *(ptr_user_input_index1 - 1);
      res += 4;
      ptr_user_input_index1 += 3;
      *(res - 6) = base64table[(unsigned __int64)user_input_first_element >> 2];
      v11 = (unsigned __int8)*(ptr_user_input_index1 - 3);
      v12 = v11 & 0xF;
      v13 = (16LL * (user_input_first_element & 3)) | (v11 >> 4);
      v14 = (unsigned __int8)*(ptr_user_input_index1 - 2);
      *(res - 5) = base64table[v13];            // 换表的base64
      *(res - 4) = base64table[((unsigned __int64)v14 >> 6) | (unsigned int)(4 * v12)];
      *(res - 3) = base64table[v14 & 0x3F];
      --v7;
    }
    while ( v7 );
    v15 = 0LL;
    toXorIndex = 0;
    ptr_Malloc_61ull[59] = 61;
    v17 = -1LL;
    do
      ++v17;
    while ( ptr_Malloc_61ull[v17] );
    if ( v17 )
    {
      res = ptr_Malloc_61ull;
      do
      {
        *res++ ^= 2u;                           // 变种base64后,每个字符和2异或
        ++toXorIndex;
        check_count = -1LL;
        do
          ++check_count;
        while ( ptr_Malloc_61ull[check_count] );// 检测是否处理完全部字符
      }
      while ( toXorIndex < check_count );
    }
    LOBYTE(res) = 0;                            // 把0赋值给res的低字节
    do
      ++index;                                  // index从0开始
    while ( str_arry[index] );
    if ( index )
    {
      while ( ptr_Malloc_61ull[v15] == (unsigned __int8)str_arry[v15] )// v15从零开始,这里也是验证加密后的成果
      {
        LOBYTE(res) = (_BYTE)res + 1;           // 逐字节比较
        v15 = (char)res;
        if ( (char)res >= index )               // 上面有个while,index自动停止到,strarry读取结束,成立跳转
          goto LABEL_19;
      }
      v19 = "\nWrong! Try again!";
    }
    else
    {
LABEL_19:
      v19 = "\nYes you are right";              // 所以思路就很简单了,异或+base64换表
    }
    sub_7FF628A41330(
      std::cout,
      v19,
      ptr_Malloc_61ull,
      res,
      *(_QWORD *)str_arry,
      *(_QWORD *)&str_arry[8],
      *(_QWORD *)&str_arry[16],
      *(_QWORD *)&str_arry[24],
      *(_QWORD *)&str_arry[32],
      *(_QWORD *)&str_arry[40],
      *(_QWORD *)&str_arry[48],
      *(_QWORD *)&str_arry[56]);
  }
  else
  {
    Out_Microsoft("\nLength wrong. Must be 44");
  }
  return 0;
}

我们从上往下看,如下,说明str_arry是一个char array数组类型,所以我们重新命名str_arry的类型为char star_arrt[64]

​​​​​​​strcpy(str_arry, "M@ASL3MF`uL3ICT2IhUgKSD2IeDsICH7Hd26HhQgKSQhNCX7TVL3UFMeHi2?");
Out_Microsoft("input your flag:", argv, envp);

如上,我们跟进这个函数

int Out_Microsoft(const char *const Format, ...)
{
  FILE *stream; // rbx
  unsigned __int64 *options; // rax
  va_list va; // [rsp+58h] [rbp+10h] BYREF

  va_start(va, Format);
  stream = _acrt_iob_func(1u);
  options = (unsigned __int64 *)get_options();
  return _stdio_common_vfprintf(*options, stream, Format, 0LL, va);
}

这里面主要设计的是微软对printf函数的底层封装,详细内容见下面

### 获取标准输出流的文件指针(微软)

`_acrt_iob_func`:通常出现在 Windows 平台(尤其是使用 MSVC 编译器)的 C/C++ 程序中,**获取标准 I/O 流(stdin、stdout、stderr)对应的 `FILE` 结构体指针**。

其参数如下:

* `0u`:对应标准输入流(`stdin`)
* `1u`:对应标准输出流(`stdout`)
* `2u`:对应标准错误流(`stderr`)

其返回值就是`FILE` 结构体指针



**示例应用**:
获取 `v2`(标准输出流指针)后,通常用于文件操作函数,例如:

```c
v2 = _acrt_iob_func(1u); 
// 向标准输出流(控制台)写入内容
fprintf(v2, "Hello, stdout!\n"); // 等价于 printf("Hello, stdout!\n");
// 刷新输出缓冲区
fflush(v2); // 等价于 fflush(stdout);
```

### 微软实现printf系列的底层函数

```c
int __cdecl _stdio_common_vfprintf(
    unsigned __int64 options,      // 控制行为的 flag
    FILE *stream,                  // 输出目标流
    char const *format,            // 格式化字符串
    _locale_t locale,              // 本地化(通常传 0)
    va_list args                   // 可变参数列表
);
```

微软在实现时会把 `printf` / `fprintf` 都重定向到它。

`options` 是一个 64 位的位掩码(bitmask/flags),用来控制函数的不同行为,微软文档里对 `options` 的表述是“修改函数行为的选项”。但没有具体公布示例。

我们需要关注的只有如下
 

stream = _acrt_iob_func(1u);
  • 0u:对应标准输入流(stdin

  • 1u:对应标准输出流(stdout

  • 2u:对应标准错误流(stderr

所以可以确定,下面这个就是输入流了

scanf_Microsoft_0("%s", user_input);
  userInputLen_and_count = -1LL;
  do
    ++userInputLen_and_count;
  while ( user_input[userInputLen_and_count] );
  if ( userInputLen_and_count == 44 )

从这个可以判断处我们需要输入的就是44字节长的flag

      ptr_user_input_index1 += 3;
      *(res - 6) = base64table[(unsigned __int64)user_input_first_element >> 2];
      v11 = (unsigned __int8)*(ptr_user_input_index1 - 3);
      v12 = v11 & 0xF;
      v13 = (16LL * (user_input_first_element & 3)) | (v11 >> 4);
      v14 = (unsigned __int8)*(ptr_user_input_index1 - 2);
      *(res - 5) = base64table[v13];            // 换表的base64
      *(res - 4) = base64table[((unsigned __int64)v14 >> 6) | (unsigned int)(4 * v12)];
      *(res - 3) = base64table[v14 & 0x3F];

从四个*(res-xx),和与0x3F以及base64table的跟进,我们可以推定这个就是base64的换表。

    if ( v17 )
    {
      res = ptr_Malloc_61ull;
      do
      {
        *res++ ^= 2u;                           // 变种base64后,每个字符和2异或
        ++toXorIndex;
        check_count = -1LL;
        do
          ++check_count;
        while ( ptr_Malloc_61ull[check_count] );// 检测是否处理完全部字符
      }
      while ( toXorIndex < check_count );
    }

如上面和下面代码,就是又进行了一个异或,然后验证字符,至此逻辑就很简单了,就是我们提取字符M@ASL3MF`uL3ICT2IhUgKSD2IeDsICH7Hd26HhQgKSQhNCX7TVL3UFMeHi2?进行异或,和换表base64就出来了。

    while ( ptr_Malloc_61ull[v15] == (unsigned __int8)str_arry[v15] )// v15从零开始,这里也是验证加密后的成果
      {
        LOBYTE(res) = (_BYTE)res + 1;           // 逐字节比较
        v15 = (char)res;
        if ( (char)res >= index )               // 上面有个while,index自动停止到,strarry读取结束,成立跳转

至于换表base64我们可以使用python如下

import base64

encoded = "zCN7zTJJP3hj71C3Bxsj72susnhQ="
old = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
new = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/"

mapper = str.maketrans(new, old)
tmp = encoded.translate(mapper)
flag = base64.b64decode(tmp)

print(flag.decode())

或者赛博厨子


网站公告

今日签到

点亮在社区的每一天
去签到