[MiniL CTF 2022] Reverse部分赛题复现
再不学re👶要被开了
为什么是部分复现呢?因为有个wasm题没有环境没法复现捏
1|0twin
👶最开始以为这是个签到题。。
打断点调试发现main之前已经有东西在运行了,调了一会发现有个函数叫TlsCallback_0
搜了下发现是TLS回调函数
TLS回调函数是指,每当创建/终止进程的线程时会自动调用执行的函数,创建进程的主线程时也会自动调用回调函数,且其调用执行先于EP代码
可以理解为创建线程和终止线程的时候会各执行一次TLS回调函数
发现没法反编译(sp-analysis failed,栈帧错误),点进去看汇编
分析一下汇编,发现0x401999C处有个call $+5
,$+5就是40199C + 5 = 4019A1,就是下一句话的地址。call将下一句的地址压栈,然后add给esp指针加了个0x1E(var_12C = -12Ch),retn的地址就是0xA1 + 0x1E = 0xBF,所以中间这一串屁用没有,那直接把call到字符串这里全nop了。
发现还是不能反编译(继续sp-analysis failed),最后发现是函数的结束地址被最开始0x40199C给干扰了(指看了wp才知道),最后正确的retn地址是0x401D5B,alt + p把tls函数的end address改成401D5C就行。401A03处也有这个花指令。
1|1a2 = 1
最开始的BeingDebugged = NtCurrentPeb()->BeingDebugged;
是反调试,动调的时候jz jnz反着patch就能过掉
v7这串玩意在异或0x7F后是Please input your flag:,v16异或后是%s,可以发现后面两个函数分别是输出和输入,输入存储在dword_404448内
在反调试的下面有一句*(&TlsCallbacks + 1) = (int (__cdecl *)(int, int))sub_401D60;
这里判断若没有在动调就在 TLS函数后增加一个函数sub_401D60
,这个函数就在TLS下面。
现在切过去看发现这里也爆红了,但花指令和TLS的花是一样的
Xor_0x7Fu(ProcName);
:WriteFileXor_0x7Fu(ModuleName);
:kernel32.dllhModule = GetModuleHandleA(ModuleName);
:获得kernel32.dll的句柄
这里是导入了WriteFile和kernel32.dll(后续实现了注入)。GetProcAddress取得WriterFile的函数指针存储在dword_4043DC中
该函数中遍历了kernel32.dll的导入表, 并在lpAddress为WriteFile的时候调用VituralProtect获取权限,从而修改其为a2(即sub_401650)。换言之,该函数将原本的WriteFile给hook成了sub_401650。
好几把高端,以前没见过这种玩意。。
一开始看的时候不知道上面两个6有啥用,唯一能看懂的是sub_4017C0和sub_40166C0类似,只是这次是将hook给取消了,WriteFile调用的还是自己。但一开始也能猜出来这次hook就是为了执行前面两个置6的操作。
(后面会发现是把xxtea的z的位移改成了6)
1|2a2 != 1
sub_401D60末尾有ExitProcess,这之后a2 = 0,进入下半段函数。
v11是correct,v15是wrong,v9是Please close the debugger and try again。
其中有一个加密函数内是xxtea加密,对输入的后20位进行判断。在动调拿出enflag和key后可以解出是3e90c91c02e9b40b78b}
,显然是后半段flag
这里面有个重要函数sub_401410
1|3子文件tmp
sub_401410中创建了一个叫做tmp的文件,断点打在return处动调再运行几步可以拿到完整的tmp文件。然后分析tmp文件。
最开始的sub_401400中有sub_4010F0,是个检测动调的函数,如果检测到一些动调进程的名字的话就会退出程序。然后有对delta的异或操作和一步添加VEH(但好像并没有用到这个VEH)。
sub_4010E0是IsDebuggerPresent反调试
后面才发现sub_401390和之前主体文件a2=1部分中的这一句对应
这个NAME字符串异或出来就是"FLAG",hObject这一句创建了名为FLAG的文件映像,dword_404448这一句将内存映射的文件存了下来,这样可以让子进程访问dword_404448指向的内存,实现修改等操作。
那么sub_401390里面获取了dword_404448的共享内存,然后把lpBaseAddress指向的数据复制0x28u位(40位,按位数来说应该是输入)给a1(unk_4043A8)。
随后,对unk_4043A8进行了xxtea加密。
注意,此处的xxtea加密是有变动的:其右移位数从原来的5位变成了6位(前面sub_401650写进去的两个6就在这里)。
动调发现sub_401210炸了,看汇编看到了和以前一样的花指令,去掉后发现还是爆红了
MEMORY[0] = 0是往地址0内写值了,引发了内存访问异常。在汇编里面你可以看见这几句话
xor先把ebx置0,然后mov [ebx], ebx就向地址0里面写值了, 就异常了。
尝试动调的时候把eip改改跳到401234,发现还是死了。。
分析一下发现[ebp+var_4]的值是delta(在401220处mov [ebp+var_4], eax),显然这个地址没卵用,还是会爆异常(这就是为什么上面说好像并没有用到VEH,他完全没处理异常。。)
滚回原来的程序,从创建tmp那里往下看,发现sub_401510里面又有花,去掉
WaitForDebugEvent表明父进程是通过调试的方法打开的子进程,并且通过百度发现
联合体u的值
1u.Exception
2 u.Create Thread
3 u.CreateProcessInfo
4 u.ExitThread
5 u.ExitProcess
6 u.LoadDll
7 u.UnloadDll
8 u.DebugString
9 u.RipInfo
union的值是通过DebugEvent.dwDebugEventCode决定的,也就是说,=1的时候说明触发了异常,此时父进程会处理子进程的异常,修改子进程代码;=5说明程序正常退出。
if ( v4[0] == -1073741819 )
下面对eip和eax进行了修改,经过搜索发现-1073741819(就是0xC0000005)代表的错误类型是发生访问冲突,也就是内存访问异常。
联系一下tmp文件里面爆红的MEMORY[0]=0,说明这里就是检测到子进程发生了异常后,程序会转到父进程进行异常处理,并且如果是内存访问异常的话就修改eip和eax。
联系到tmp文件内对应的eax的值是处理后的delta,说明tmp里面的xxtea的delta还得再异或0x1B207u才是正确的值。这下就可以用正确的魔改版xxtea解出前一半的flag了:miniLctf{cbda59ff59e
最后得到flag:
miniLctf{cbda59ff59e3e90c91c02e9b40b78b}
这个题学到的新东西真的太多了,👶打算下一个自学坑就开seh veh这些异常处理了
2|0NotRC4
3|0lemon
__EOF__
作 者:iPlayForSG
出 处:https://www.cnblogs.com/Here-is-SG/p/16285687.html
关于博主:编程路上的小学生,热爱技术,喜欢专研。评论和私信会在第一时间回复。或者直接私信我。
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)