week2--RE--刷题记录

week2刷题

1. [Zer0pts2020]easy strcmp

​ 下载后直接IDA打开题目,结构非常的简单,直接看main函数
img

只有一个赤裸裸的strcmp,进去看看有什么操作:
img

此时这个strcmp大概率就是被hook了。

在ELF当中,init会比main先执行,可以看看init里面的函数做了什么:

img

也就是这三个函数:
img

逐个查看,发现在sub_795当中hook的strcmp:
img

而hook函数就是sub_6EA,接下来看看这个函数怎么回事:

__int64 __fastcall sub_6EA(__int64 a1, __int64 a2)
{
  int i; // [rsp+18h] [rbp-8h]
  int v4; // [rsp+18h] [rbp-8h]
  int j; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; *(_BYTE *)(i + a1); ++i )
    ;
  v4 = (i >> 3) + 1;
  for ( j = 0; j < v4; ++j )
    *(_QWORD *)(8 * j + a1) -= qword_201060[j];
  return qword_201090(a1, a2);
}

​ 这里就是先进行了 - qword_201060的加密,然后调用原来的strcmp,这里是将输入的字符串,每8个字节减去qword_201060当中的一个数,最后得到最开始的strcmp的字符串,知道这个就能直接写exp了,这里使用C/C++写比较好,python对这种整型类型处理不太方便,容易出错:

#include <stdio.h>
int main()
{
    char enc[] = "zer0pts{********CENSORED********}";
    unsigned long long key[] = {0, 0x410A4335494A0942, 0x0B0EF2F50BE619F0, 0x4F0A3A064A35282B};
    for(int i = 0; i < 4;i++)
    {
        ((unsigned long long*)enc)[i]  = ((unsigned long long*)enc)[i] + key[i];
    }
    for(int i = 0;i < 32;i++)
    {
        printf("%c",enc[i]);
    }
}

最后解出flag为:zer0pts{l3ts_m4k3_4_DETOUR_t0d4y}

2. [buuctf] findKey

​ 这题是我认为比较有意思的一道题,首先下载下来的题目运行一下,是一个win32写的题目:
img
img

一共有两个对话框,然后有一个是在help当中的About选项当中。这个时候还不知道要怎么解这个题。那么直接IDA打开题目:
img

很快就找到了主对话框的回调函数,不过加了花指令,需要整理一下才能够反编译,反编译之后是这样的:
img

img

方法一:

​ 分为了上下两个部分,先来看上部分的if,这里就是如果窗口接收到0x111,也就是WM_COMMAND的消息就进入if。517和520分别是0x2050x208,对应的消息是鼠标左键抬起鼠标右键抬起,如果接收到0x205就进入String1的判断流程,具体判断流程如下:

  1. 判断String1是否为空

  2. 不为空则将String1丢到sub_40101E函数当中运算,经过分析为md5

  3. 接着将字符串

    "0kk`d1a`55k222k2a776jbfgd`06cjjb"
    

    SS进行sub_401005的运算,这里其实是xor

  4. String1加密后的值与上面字符串xor后的字符串进行比较,如果不正确则弹出对话框

  5. 如果正确则使用sub_401005将求出的flag输出出来。(这里的v18String1原来的值,而v10为密文)

经过上面的推断已经可以写出exp解出脚本了,
img
img

这里解出String1的值就是123321。然后再使用python解就能解出flag

enc = bytes.fromhex("57 5E 52 54 49 5F 01 6D 69 46 02 6E 5F 02 6C 57 5B 54 4C")
key = b"123321"
for i in range(len(enc)):
    print(chr(enc[i]^key[i % 6]),end="")
  
# flag{n0_Zu0_n0_die}

​ 到这一步就已经解出来了。

方法二:

​ 有趣的部分并不在上面过程,对于整个程序的逻辑也没有理清楚。String1还不知道是从哪里来的,再来继续往下分析。看到下面的部分
img

这里当消息当中的wParam为104的时候会创建一个Dlg,那么很容易就能猜到是这个About对话框了,而这个DialogFunc就是这个About对话框的回调函数,跟进分析一下:

img

​ 整个回调函数逻辑很简单,就是判断接收到的消息是否是对应的消息,如果是对应的消息就进行相应的操作,而这里的0x202,0x205,0x208,分别就是左键抬起,右键抬起,中键抬起。然后给String1赋值,原来String1是从这里来到。而这个程序算是用鼠标事件激活的一个CrackMe,通过使用鼠标事件来将String1拼接成123321,然后当作输入解出flag,所以我们可以通过这个方法来解出flag,脚本如下:

void decrypt()
{
    HWND hWnd = FindWindow(NULL, TEXT("find flag"));
    if (!hWnd)
    {
        printf("[+] : 没有找到指定窗口\r\n");
        return;
    }
    printf("[+] : 已经找到find flag窗口\r\n");
    printf("[+] : find flag窗口句柄 = %x\r\n", hWnd);
    ::PostMessageA(hWnd, 0x111, 0x68, 0);   // 打开about
    printf("[+] : 打开About成功\r\n");
    Sleep(5000);

    HWND hAbout = FindWindow(TEXT("#32770"), TEXT("About"));
    if (!hAbout)
    {
        printf("[+] : 没有找到指定窗口\r\n");
        return;
    }
    printf("[+] : 已经找到About窗口\r\n");
    printf("[+] : About窗口句柄 = %x\r\n", hAbout);
    
    printf("[+] : 即将发送消息.....\r\n");
    UINT msg[] = { 0x202,0x208,0x205,0x205, 0x208 ,0x202 };
    for (int i = 0; i < 6; i++)
    {

        ::SendMessageA(hAbout, msg[i], 0, 0);
        //Sleep(500);
        printf("[+] : 已向子窗口发送消息 %x\r\n", msg[i]);
    }
    ::SendMessageA(hAbout, 0x111, 1, 0);
    printf("[+] : About窗口处理完成\r\n");

    ::SendMessageA(hWnd, 0x205, 50, 50);
    printf("[+] : 开始校验...");
}

​ 这个脚本要注意的是,最后向主窗口发送0x205的时候,lParam需要包含鼠标的X和Y坐标,如果直接填0,则设置成(0,0)。在窗口客户区的左上角,需要移动一下位置才能收到这个消息。

​ 通过脚本向指定窗口发送消息,这样就能够让这个题目直接将flag输出出来了:

img

3. [FlareOn5]Ultimate Minesweeper

​ 这是一道C#的程序,打开之后就是一个扫雷:
img

​ 随机点击一个格子,如果踩到雷就会失败:
img

​ 这个题目可以网上找些挂一把梭了,但是这里不准备这样做。

那么直接使用dnSpy来反编译这个程序,进来就看到一个SuccessPopup的方法了,直接跟过去
img

然后通过分析找出调用SuccessPopUp这个函数的地方:
img

这里可以看到判断是判断了当MineField当中的一个值为0的时候就调用这个成功的函数,而上面的if看名称应该就是踩到炸弹的判断了,进去看看这个雷是专门判断的:
img

可以看到是通过遍历MinesPresentMinesVisible来判断的,而这两个东西猜测就是地图,一个是雷,另一个应该是遮住雷的方块。只有当这两个都为true的时候才算踩到雷,那么换个角度其实就是当true是雷和遮挡,分析一个这两个成员的set的方法,看看在哪里初始化:
img

可以看到是在这个MineField当中被初始化的。那么通过动态调试就可以直接看出这个内存当中的雷在什么位置了。那么就直接启动调试:
img

不过经过调试之后发现,这个雷的map全是false,那就是说明这里只是申请了空间,并没有埋雷,那么就继续步过看看什么时候变成的true:
img

​ 没走两步经过这里之后就看到了MinesPresent当中已经被赋值成true了,那么赋值应该就是在这个AllocateMemory当中赋值的,进入里面看看情况:
img

​ 这个就是给雷区赋值的了,注意这里是将展现给玩家的map,行列互换之后再赋值到MinesPresent当中的,所以MinesPresent找出来的坐标映射到真正的地图上时要交换坐标,其次还需要行列+1才是真正的map的位置,直接断点打在这个GarbageCollect,然后就能找到坐标了:

img

img

img

然后就能通过扫雷解出flag了:
img

选中就是flag了。

posted @ 2025-11-26 18:13  x0rrrr  阅读(5)  评论(0)    收藏  举报