吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2377|回复: 4
收起左侧

[CTF] SUCTF2019_Akira-Homework

[复制链接]
Y1rannn 发表于 2022-1-27 22:31

这道题真的是很难的,....,做完之后上网看了看别的师傅的题解,虽然整体是这个流程,但是细节处理一个人一个方法,有的方法就会简便很多.

不过我这种各种瞎猜的果然还是比较简单(着实是沾了非实战是个做题的光)

做题和实战最大的区别就是做题往往整个程序控制流都和题有关,不会有大量的无关的东西.

看上去又是一道双线程"双手互搏"题.

运行一下发现全程不需要输入,后来发现是第一次我的terminal抽风.

PS E:\ctf\work\SUCTF2019 Akira Homework> .\WinRev.exe
[+]======================[+]
[+] Akira's Homework 2nd [+]
[+]======================[+]
[=] My passwords is:

调用很多WinAPI,还是动调一下. 一动调直接谈了个对话框"Stop debugging program"

屏幕截图 2022-01-27 192049.png

找到对应位置, 扬了就行

屏幕截图 2022-01-27 193801.png

对应位置后面是这样的,把四个数组分别异或一个table中的值,我们直接打个断点看看

屏幕截图 2022-01-27 194210.png

荧光笔打上的地方就是四个字符串
屏幕截图 2022-01-27 194310.png

GetProcAddress的MSDN文档

可以读到就是从ntdll提取这三个模块的地址, 我们继续检查这个函数8850的交叉引用,看看这个地址存在哪里要用来干嘛
屏幕截图 2022-01-27 194646.png

放到了一个堆内存里.这个函数是main的第一个函数.没太能看的出来它想干啥, 但是这三个函数分别是获取进程\线程信息和Apc队列. 没见过这东西,查一下.

大概就是在多线程运行条件下,当某个程序执行到特殊函数造成的等待状态时,系统中断, 本线程优先去APC队列中调用一个Callback.

但是现在没有APC到底注入了什么函数的信息, 翻了翻也没找到, 我们回到main, 去看看第二个线程执行了什么

屏幕截图 2022-01-27 195436.png

第二个线程从StartAddress开始,注意到main的结尾调用了一个SleepEx,这个函数是可以触发APC的

屏幕截图 2022-01-27 195527.png

StartAddress里也同样有一个Sleep, 我们看看都干了啥

__int64 sub_7FF7B0ED8B20()
{
  size_t v1; // rax
  int i; // [rsp+24h] [rbp-304h]
  int j; // [rsp+28h] [rbp-300h]
  BOOL v4; // [rsp+2Ch] [rbp-2FCh]
  int v5; // [rsp+30h] [rbp-2F8h] BYREF
  HANDLE hSnapshot; // [rsp+38h] [rbp-2F0h]
  __int64 v7; // [rsp+40h] [rbp-2E8h] BYREF
  PROCESSENTRY32W pe; // [rsp+50h] [rbp-2D8h] BYREF
  char v9[48]; // [rsp+290h] [rbp-98h] BYREF
  wchar_t String1[40]; // [rsp+2C0h] [rbp-68h] BYREF

  sub_7FF7B0ED8300(&v7, 1i64, 1048577i64);
  memset(&pe, 0, sizeof(pe));
  pe.dwSize = 568;
  hSnapshot = CreateToolhelp32Snapshot(2u, 0);
  if ( hSnapshot == (HANDLE)-1i64 )
    return 0i64;
  v4 = Process32FirstW(hSnapshot, &pe);
  if ( !v4 )
    return 0i64;
  while ( v4 )
  {
    memset(v9, 0, 0x21ui64);
    memset(String1, 0, 0x42ui64);
    v1 = wcslen(pe.szExeFile);
    sub_7FF7B0ED7DD0(pe.szExeFile, v1, v9);
    for ( i = 0; i < 16; ++i )
      sprintf_s((char *const)&String1[2 * i], 3ui64, L"%02x", (unsigned __int8)v9[i]);
    for ( j = 0; (unsigned __int64)j < 0x31; ++j )
    {
      if ( !wcscmp(String1, &a438078d884693c[33 * j]) )
        exit(-1);
    }
    v4 = Process32NextW(hSnapshot, &pe);
  }
  v5 = 106;
  if ( !byte_7FF7B0EE6158 )
  {
    sub_7FF7B0ED6C10(qword_7FF7B0EE6178, v7, &v5);
    byte_7FF7B0EE6158 = 1;
  }
  return 1i64;
}

8300这个函数不明所以,但是后面6C10用到了它的返回值.我们先从可读的地方探一探

这个CreateToolhelp32Snapshot是为了获取一个进程快照,第一个参数代表快照的系统部分,为2代表要包括系统中的所有进程.第二个参数为0代表当前进程

之后Process32First/Next就是遍历这个Snapshot中打下的系统的所有进程. szExeFile是这个遍历到的进程的名称.其实我目前还没有理解这到底是是打下了所有进程还是我当前进程, 我猜测是因为不知道有没有其他进程创建当前进程,所以dump下来系统其他进程的信息, 和当前进程内存信息

屏幕截图 2022-01-27 200317.png

7DD0这个函数可以通过magic number判断是一个md5函数, 没有什么好说的, 最后结果存到a3指向地址里

结合一起猜测这应该也是个反调, 要求不能有进程的md5和固定值符合, 虽然是瞎猜的,但是调试时确实会进到这里, 扬掉

后半段是一些和线程锁相关的东西,感觉是切换线程前的准备

继续看StartAddress的第二个函数

屏幕截图 2022-01-27 201143.png

晕, 这反调多的眼花缭乱的, 这个a1通过交叉引用也找到了位置:正是我们一开始解密的ntdll里的三个函数, 注意到有一个qword_7FF7B0EE6188 = (__int64)v6; 这里用到了这个指针qword

fastcall func2(void (__fastcall **callback)(HANDLE, __int64, _QWORD, _QWORD))

这里的函数声明也可以对应的改成callback了,方便看

这整个函数都不好,看了也没用,直接干掉它的调用了(我在想是不是整个子进程都可以干掉). 再测一下发现main能正常进去了, 我们调一下

屏幕截图 2022-01-27 201826.png

这个exit-1看着就不像好事,猜测9710这个函数要返回true, table[3] 是 0x49, 我们直接看看它会puts啥吧.

擦, 这个进程进去之后会等一会-1退出, 正好我拖IDA的窗口,把IDA干崩了,

unsigned char ida_chars[] =
{
  0x4A, 0x3A, 0x4C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 
  0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 
  0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x4A, 0x3A, 0x4C, 0x1B, 0x4A, 
  0x3A, 0x4C, 0x31, 0x50, 0x7A, 0x78, 0x63, 0x70, 0x36, 0x62, 
  0x31, 0x59, 0x7E, 0x7C, 0x74, 0x66, 0x7E, 0x63, 0x7A, 0x31, 
  0x23, 0x7F, 0x75, 0x31, 0x4A, 0x3A, 0x4C, 0x1B, 0x4A, 0x3A, 
  0x4C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 
  0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 
  0x2C, 0x2C, 0x2C, 0x4A, 0x3A, 0x4C, 0x1B, 0x4A, 0x2C, 0x4C, 
  0x31, 0x5C, 0x68, 0x31, 0x61, 0x70, 0x62, 0x62, 0x66, 0x7E, 
  0x63, 0x75, 0x62, 0x31, 0x78, 0x62, 0x2B, 0x11
};
#include <cstdio>
int main() {
    for(int i = 0; i < 108; i ++ ) {
        putchar(ida_chars[i] ^ 0x11);
    }
}

---
/out:test1.exe 
test1.obj 
[+]======================[+]
[+] Akira's Homework 2nd [+]
[+]======================[+]
[=] My passwords is:

9710干了这件事, 然后应该是读入一个18s, 并扔到9200里判断要不要返回1

如果判断错误会打印WHO ARE YOU????然后退出, 先把9200的结果逆了

  memset(v6, 0, 0x13ui64);
  memcpy(v6, &target1, 0x12ui64);
  for ( i = 0; i < 0x13; ++i )
    v6[i] ^= xor_table[1];
  memset(v7, 0, 0x13ui64);
  for ( j = 0; j < 0x12; ++j )
    v7[j] = j ^ a1[j];
  v5[0] = 1;
  v5[1] = 5;
  v5[2] = 4;
  v5[3] = 2;
  v5[4] = 3;
  v5[5] = 0;
  for ( k = 0; k < 0x12; ++k )
  {
    if ( v6[k] != v7[6 * (k / 6) + v5[k % 6]] )
      return 0;
  }

核心直接摆这儿了,就是一个Xor+轮换,很好逆

#include <cstdint>
#include <cstdio>
uint8_t target1[] =
{
  0x2F, 0x1F, 0x20, 0x2E, 0x34, 0x04, 0x37, 0x2D, 0x10, 0x39, 
  0x7C, 0x22, 0x7B, 0x75, 0x0A, 0x38, 0x39, 0x21
};
uint8_t ans[20];
uint8_t shift[6] = {1,5,4,2,3,0};
int main() {
    for(int i = 0; i < 18; i ++ ) target1[i] ^= 0x45;
    for(int i = 0; i < 18; i ++ ) {
        ans[6*(i/6)+shift[i%6]] = target1[i];
    }
    for(int i = 0; i < 18; i ++ ) {
        ans[i] ^= i;
    }
    printf("%s", ans);
}
// Akira_aut0_ch3ss_!

Akira自走棋是吧

下面还有个6C10用了我们的输入, 走进去看看, 又用了个虚函数, 懒得找表,动调看看在哪

char __fastcall sub_7FF6B9348910(__int64 a1, const char *a2)
{
  int i; // [rsp+20h] [rbp-28h]
  int v4; // [rsp+24h] [rbp-24h]

  v4 = strlen(a2);
  for ( i = 0; i < dword_7FF6B9351194; ++i )
  {
    if ( !(i % 3) )
      byte_7FF6B93511A0[i] ^= a2[i / 3 % v4];
  }
  SetEvent(Handles[0]);
  return 1;
}

把一段长度4C00的内存和我们的输入异或了, 而且这段内存后面还有几次调用,暂时不知道在哪,我们给它改个名叫secret

[=] My passwords is:
Akira_aut0_ch3ss_!
Have no sign!
Time out!

接下来要在有限的时间内sign,不然就exit

屏幕截图 2022-01-27 203859.png

我想找找在哪,首先关注到了main函数最后调用的一个函数,的最末尾,但是这里是另一个failed

Failed to check sign!
屏幕截图 2022-01-27 204047.png

我们回到这个函数的开头,关注另外两个异或解密,

Source的结果是:signature

Buffer是Have no sign!

就是要创建或打开一个$FileName:signature的文件,如果创建或打开不了就no sign, 我猜后面的参数是不创建只打开, 看看MSDN

OPEN_EXISTING3 仅当文件或设备存在时才打开它。如果指定的文件或设备不存在,则函数失败并且最后一个错误代码设置为 ERROR_FILE_NOT_FOUND (2)

正是如此, 所以我们要创建一个这个文件然后过验证

诶人傻了怎么不能带冒号啊...

查了一下这个冒号是CreateFIleW里指定的文件名:文件流, 也就是我们实际上是要创建一个叫signature的流写到源文件里

这里要求流的md5和给定的md5值相同, 然后有一个类似之前的结构调用了第二个虚函数

先给这个MD5解密
屏幕截图 2022-01-27 210808.png

屏幕截图 2022-01-27 210915.png

我尝试直接向WinRev.exe:signature写\用Linux写,都失败了

屏幕截图 2022-01-27 211343.png

先不管了, 先找到了这三个虚函数, 8910是和自走棋异或, 8A80没有参数, 是把它的高低位交换

89e0则是把它和一个参数^0x33再异或,结合这部分调用的虚函数传进去的参数为0, 而且调用在8910的后面,我优先猜测它应该是高低位交换,现在不管文件流,我们要找找89e0在哪里调用的

屏幕截图 2022-01-27 211849.png

其实就是找到这样的结构, 诶诶诶, 其实要是点进去6C10, 会发现这里也有很多和mutex相关的东西,还记得好久以前我说过一个函数和信号量相关,应该没用么?我猜当时是虚函数调用被我忽略了.我们回去找找8B20, 没错,就是这儿,而且当时调用的也是6C10这个函数

屏幕截图 2022-01-27 212250.png

其实三次调用的都是这个,不过第二个参数影响了调用虚函数具体是哪个,结合这个函数出现的位置,我们可以猜测它就是第一个异或固定值的虚函数, 传进去的参数是106, 这样我们应该就把那段长度4C00的内存解密了, 虽然那么长应该不是Flag还没完...

uint s[] = {
    略
}
#include <cstdio>
#include <cstdint>
int main() {
  int len = 19456;
  char s1[] = "Akira_aut0_ch3ss_!";
  for(int i = 0; i < len; i ++ ) if(i % 3 == 1) {
    s[i] ^= 106 ^ 0x33;
  }
  for(int i = 0; i < len; i ++ ) if(i % 3 == 0) {
    s[i] ^= s1[i/3%18];
  }
  for(int i = 0; i < len; i ++ ) if(i % 3 == 2) {
    s[i] = (s[i] >> 4) | ((s[i] << 4)&0xFF);
  }
  FILE *fp = fopen("tt.bin", "wb");
  fwrite(s, 1, len, fp);
  fclose(fp);
}

这东西打开之后清晰可见的一个MZ头,不是dll就是一PE, 直接扔IDA里看好了

屏幕截图 2022-01-27 213423.png

这下怎么都该完了吧..看看这个解一下

跟到2800里一看,跟了好几层, 终于有了一个变换了
屏幕截图 2022-01-27 213531.png

内存是这样的, 开头0x63,0x7C,0x77,0x7B..这..一眼AES, 我现在只期望它是个朴素的AES-128,不要再玩花了, 我们输入要和AES的结果一样, AES的明文由原程序给出,看来原来的程序最后应该是装载了这个DLL, 我猜是最后一个sleep之后, 前面那个A什么队列里塞了装载.. 当然,这是本着做题的角度,我觉得那个不可能没用.. 现在回头看看合格到底怎么调用的..

而且我觉得这里真的好怪啊, 传过来的是AES明文, 输入的是加密后的结果, 那加密后的结果也够呛能打印啊.一般应该是我输入加密之后和传过来的密文对比吧..不管怎么样先弄到共享内存的值再说, 因为这后面有个0x52, 0x9, 没准下标+256这不是加密是解密呢((((

共享内存的MSDN文档

直接搜ShareMemory字符串

屏幕截图 2022-01-27 221207.png

找到Target,
屏幕截图 2022-01-27 221648.png

呜..这题真麻烦啊...我做了3个小时..

免费评分

参与人数 3威望 +1 吾爱币 +22 热心值 +3 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
qiwenchun + 1 + 1 谢谢@Thanks!
aa530416 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2022-2-10 17:55
@Y1rannn 你帖子用的图床ssl证书有问题,直接把几个帖子的图片上传到论坛本地吧,也能防止丢失不显示。
 楼主| Y1rannn 发表于 2022-2-18 12:33
Hmily 发表于 2022-2-10 17:55
@Y1rannn 你帖子用的图床ssl证书有问题,直接把几个帖子的图片上传到论坛本地吧,也能防止丢失不显示。

图床是我自己的博客,前一段证书过期但是我一直在旅游, 当天就发现了但是一直没解决,马上签好。
上传论坛好像有一点点麻烦(比 Typecho

点评

其实论坛上传还行,不算麻烦,传完了,用discuz的方式贴图,你别用markdown的语法,其实还行。  详情 回复 发表于 2022-2-18 14:57
Hmily 发表于 2022-2-18 14:57
Y1rannn 发表于 2022-2-18 12:33
图床是我自己的博客,前一段证书过期但是我一直在旅游, 当天就发现了但是一直没解决,马上签好。
上传 ...

其实论坛上传还行,不算麻烦,传完了,用discuz的方式贴图,你别用markdown的语法,其实还行。
头像被屏蔽
hua111 发表于 2022-2-19 10:57
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-25 14:15

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表