当前位置: 首页 > article >正文

CTF Show逆向4reserve wp--mingyue

(本题难度较大,分析起来比较复杂,针对该题本文对其中比较重要的部分做了较详细的概述,有问题的地方,请指正)

第一步    查壳。本题为64位

第二步 各部分函数分析

(一)

分析main函数。

函数签名

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)

  • int __cdecl __noreturn:这表明这是一个main函数,使用__cdecl调用约定,且带有__noreturn修饰符,这表示该函数永远不会返回。这个修饰符通常用于那些程序会在函数结束时终止进程的情况下,比如通过调用exit()或是进入一个无限循环。
  • main(int argc, const char **argv, const char **envp):这是标准的main函数签名,其中:
    • argc是命令行参数的数量。
    • argv是指向参数字符串的指针数组。
    • envp是指向环境变量字符串的指针数组。

变量定义

qword_140004618 = (__int64)malloc(0x10ui64);

qword_140004620 = qword_140004618;

  • qword_140004618 和 qword_140004620 是两个全局变量,可能是64位整型的变量(从名字中的qword可以推测)。具体来看:
    • qword_140004618 = (__int64)malloc(0x10ui64);:调用malloc分配了16字节(0x10ui64)的内存,并将其地址转换为__int64类型,存储在qword_140004618中。
    • qword_140004620 = qword_140004618;:将qword_140004618的值(即刚分配的内存地址)复制到qword_140004620。

内存操作

*(_QWORD *)(qword_140004618 + 8) = 0i64;

  • *(_QWORD *)(qword_140004618 + 8) = 0i64;:这是一个指针操作,具体做了以下事情:
    • qword_140004618 + 8计算了刚刚分配内存块的第8个字节位置。
    • _QWORD代表64位的整数类型,*(...)表示解引用。
    • 这行代码把0存储在内存块的第8到第15字节位置。

也就是说,分配的16字节内存中,第8个字节到第15个字节的位置被设置为0

字符串输出与输入

sub_140001020("请输入正确的数字:\n");

sub_140001080("%lld");

  • sub_140001020("请输入正确的数字:\n");:调用函数sub_140001020,参数是一个字符串 "请输入正确的数字:\n",这个函数可能是用于输出提示信息的,比如让用户输入数字。
  • sub_140001080("%lld");:调用另一个函数sub_140001080,参数为格式化字符串"%lld",这个函数可能是用于接收输入的。"%lld"是用于读取长长整型(long long)的格式说明符。

函数调用

((void (__fastcall *)())sub_1400010E0)();

  • ((void (__fastcall *)())sub_1400010E0)();:这是一个类型转换和调用,分两部分:
    • ((void (__fastcall *)())sub_1400010E0)将sub_1400010E0函数指针转换为一个使用__fastcall调用约定的函数指针,该函数不带参数且返回类型为void。
    • ()紧接在这个类型转换后的指针上,表示立即调用这个函数。

小结一下:

  1. 分配了一段16字节的内存,将其地址保存到两个全局变量qword_140004618和qword_140004620中。
  2. 将分配内存的第8到第15字节位置设置为0。
  3. 输出提示信息要求用户输入一个数字。
  4. 可能通过格式化字符串读取用户输入。
  5. 最后调用了一个sub_1400010E0的函数指针

(二)

分析sub_140001020

函数签名:

int sub_140001020(char *Format, ...)

这个函数名为 sub_140001020,返回类型是 int,接受一个字符指针(char *Format)和可变参数(...)。函数中的 Format 通常用于格式化输出。

局部变量:

FILE *v2; // rbx

unsigned __int64 *v3; // rax

va_list va; // [rsp+58h] [rbp+10h] BYREF

  • v2 是一个指向 FILE 类型的指针,保存在寄存器 rbx 中。
  • v3 是一个指向 unsigned __int64 类型的指针,保存在寄存器 rax 中。
  • va 是一个 va_list 类型的变量,用来处理可变参数列表。

可变参数处理:

va_start(va, Format);

  • va_start 宏初始化 va_list 变量,准备访问可变参数列表。这里的 va 变量将会存储传递给函数的可变参数信息。

获取标准输出流:

v2 = _acrt_iob_func(1u);

  • _acrt_iob_func 是一个微软C运行时库(CRT)中的内部函数,通常用于获取标准输入输出流。
  • _acrt_iob_func(1u) 返回标准输出流 stdout,即 v2 指向标准输出的 FILE 对象。

调用另一个函数:

v3 = (unsigned __int64 *)sub_140001000();

  • sub_140001000 是另一个函数,被调用后返回一个 unsigned __int64 * 类型的指针,并赋值给 v3。这个指针可能指向某种控制结构或特定数据。

执行格式化输出:

return _stdio_common_vfprintf(*v3, v2, Format, 0i64, va);

  • _stdio_common_vfprintf 是一个低级别的格式化输出函数,它负责处理带有可变参数的格式化输出。
  • 这个函数的参数分别为:
    • *v3: 通过解引用 v3 得到的值,这个值可能代表某种标志或格式化选项。
    • v2: 指向标准输出的 FILE 对象。
    • Format: 字符串格式,用于定义如何格式化输出。
    • 0i64: 通常是一个与选项标志相关的值,这里指定为0。
    • va: 处理后的可变参数列表。
  • 函数的返回值是 _stdio_common_vfprintf 的返回值,即该函数成功写入的字符数,或者是遇到错误时的负值。

(三)

分析sub_140001080

  1. 函数声明:

int sub_140001080(char *Format, ...)

  1. 这是一个函数定义,返回类型为int,接受一个格式化字符串Format作为第一个参数,以及一个可变参数列表。

  1. 局部变量声明:

FILE *v2; // rbx

_QWORD *v3; // rax

va_list va; // [rsp+58h] [rbp+10h] BYREF

  1. v2 是一个指向 FILE 结构的指针(通常用于表示文件流)。
  2. v3 是一个 _QWORD 类型的指针,通常 _QWORD 是一个 64 位整数类型,用于存储 8 字节的数据。
  3. va_list va 是一个用于存储可变参数列表的对象。

     3.初始化可变参数列表:

va_start(va, Format);

  1. 使用 va_start 宏初始化 va_list 对象 va,并使其指向 Format 之后的第一个可变参数。

      4.获取标准输入流:

v2 = _acrt_iob_func(0);

  1. _acrt_iob_func(0) 是一个获取标准输入流 stdin 的函数,返回一个指向 FILE 结构的指针,并赋值给 v2

      5.调用非标准库函数:

v3 = (_QWORD *)sub_140001010();

  1. sub_140001010 是一个自定义的函数,返回一个 _QWORD 类型的指针,并将其结果赋值给 v3。该函数的具体实现未知,但返回值的使用表明它可能与格式化输入处理相关。

       6.调用标准库函数:

return _stdio_common_vfscanf(*v3 | 1i64, v2, Format, 0i64, va);

  1. _stdio_common_vfscanf 是一个底层标准输入函数,用于根据提供的 Format 格式从 v2(通常是 stdin)中读取输入,并将结果存储到与可变参数 va 对应的变量中。
  2. *v3 | 1i64 代表对返回的 _QWORD 指针进行位或操作,可能是设置某个标志位(具体用途取决于 sub_140001010 的实现)。

(四)

进入sub_1400010E0(重点)

      1.变量初始化和赋值:

v2 = 0;

v3 = (__int64)a1;

v2被初始化为0v3被赋值为a164位整型值。

      2.条件判断和循环:

if ( a1 )

{

    v4 = &v9;

    do

    {

        ++v4;

        ++v2;

        a1 = &a4890572163qwe[-26 * (v3 / 26)];

        v5 = a1[v3];

        v3 /= 26i64;

        a2 = v3;

        *(v4 - 1) = v5;

    }

    while ( v3 );

}

a1不为空的情况下执行。它初始化v4v9的地址,然后进入一个do-while循环:

  • ++v4;:v4指针递增,指向下一个元素。
  • ++v2;:v2计数器递增。
  • a1 = &a4890572163qwe[-26 * (v3 / 26)];:这里有一个数组a4890572163qwe,a1被重新赋值为该数组的一个元素地址,该元素的索引是通过v3除以26并乘以-26计算得到的。
  • v5 = a1[v3];:v5被赋值为a1指针所指向的数组元素的值。
  • v3 /= 26i64;:v3整除26。
  • a2 = v3;:将v3的值赋给a2。
  • *(v4 - 1) = v5;:将v5的值赋给v4指针前一个位置的元素。

循环继续执行,直到v30

        3.第二个循环:

v6 = v2;

while ( v6 )

{

    v7 = *(&v8 + v6--);

    sub_1400011E0(v7 ^ 7u, a2, v3);

}

这里,v6被赋值为v2,然后进入一个while循环:

  • v7 = *(&v8 + v6--);:v7被赋值为v8偏移v6个位置的元素的值,然后v6递减。
  • sub_1400011E0(v7 ^ 7u, a2, v3);:调用一个名为sub_1400011E0的函数,传入参数为v7与7进行异或操作的结果、a2和v3。

循环继续执行,直到v60

        4.函数调用:

sub_140001220(a1, a2, v3);

最后,调用一个名为sub_140001220的函数,传入参数为a1a2v3

(五)

进入sub_140001220(重点)

  1. 变量初始化:

v0 = qword_140004620;

v1 = 0;

v2 = 0i64;

v0被初始化为全局变量qword_140004620的值,v1v2分别被初始化为0。

       2.无限循环:

while ( 1 )

{

这里是一个无限循环,它将一直执行直到遇到break语句。

      3.循环体内的操作:

v3 = *(_BYTE *)v0;

v4 = v1 + 1;

v5 = *(_QWORD *)(v0 + 8);

  • v3被赋值为v0指向的字节。
  • v4被赋值为v1加1。
  • v5被赋值为v0加8地址处的64位值。

      4.条件判断和赋值:

if ( v3 != aV4pY59[v2 - 1] )

  v4 = v1;

qword_140004620 = v5;

if ( !v5 )

  break;

  • 如果v3不等于数组aV4pY59中v2-1索引处的值,则v4被重新赋值为v1。
  • 更新全局变量qword_140004620为v5的值。
  • 如果v5为0,则退出循环。

      5.更多的循环体内操作:

v6 = *(_BYTE *)v5;

v7 = v4 + 1;

v0 = *(_QWORD *)(v5 + 8);

if ( v6 != aV4pY59[v2] )

  v7 = v4;

qword_140004620 = v0;

  • v6被赋值为v5指向的字节。
  • v7被赋值为v4加1。
  • v0被赋值为v5加8地址处的64位值。
  • 如果v6不等于数组aV4pY59中v2索引处的值,则v7被重新赋值为v4。
  • 更新全局变量qword_140004620为v0的值。

    6.更新循环变量:

if ( v0 )

{

  v2 += 2i64;

  v1 = v7;

  if ( v2 < 14 )

    continue;

}

  • 如果v0不为0,则v2增加2,v1被赋值为v7。
  • 如果v2小于14,则跳过后续代码,继续下一次循环。

     7.无条件跳转:

goto LABEL_11;

如果v00v2等于或大于14,则跳转到标签LABEL_11

     8.循环结束后的操作:

v7 = v4;

LABEL_11:

if ( v7 == 14 )

  sub_1400012E0();

sub_1400012B0();

  • 循环结束后,v7被赋值为v4。
  • 如果v7等于14,则调用函数sub_1400012E0。
  • 不论条件如何,都会调用函数sub_1400012B0。

以上两个数组如下

第三步   编写脚本

分析一下脚本:

1. 变量定义

a4890572163qwe = ")(*&^%489$!057@#><:2163qwe"

aV4pY59 = "/..v4p$!>Y59-"

2. equal 函数

def equal(a, str1):

    for i in range(0, 26):

        if str1[i] == a:

            return i

    return -1

  • equal 函数的作用是:在 str1 的前 26 个字符中找到字符 a 的位置(索引)。如果找到了,就返回这个索引;如果没找到,则返回 -1。
  • 注意,这里假设 str1 至少有 26 个字符,否则可能会引发越界错误。

3. 主体逻辑分析

flag = 0

newarray = []

for i in range(14):

    flag *= 26

 

    if i < len(aV4pY59):

        index = equal(chr(ord(aV4pY59[i]) ^ 7), a4890572163qwe)

        newarray.append(index)

        flag += index

    else:

        break 

  • 初始化 flag 为 0 和 newarray 为空列表。
  • 进入一个循环,循环次数为 14 次。

循环体内的操作:

  • 每次循环开始时,flag 被乘以 26,这意味着 flag 正在逐步扩展为一个 26 进制数。
  • 接下来,通过判断 i < len(aV4pY59),确保不会超出 aV4pY59 的长度。
  • 通过 ord(aV4pY59[i]) ^ 7 对 aV4pY59 中第 i 个字符执行异或操作,得到一个新的字符,然后用 chr() 将其转化为对应的字符。
  • 使用 equal 函数查找这个新字符在 a4890572163qwe 中的位置(索引),并将结果保存到 index 中。
  • 将 index 添加到 newarray 中,同时将 index 累加到 flag 上。
  • 如果 i 达到了 aV4pY59 的长度,则退出循环。

验证成功

flag{2484524302484524302}


http://www.kler.cn/news/288456.html

相关文章:

  • 滑动窗口系列(不定长滑动窗口长度) 9/2
  • 09--kubernetes持久化存储和StatefulSet控制器
  • Ubuntu最新镜像下载,国内镜像源地址大全
  • RocketMQ集群搭建,及RocketMQ-Dashboard部署(前RocketMQ-Console)
  • vscode远程连接服务器并根据项目配置setting.json
  • 四、基本电路设计笔记——4.1 DC-DC稳压电路
  • 【Python123题库】#研究生录取数据分析A #图书数据分析(A)
  • 【算法每日一练及解题思路】判断数字是否为偶数
  • Vue实现步骤条(el-step)+Popover弹出框
  • Oracle 网络安全产品安全认证检索
  • 编程如何塑造我们的世界
  • 安宝特科技 | AR眼镜在安保与安防领域的创新应用及前景
  • 项目管理时间痛点解决百宝箱
  • 2025最新剧本杀服务平台构建攻略,Java SpringBoot+Vue,打造沉浸式用户体验!
  • 【Kubernetes部署篇】二进制搭建K8s高可用集群1.26.15版本(超详细,可跟做)
  • VMware命令
  • python基础语法(二)
  • 微软分享其首款定制人工智能芯片Maia 100的更多细节
  • ssh的小绝招,一般人我不告诉他!ssh免密登陆和第三方踏板登陆内网
  • 【负载均衡】LoadBalance场景演示
  • kafka快速上手
  • React 服务器组件
  • 智能汽车座椅制造:RFID技术助力精密加工与全程追踪
  • Getting an error trying to import environment OpenAI Gym
  • mongodb 时间存储使用Date还是时间戳
  • 【Python机器学习】NLP词频背后的含义——主成分分析
  • 使⽤docker部署project-exam-system(2)
  • [翻译+笔记] 用于视频生成的Diffusion Model
  • codesys进行控制虚拟轴运动时出现的一些奇怪bug的解释
  • 山体滑坡监测预警系统—百科分享