首页
社区
课程
招聘
[原创] 使用Frida Hook 自动化发现可DLL劫持利用白文件
发表于: 2023-3-24 23:37 13412

[原创] 使用Frida Hook 自动化发现可DLL劫持利用白文件

2023-3-24 23:37
13412

背景

之前只知道Frida在android平台使用,最近发现其实也支持Windows平台,正好学习下Frida怎么用,写了个小脚本练练手。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# https://github.com/geemion
 
import frida
import subprocess
from win32process import CREATE_SUSPENDED
import psutil
import os
import argparse
 
 
class SideLoadFinder:
    def __init__(self, out_path, timeout):
        self.out_path = out_path
        self.timeout = timeout
        self.LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020
        self.image_path = ""
        self.line_content = ""
        self.frida_script = """
var LoadLibraryEx = Module.findExportByName("api-ms-win-core-libraryloader-l1-2-0.dll", "LoadLibraryExW");
if (LoadLibraryEx == 0) {
    LoadLibraryEx = Module.findExportByName("kernelbase.dll", "LoadLibraryExW");
}
var FakeModule = 0x777777;
Interceptor.attach(LoadLibraryEx, {
    onEnter: function(args, state) {
        this.lpLibFileName = Memory.readUtf16String(args[0]);
        this.dwFlags = args[2].toUInt32();
    },
    onLeave: function(retval, state) {
        if (retval == 0) {
            if (this.dwFlags != 2) {
                send({"payload_type":"dll", "dll":this.lpLibFileName, "flag":this.dwFlags});
                retval.replace(FakeModule);
            }
        }
    }
});
var GetProcAddress = Module.findExportByName("kernel32.dll", "GetProcAddress");
Interceptor.attach(GetProcAddress, {
    onEnter: function(args, state) {
        this.Procname = Memory.readAnsiString(args[1]);
        this.module = args[0];
        if(this.module == FakeModule){
        send({"payload_type":"proc", "proc":this.Procname});
        }
    },
});
"""
 
    def on_message(self, message, data):
        if message["type"] == "send":
            print(message)
            if message["payload"]["payload_type"] == "dll":
                self.handle_dll_msg(message)
            else:
                self.handle_proc_msg(message)
        else:
            print(message)
 
    def handle_dll_msg(self, message):
        self.out_csv()
        flag = message["payload"]["flag"]
        if flag & self.LOAD_LIBRARY_AS_IMAGE_RESOURCE:
            return
        self.line_content = "{},{},0x{:x}".format(self.image_path, message["payload"]["dll"], flag)
 
    def handle_proc_msg(self, message):
        proc = message["payload"]["proc"]
        self.line_content = "{},{}".format(self.line_content, proc)
 
    def out_csv(self):
        if self.line_content == "":
            return
        self.line_content = self.line_content + "\n"
        with open(self.out_path, "a+") as f:
            f.write(self.line_content)
            print(self.line_content)
            self.line_content = ""
 
    def finder(self, image_path):
        try:
            self.image_path = image_path
            pid = subprocess.Popen(self.image_path, creationflags=CREATE_SUSPENDED).pid
            session = frida.attach(pid)
            script = session.create_script(self.frida_script)
            script.on('message', self.on_message)
            script.load()
            p = psutil.Process(pid)
            p.resume()
            try:
                p.wait(timeout=self.timeout)
            except Exception as e:
                print(e)
                p.kill()
            session.detach()
        except Exception as e:
            print(e)
        finally:
            self.out_csv()
 
    def run(self, image_dir):
        for parent, _, filenames in os.walk(image_dir):
            for filename in filenames:
                if not filename.lower().endswith(".exe"):
                    continue
                image_path = os.path.join(parent, filename)
                self.finder(image_path)
 
 
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Windows sideload finder by frida')
    parser.add_argument('-i', type=str, help='exe samples dir')
    parser.add_argument('-o', type=str, help='output csv path')
    args = parser.parse_args()
    if not args.i or not args.o:
        parser.print_help()
        exit(1)
    out_path = args.o
    image_dir = args.i
    finder = SideLoadFinder(out_path, 5)
    finder.run(image_dir)

说明

代码逻辑非常简单,简单说下原理。通过Hook LoadLibraryExW 感知DLL加载失败事件并记录加载失败的DLL文件名,在LoadLibraryExW给一个标记的FakeModule值;
同时Hook GetProcAddress在函数中感知是否为FakeModule,如果为FakeModule则记录函数名称;
这样就可以得到一组可劫持利用的DLL名称及函数名了。

项目地址

https://github.com/roadwy/SideloadFinder 欢迎star&pr


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-9-19 08:57 被KenLi编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//