XCTF-RE-EASYHOOK

CTF · 03-21 · 74 人浏览

一、查看文件类型

Pasted image 20231207160955.png
PE32 无壳
Pasted image 20231207161009.png

二、HOOK原理

1.什么是hook(钩子)

对于Windows系统,它是建立在事件驱动机制上的,说白了就是整个系统都是通过消息传递实现的。hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并进行处理。所以说,我们可以在系统中自定义钩子,用来监视系统中特定事件的发生,完成特定功能,如屏幕取词,监视日志,截获键盘、鼠标输入等等。

钩子的种类很多,每种钩子可以截获相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。钩子可以分为线程钩子和系统钩子,线程钩子可以监视指定线程的事件消息,系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。

所以说,hook(钩子)就是一个Windows消息的拦截机制,可以拦截单个进程的消息(线程钩子),也可以拦截所有进程的消息(系统钩子),也可以对拦截的消息进行自定义的处理。Windows消息带了一些程序有用的信息,比如Mouse类信息,就带有鼠标所在窗体句柄、鼠标位置等信息,拦截了这些消息,就可以做出例如金山词霸一类的屏幕取词功能。

2.hook分类

 (1)线程钩子监视指定线程的事件消息。
 
(2)系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL)中。这是系统钩子和线程钩子很大的不同之处。

3、HOOK(钩子)的工作原理

   在正确使用钩子函数前,我们先讲解钩子函数的工作原理。当您创建一个钩子时,WINDOWS会先在内存中创建一个数据结构,该数据结构包含了钩子的相关信息,然后把该结构体加到已经存在的钩子链表中去。新的钩子将加到老的前面。当一个事件发生时,如果您安装的是一个线程钩子,您进程中的钩子函数将被调用。如果是一个系统钩子,系统就必须把钩子函数插入到其它进程的地址空间,要做到这一点要求钩子函数必须在一个动态链接库中,所以如果您想要使用系统钩子,就必须把该钩子函数放到动态链接库中去。

   当然有两个例外:工作日志钩子和工作日志回放钩子。这两个钩子的钩子函数必须在安装钩子的线程中。原因是:这两个钩子是用来监控比较底层的硬件事件的,既然是记录和回放,所有的事件就当然都是有先后次序的。所以如果把回调函数放在DLL中,输入的事件被放在几个线程中记录,所以我们无法保证得到正确的次序。故解决的办法是:把钩子函数放到单个的线程中,譬如安装钩子的线程。
       几点需要说明的地方: 
  
  (1) 如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。 
  (2) 对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。 
  (3) 钩子特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。

三、IDA静态分析和动态调试

1.查看main函数

Pasted image 20231211114203.png
程序逻辑分析:
将输入存储在Buffer中,长度为19,进行长度判断后,调用了函数sub_401220,当前此函数功能未知,接着调用CreateFileA和WriteFile创建文件并将输入写入本地文件夹中的文件,
双击文件名查看为
Pasted image 20231211120013.png
再调用sub_401240后,进行最后的flag判断,猜测sub_401240为关键加密函数

动态调试后发现,输入字符串“1234567891234567891”时,写入文件Your_input中的值发生了改变,其值如下:
Pasted image 20231211120412.png
只有两种可能,对输入的修改要么是在未知函数sub_401220中,要么是在WriteFile中,题目名为easyhook,故猜测在函数sub_401220进行了hook操作。
Pasted image 20231211135922.png

2.查看sub_401220

Pasted image 20231211121523.png
第9行通过GetProcAddress获取WriteFile函数的地址,
接着第12-16行进行一系列操作计算函数sub_401080和函数WriteFile的偏移地址,
第15行赋值的0xE9是jmp指令的机器码,
最后调用sub_4010D0后返回

3.查看sub_4010D0

Pasted image 20231211133227.png
此函数将byte_40C9BC的值写入lpAddress,byte_40C9BC的值在上层函数中定义了,经动态调试后发现为0xE9 892ED82Bh
Pasted image 20231211133954.png
即此时指令变为了jmp sub_401080
0xE9是jmp指令的机器码,确定sub_4010D0中进行了hook操作,执行WriteFile时,没有写文件,而是跳转到sub_401080

4.查看sub_401080

Pasted image 20231211140324.png

逻辑分析:
首先调用 sub_401000 函数,分析参数传递可知参数 lpBuffer 即是我们输入的 flag,那么很有可能就是在 sub_401000 里面对 flag 进行了修改。
然后调用 sub_401140 函数,接着又一次调用了WriteFile 函数。我们知道虽然 WriteFile 被 hook 了,但最后确实是把 flag 写入到了文件里面,所以很有可能是在 sub_401140 里面对 WriteFIle 进行了 hook 还原。
最后对 sub_401000 的返回值进行判断,如果非 0 则将NumberOfBytesWritten 置 1。

查看sub_401140
Pasted image 20231211140221.png
证实进行了hook还原

5.查看sub_401000

Pasted image 20231211140631.png
逻辑分析:
(1)我们输入的长度为 19 的 flag 经过第一个循环的处理之后,如果和 byte_40A030 指向的全局字符串相同,那么 sub_401000 返回 1。
(2)在 sub_401080 里面对sub_401000 的返回值进行判断,如果返回值为 1,则NumberOfBytesWritten 置 1。
(3)在最外层的 main函数里面进行判断,如果NumberOfBytesWritten 为 1,则输出正确的提示信息。

byte_40A030如下:
Pasted image 20231211140748.png
提取出来为:
unsigned char ida_chars[] =
{
0x61, 0x6A, 0x79, 0x67, 0x6B, 0x46, 0x6D, 0x2E, 0x7F, 0x5F,
0x7E, 0x2D, 0x53, 0x56, 0x7B, 0x38, 0x6D, 0x4C, 0x6E, 0x00
};

6.查看main最后看似关键的函数sub_401240

Pasted image 20231211141046.png
逻辑分析或动态调试都发现其毫无作用

四、解题

关键加密函数sub_401000
Pasted image 20231211140631.png

将 byte_40A030 指向的字符串做一次 sub_401000 函数里面第一个循环处理的逆运算,并将byte_40A030 最后一位与0x13异或,就可以得到输入正确的 flag 了。

脚本如下:


a = [0x61, 0x6A, 0x79, 0x67, 0x6B, 0x46, 0x6D, 0x2E, 0x7F, 0x5F,  
     0x7E, 0x2D, 0x53, 0x56, 0x7B, 0x38, 0x6D, 0x4C, 0x6E, 0x00]  
flag = list("1234567891234567890")  #长度为19  
  
for i in range(0,18):  
    if i % 2 ==1:  
        flag[i] = chr((a[i]^i)+i)  
    else:  
        flag[i+2] = chr(a[i]^i)  
flag[18]= chr(a[18]^0x13)  
  
print("".join(flag))  
#结果第一位有错误,可能是程序中缺失了,改为f

运行结果:
Pasted image 20231211141314.png
尝试提交后发现不对,将第一位改为f后提交,发现正确

flag为:flag{Ho0k_w1th_Fun}
Pasted image 20231211141828.png

CTF RE XCTF hook
Theme Jasmine by Kent Liao