Skip to content

Latest commit

 

History

History
206 lines (154 loc) · 5.85 KB

WriteUp.md

File metadata and controls

206 lines (154 loc) · 5.85 KB

D^3CTF Re jumpjump 解题思路

本题考查点是C语言标准库中setjmplongjmp的逆向识别, 难度低. C++的异常处理就是依赖这两个函数进行实现的.

题目程序是x86_64架构的elf文件, 静态链接, 无符号表, 未加壳.

前置知识

setjmp库是一个类似于跨函数goto语句的实现, 利用jmp_buf来保存一个函数的状态, 在用户setjmp后可以通过longjmp函数快速跳转到setjmp函数所在的位置, 同时支持传递一个整数值. 在第一次setjmp时, 返回值恒为0. 在通过longjmp进行跳转时, setjmp的返回值为longjmp的第二个参数.

题解

将程序拖入到IDA中, 通过字符串窗口可以快速定位到主程序的位置sub_40197C, 同时可以通过程序中的静态字符串得知编译时静态链接的libc版本与发行版包版本, 为glibc 2.33. 通过镜像站或其他途径获得libc之后, 利用rizzo插件还原一部分符号信息. (当然如果熟悉C++异常处理或者setjmp库的底层实现的话也可以手动识别).

pic

pic

简单识别一下main函数里的各个函数:

可以发现程序对输入的flag进行了两次check, 一次是sub_40189D, 另一次利用setjmp设置跳转标志, 然后调用sub_40191E进行验证. 进入sub_40189D, 这里也通过setjmp设置了自动跳转:

_BOOL8 __fastcall check1(char *input, __int64 a2, int a3, int a4, int a5, int a6)
{
  char v7; // [rsp+0h] [rbp-20h]
  int jmp_flag; // [rsp+1Ch] [rbp-4h]

  jmp_flag = j_setjmp((int)&jmp_buf_check1, a2, a3, a4, a5, a6, v7);
  if ( !jmp_flag )
    sub_401825((__int64)input);
  return jmp_flag == 36;
}

查看sub_401825函数:

void __fastcall __noreturn sub_401825(const char* input)
{
  int input_len; // [rsp+1Ch] [rbp-4h]
  int i; // [rsp+1Ch] [rbp-4h]

  input_len = j_strlen_ifunc(input);
  if ( input_len != 36 )
    j_longjmp(&jmp_buf_check1, input_len);
  for ( i = 0; i <= 35; ++i )
    *(_BYTE *)(i + a1) ^= 0x57u;
  j_longjmp(&jmp_buf_check1, 36);
}

这两个函数结合起来就是测试输入长度是否为36. 如果为36则返回true, 否则返回false. 在检测长度的同时对输入数组进行异或操作.

接着看第二个check, 在main函数的过程中是这样的:

  jmp_flag_main = j_setjmp((int)&jmp_buf_main, (__int64)input, v7, v8, v9, v10, v11);
  if ( !jmp_flag_main )
    check2(input, 36LL);
  if ( jmp_flag_main == 1 )
  {
    printf("Sorry.\n");
    exit(0LL);
  }
  printf("Good!\n");
  exit(0LL);

很显然在check2函数以及check2函数所调用的函数中通过跳转jmp_buf_main标志来将检测结果传递回来. 传递值为1时说明结果错误, 传递值为除了0(默认值为0)和1之外的其他值时说明结果正确.

check2函数很简单:

void __fastcall __noreturn check2(__int64 a1, int a2)
{
  unsigned __int64 i; // [rsp+18h] [rbp-8h]

  for ( i = 0LL; i < a2; ++i )
    sub_4018E0((unsigned int)i, (*(char *)(a1 + i) + 4) ^ 0x33u);
  j_longjmp(&jmp_buf_main, 2);
}

__int64 __fastcall sub_4018E0(int a1, int a2)
{
  __int64 result; // rax

  result = dword_4CC100[a1];
  if ( a2 != (_DWORD)result )
    j_longjmp(&jmp_buf_main, 1);
  return result;
}

通过sub_4018E0对每一位进行检测, 如果不对就立即跳回main函数并输出.

主要的逻辑还是异或运算.

解密脚本十分简单, 提取出dword_4CC100数组:

magic = [9,  11, 6,  90, 91, 10, 84, 5,  77, 87, 86, 84,
         11, 77, 84, 9,  85, 64, 77, 9,  6,  89, 11, 77,
         85, 84, 88, 87, 91, 9,  11, 64, 5,  10, 5,  9]
for i in magic:
    print(chr(((i ^ 0x33) - 4) ^ 0x57), end='')
print('')

# acf23b4e-764c-4a58-af1c-54073ac8ebea

验证:

题目源码

// Created by Reverier from L-team, 2021.02.24

// flag:  acf23b4e-764c-4a58-af1c-54073ac8ebea

#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static jmp_buf len_jmp;
static jmp_buf incoming;

static int magic[] = {9,  11, 6,  90, 91, 10, 84, 5,  77, 87, 86, 84,
                      11, 77, 84, 9,  85, 64, 77, 9,  6,  89, 11, 77,
                      85, 84, 88, 87, 91, 9,  11, 64, 5,  10, 5,  9};

// libc version, for rizzo.
static const char* libcs = "GNU C Library (GNU libc) release release version 2.33.\nCopyright (C) 2021 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions.\nThere is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE.\nCompiled by GNU CC version 10.2.0.\nlibc ABIs: UNIQUE IFUNC ABSOLUTE\nFor bug reporting instructions, please see:\n<https://bugs.archlinux.org/>.";

static const char* glibcs = "core/glibc 2.33-4";

void check_len_real(char* v1) {
    int i = strlen(v1);
    if (i != 36) {
        longjmp(len_jmp, i);
    } else {
        for (i = 0; i < 36; i++) {
            v1[i] ^= 0x57;
        }
        longjmp(len_jmp, 36);
    }
}

int check_len(char* v1) {
    int n = setjmp(len_jmp);
    if (!n) {
        check_len_real(v1);
    } else if (n != 36) {
        return 0;
    } else {
        return 1;
    }
}

void real_valid(int i, int n) {
    if (magic[i] != n) {
        longjmp(incoming, 1);
    } else
        return;
}

void valid(char* buf, int n) {
    for (size_t i = 0; i < n; i++) {
        real_valid(i, (buf[i]+4)^0x33);
    }
    longjmp(incoming, 2);
}

int main() {
    char buf[200];

    printf("<<- Welcome to AntCTF & D3CTF! ->>\n\nInput your key: ");
    scanf("%200s", buf);

    if (!check_len(buf)) {
        printf("Sorry.\n\n");
        exit(0);
    }
    int n = setjmp(incoming);
    if (!n) {
        valid(buf, 36);
    } else if (n == 1) {
        printf("Sorry.\n\n");
        exit(0);
    } else {
        printf("Good!\n\n");
        exit(0);
    }

    return 0;
}