banner
NEWS LETTER

2022西湖论剑-Review

Scroll down

最近看了一下西湖论剑的Re题,收获很大(* ̄3 ̄)╭ ,能学到很多Re的技巧和 ( 在我看来 ) 不大常规的知识点, 同时发现自己离参加线下赛的路还很长 😭 还得继续努力了。CTF之路任重道远呀~

本篇没有涉及太多关于题目中加解密逻辑的分析,更多的偏向Trick的具体实践和分析,完整WP还请参看其他大佬的blog~

在此特别感谢 Lu1u师傅 云之君师傅 Sink师傅 的详细WP分享

BabyRE

hook & atexit

Core

int atexit(void (*func)(void))

  • func在程序终止时被调用的函数。
  • 如果函数成功注册,则该函数返回零,否则返回一个非零值。
  • 注册顺序和调用顺序相反

分析函数调用顺序

  1. TLS_callback (4010F0) 创建/终止进程的线程时会自动调用执行的函数(前后共调用两次)且其调用执行先于EP代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int __stdcall TlsCallback_0(int a1, int a2, int a3)
    {
    int result; // eax

    __CheckForDebuggerJustMyCode(&unk_4090A2);
    data[0] = 0x102030;
    data[1] = 0x40506070;
    data[2] = 0x8090A0B0;
    result = 4;
    data[3] = 0xC0D0E0F0;
    return result;
    }
  2. 初始化(401000)

    1
    2
    3
    4
    5
    6
    int dynamic_initializer_for__NetworkIDGenerator::IDTree__()
    {
    __CheckForDebuggerJustMyCode(&unk_4090A2);
    get_input(&unk_4085C6);
    return atexit(std::_Destroy_in_place<char *>);
    }
  3. sub_401000 → get_input ( 401170 )

  4. sub_401000 注册函数 sub_405770

  5. sub_401050 , 函数和401000类似,随后进入sub_401230并注册sub_4057B0

  6. sub_4010A0 , 函数和401050类似,随后进入sub_4012B0并注册sub_4057F0

  7. sub_4010A0 → sub_4012B0,核心点在sub_4012B0,简单来说就是hook了GetLastError() ,下面贴上分析内容(小技巧:飞书的截图功能可以滚动截图哦,对于函数展示还是很好用的)

    Untitled

至此,有关atexit的函数已经注册完毕,且在程序终止时的调用顺序为: sub_4057F0→ sub_4057B0 → sub_405770

  1. 进入main函数(00402550)可以看到这里的GetLastError(),因为上面已经hook了,所以实际上调用的是sub_4010D0

    1
    2
    3
    4
    5
    6
    int __cdecl main(int argc, const char **argv, const char **envp)
    {
    __CheckForDebuggerJustMyCode(&unk_4090A2);
    GetLastError();
    return 0;
    }
  2. 这时候主程序要exit了,因此就是sub_4057F0→ sub_4057B0 → sub_405770

加密逻辑

我是懒鬼 参考其他师傅的blog( ̄︶ ̄*))

Dual-personality

天堂之门参考:天堂之门技术

简单来说就是将32位的程序切换到64位执行部分64位的代码。

关键逻辑

sub_401120分析:

Untitled

Untitled

Untitled

注意这里显示的jmp far ptr loc_4014FE+2 并不是真实地址!最好通过capstone来分析

1
2
3
4
5
6
7
from capstone import *

CS = Cs(CS_ARCH_X86, CS_MODE_32)
a = CS.disasm(b'\xea\xd0\x11\x40\x00\x33\x00', 0)

for i in a:
print(f"{i.mnemonic} {i.op_str}") #ljmp 0x33:0x4011d0

处理方法

复制一份文件, 修改此处,用IDA64打开分析

Untitled

这里遇到一个别人没提到的坑,IDA64打开后发现地址奇奇怪怪的,这时候要重新对齐一下基地址 Edit → Segments → Rebase Program → Value → 0x400000

Untitled

继续分析

初始化了0x407058的值,再跳到前面开辟的新空间继续运行

Untitled

Untitled

main继续执行,再次碰到天堂之门call fword ptr off_40700C 同样不可信,实际上指令是lcall [0x40700c],此时的存值是dword_401200,换到ida64看

Untitled

后面还有一次调用到core_func(0x401120),这里不赘述了

后面的一些跳转感觉比较奇怪,主要是我确定不了什么时候回到32位了(例如下面这段看起来就很怪,调了半天发现是在64位执行的,但是这段过后看不到哪里有将cs复原回0x23执行0x4014C5以后的代码)不过两个文件结合起来分析就好了

Untitled

Untitled

Untitled

Berkeley

ebpf逆向

单看Trick的话这个应该算是比较常规的,就不多说了,可以看别的师傅的blog

Guest

Intel VT

常规的VT分析思路,源码改自 https://github.com/zzhouhe/VT_Learn

guest.exe此处触发异常, 调用对应的异常处理函数, 加密逻辑基本是rc4+魔改tea

这题的加密逻辑分析还是挺多的,最好还是能动调起来(但是动调真的好麻烦阿🤡)

Untitled

其他文章
cover
test
  • 23/04/05
  • 01:52
  • CTF