首页
社区
课程
招聘
[原创]IDA&Frida学习
2023-3-16 12:17 9788

[原创]IDA&Frida学习

2023-3-16 12:17
9788

IDA&Frida

前言

第一次学习idapython,偶然间发现了一本秘籍《IDA脚本开发之旅》,这是白龙的系列文章,主要是安卓平台,笔者只是根据他的知识点学习,拓展,可以会稍微提及别的平台。本文并不会贴出他的思路分析,只对于源码进行学习,有兴趣可以加他星球一起学习!

 

另外本文也学习了无名侠的IDAFrida脚本,有兴趣可以加他星球一起学习!

脚本学习

IDA 静态分析 + Frida Hook,可以让分析变得更加丝滑。

 

这里我们会学习两个脚本 一个是白龙的 一个是 无名侠的。

ShowFridaCode

这是白龙写的一个Python脚本,用于生成Frida的Hook代码。主要包含了以下几个部分:

  1. 导入模块
  2. Frida Hook函数模板
  3. Frida Inline Hook函数模板
  4. 打印参数的函数
  5. 生成函数Hook代码的函数
  6. 生成Inline Hook代码的函数
  7. 根据地址生成Hook代码或Inline Hook代码的函数
  8. 生成初始化代码的函数
  9. 生成Dump内存代码的函数
  10. IDA View_Hooks类,用于处理在IDA视图中双击和单击事件
  11. 插件类,实现插件的初始化、运行和退出

前置知识

frida ida就不说了,主要说一下 其他的知识

 

android_dlopen_ext 是 Android 系统中的一个函数,用于在运行时动态加载共享库。与标准的 dlopen() 函数相比,android_dlopen_ext 提供了更多的参数选项和扩展功能,例如支持命名空间、符号版本等特性。该函数通常用于 Android 应用程序或系统进程中,用于加载共享库并获取其中定义的符号。在 Android 应用程序中,常常会使用 System.loadLibrary() 函数调用 android_dlopen_ext(),从而加载与应用程序打包在一起的共享库文件。

 

在win上我们一般用 LoadLibrary 函数来加载 DLL(动态链接库)文件,并使用 GetProcAddress 函数来获取函数指针。在 iOS 平台上,可以使用 dlopen 函数来加载共享库,并使用 dlsym 函数来获取函数指针。

 

linker的调用流程:

  1. 在do_dlopen中通过find_library进行加载so
  2. 在加载完so后通过call_constructors完成init_array的加载
  3. find_library最后调用load_libray完成so的转载
  4. 最后通过load_library的elf_reader.load完成so的装载

了解了上面两个知识点,在Hook Native时,我们常常通过Spawn抢占两个较早的时机,1是 JNIOnLoad 前 2是 init_proc 以及 init_array 前 ,就可以通过hook上述函数。

 

接下来就看源码学习吧、我会在代码中给出详细的注释

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# Template 类提供了一种简单而安全的字符串替换机制,可以避免常见的安全漏洞,例如 SQL 注入攻击。在使用模板字符串时,可以将需要被替换的变量名以 $ 作为前缀,并在后面用花括号 {} 将变量名括起来。然后使用 substitute() 方法将变量值传递给模板,该方法会将变量名替换为对应的值。
from string import Template
# 文本行相关的函数和类。
import ida_lines
# IDA Pro 插件开发相关的函数和类。
import idaapi
import idc
# 该类型是插件开发的基本接口
from ida_idaapi import plugin_t
 
# hook函数的模板
"""
这段代码通过调用 Module.findBaseAddress("$soName") 找到指定so库的基地址,然后使用 Interceptor.attach() 函数 hook 在基地址加上指定偏移量的函数。
    通过这些占位符,可以实现动态替换相应的值
    $soName 表示要 hook 的so库的名字;
    $functionName 表示要 hook 的函数名;
    $offset 表示要 hook 的函数在so库中的偏移量;
    $args 表示在 onEnter() 函数中输出的参数值;
    $result 表示在 onLeave() 函数中输出的返回值。
"""
hook_function_template = """
function hook_$functionName(){
    var base_addr = Module.findBaseAddress("$soName");
 
    Interceptor.attach(base_addr.add($offset), {
        onEnter(args) {
            console.log("call $functionName");
            $args
        },
        onLeave(retval) {
            $result
            console.log("leave $functionName");
        }
    });
}
"""
 
# 这段模板和上面那段差不多 主要用于某行指令去call函数的时候 在onEnter回调中 可以查看函数参数、寄存器值等调用上下文的详细信息。
inline_hook_template = """
function hook_$offset(){
    var base_addr = Module.findBaseAddress("$soName");
 
    Interceptor.attach(base_addr.add($offset), {
        onEnter(args) {
            console.log("call $offset");
            console.log(JSON.stringify(this.context));
        },
    });
}
"""
 
# 参数打印
logTemplate = 'console.log("arg$index:"+args[$index]);\n'
 
# 在指定共享库加载时执行自定义逻辑的功能,可以用于动态监视和修改目标进程中的行为。
dlopenAfter_template = """
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
if(android_dlopen_ext != null){
    Interceptor.attach(android_dlopen_ext,{
        onEnter: function(args){
            var soName = args[0].readCString();
            if(soName.indexOf("$soName") !== -1){
                this.hook = true;
            }
        },
        onLeave: function(retval){
            if(this.hook) {
                this.hook = false;
                dlopentodo();
            }
        }
    });
}
 
function dlopentodo(){
    //todo
}
"""
 
# 在指定共享库初始化时执行自定义逻辑的功能 例如  init_proc 以及 init_array
init_template = """
function hookInit(){
    var linkername;
    var alreadyHook = false;
    var call_constructor_addr = null;
    var arch = Process.arch;
    if (arch.endsWith("arm")) {
        linkername = "linker";
    } else {
        linkername = "linker64";
    }
 
    var symbols = Module.enumerateSymbolsSync(linkername);
    for (var i = 0; i < symbols.length; i++) {
        var symbol = symbols[i];
        if (symbol.name.indexOf("call_constructor") !== -1) {
            call_constructor_addr = symbol.address;
        }
    }
 
    if (call_constructor_addr.compare(NULL) > 0) {
        console.log("get construct address");
        Interceptor.attach(call_constructor_addr, {
            onEnter: function (args) {
                if(alreadyHook === false){
                    const targetModule = Process.findModuleByName("$soName");
                    if (targetModule !== null) {
                        alreadyHook = true;
                        inittodo();
                    }
                }
            }
        });
    }
}
 
function inittodo(){
    //todo
}
"""
 
# 以十六进制形式打印指定so文件中指定偏移量处内存数据
dump_template = """
function dump_$offset() {
    var base_addr = Module.findBaseAddress("$soName");
    var dump_addr = base_addr.add($offset);
    console.log(hexdump(dump_addr, {length: $length}));
}
"""
 
# 生成参数打印脚本
def generate_printArgs(argNum):
    if argNum == 0:
        return "// no args"
    else:
        temp = Template(logTemplate)
        logText = ""
        for i in range(argNum):
            logText += temp.substitute({'index': i})
            logText += "            "
        return logText
 
# 生成hook函数模板 通过 soName, functionName, address, argNum, hasReturn
def generate_for_func(soName, functionName, address, argNum, hasReturn):
    # 根据参数个数打印
    argsPrint = generate_printArgs(argNum)
    # 根据是否有返回值判断是否打印retval
    retPrint = "// no return"
    if hasReturn:
        retPrint = "console.log(retval);"
    # 使用Python提供的Template字符串模板方法
    temp = Template(hook_function_template)
    offset = getOffset(address)
    result = temp.substitute(
        {'soName': soName, "functionName": functionName, "offset": hex(offset), "args": argsPrint, "result": retPrint})
    print(result)
 
# 获取地址偏移
def getOffset(address):
    if idaapi.get_inf_structure().is_64bit():
        return address
    else:
        return address + idc.get_sreg(address, "T")
 
# 生成inline hook函数模板 通过 soName, address
def generate_for_inline(soName, address):
    offset = getOffset(address)
    temp = Template(inline_hook_template)
    result = temp.substitute({'soName': soName, "offset": hex(offset)})
    if idaapi.is_call_insn(address):
        callAddr = idaapi.get_name_ea(0, idc.print_operand(address, 0))
        if callAddr != idaapi.BADADDR:
            callAddress = idc.get_operand_value(address, 0)
            argnum, _ = get_argnum_and_ret(callAddress)
            argsPrint = generate_printArgs(argnum)
            print(result.replace(
                "console.log(JSON.stringify(this.context));", argsPrint))
        else:
            print(result)
    else:
        print(result)
 
# 这个函数的作用是获取指定地址处的函数的参数个数和返回值类型。
def get_argnum_and_ret(address):
    # 获取反编译的代码
    cfun = idaapi.decompile(address)
    # 获取参数长度
    argnum = len(cfun.arguments)
    ret = True
    # 先调用cfun.print_dcl() 函数获取函数的声明语句 在调用 tag_remove 去掉字符串中的标记,它可以将类似 <hex number>、<function name> 这样的标记从字符串中去掉
    dcl = ida_lines.tag_remove(cfun.print_dcl())
    # 判断函数声明语句是否有返回值  需要注意:当一个函数返回 "void *" 类型时,它实际上是返回一个指针,指向某个数据的内存地址。
    if (dcl.startswith("void ") is True) & (dcl.startswith("void *") is False):
        ret = False
    return argnum, ret
 
# 通过地址去生成 hook 脚本
def generate_for_func_by_address(addr):
    soName = idaapi.get_root_filename()
    functionName = idaapi.get_func_name(addr)
    argnum, ret = get_argnum_and_ret(addr)
    generate_for_func(soName, functionName, addr, argnum, ret)
 
# 通过地址去生成 inline hook 脚本
def generate_for_inline_by_address(addr):
    soName = idaapi.get_root_filename()
    generate_for_inline(soName, addr)
 
# 如果传入的地址等于函数头的地址 那么就hook函数头 否则生成函数中
def generate_snippet(addr):
    startAddress = idc.get_func_attr(addr, idc.FUNCATTR_START)
    if startAddress == addr:
        generate_for_func_by_address(addr)
    elif startAddress == idc.BADADDR:
        print("不在函数内")
    else:
        generate_for_inline_by_address(addr)
 
# 生成初始化模板 脚本
def generateInitCode():
    soName = idaapi.get_root_filename()
    print(Template(dlopenAfter_template).substitute({'soName': soName}))
    print(Template(init_template).substitute({'soName': soName}))
 
# 生成dump 脚本
def generate_dump_script(start, length):
    soName = idaapi.get_root_filename()
    print(Template(dump_template).substitute(
        {'soName': soName, "offset": hex(start), "length": hex(length)}))
 
# IDA界面hook
class Hook(idaapi.View_Hooks):
    # 监听双击 如果是反汇编视图,则获取当前光标所在地址 然后生成模板
    def view_dblclick(self, view, event):
        widgetType = idaapi.get_widget_type(view)
        if widgetType == idaapi.BWN_DISASM:
            global initialized
            if not initialized:
                initialized = True
                generateInitCode()
            address = idaapi.get_screen_ea()
            generate_snippet(address)
 
        # 监听单击事件 获取开始和结束地址 进行dump
    def view_click(self, view, event):
        widgetType = idaapi.get_widget_type(view)
        if widgetType == idaapi.BWN_DISASM:
            start = idc.read_selection_start()
            end = idc.read_selection_end()
            if (start != idaapi.BADADDR) and (end != idaapi.BADADDR):
                length = end - start
                generate_dump_script(start, length)
 
 
class GenFrida_Plugin_t(plugin_t):
    # 关于插件的注释
    # 当鼠标浮于菜单插件上方时,IDA左下角所示
    comment = "A Toy Plugin for Generating Frida Code"
    # 帮助信息,我们选择不填
    help = "unknown"
    # 插件的特性,是一直在内存里,还是运行一下就退出,等等
    flags = idaapi.PLUGIN_KEEP
    # 插件的名字
    wanted_name = "ShowFridaCode"
    # 快捷键,我们选择置空不弄
    wanted_hotkey = ""
 
    # 插件刚被加载到IDA内存中
    # 这里适合做插件的初始化工作
    def init(self):
        print("ShowFridaCode init")
        return idaapi.PLUGIN_KEEP
 
    # 插件运行中
    # 这里是主要逻辑
    def run(self, arg):
        print("ShowFridaCode run")
        global myViewHook
        myViewHook = Hook()
        myViewHook.hook()
 
    # 插件卸载退出的时机
    # 这里适合做资源释放
    def term(self):
        pass
 
 
initialized = False
# register IDA plugin
 
 
def PLUGIN_ENTRY():
    return GenFrida_Plugin_t()

ShowFridaCode 这是一个十分简单但是很优秀的脚本 包含了ida界面hook frida语法 模板生成 安卓初始化流程 arm汇编的理解

IDAFrida

这是无名侠写的一个Python脚本。这段脚本主要是一个基于 IDA Pro 和 Frida 的集成工具,用于辅助逆向工程师进行动态调试。具体来说,这个工具通过 IDA Pro 分析程序的代码,提取出函数的信息(如函数名、地址、参数数量等),然后使用 Frida 注入 JS 脚本,在运行时拦截这些函数,并在函数进入和返回时打印出函数的参数和返回值。这个工具的具体功能包括:

  1. 通过 IDA Pro 分析程序代码,提取函数信息;
  2. 使用 Frida 注入 JS 脚本,拦截函数并打印参数和返回值;
  3. 提供一个 UI 界面,让用户可以修改 Frida 命令和 JS 脚本。

具体来说,这个脚本实现了以下几个类和函数:

  1. ActionManager 类:一个动作管理器,用于管理 IDA Pro 中的动作。它提供了注册、初始化和销毁动作的功能;
  2. Action 类:一个动作基类,用于创建 IDA Pro 中的动作。它包含了动作的名称、描述、快捷键等信息,并提供了动作激活和更新的虚函数;
  3. Configuration 类:一个配置类,用于保存和读取用户配置信息。它包含了 Frida 命令和 JS 脚本的配置信息,并提供了保存和读取配置信息的方法;
  4. ConfigurationUI 类:一个 UI 类,用于提供一个 UI 界面,让用户可以修改配置信息;
  5. ScriptGenerator 类:一个脚本生成器类,用于生成注入到目标程序中的 JS 脚本。

我们直接看源码学习吧 同样我也会给出详细的注释

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
import ida_name
import idaapi
 
###################
# from: https://github.com/igogo-x86/HexRaysPyTools
# 这是一个名为 ActionManager 的类,它用于管理多个动作(Action)并提供初始化和清理方法
class ActionManager(object):
    def __init__(self):
        self.__actions = []
 
    def register(self, action):
        self.__actions.append(action)
        idaapi.register_action(
            # 创建一个动作描述符(action descriptor),以便注册动作并将其添加到IDA的菜单或工具栏中。action.name是动作的名称,action.description是动作的描述,action是动作的处理程序,action.hotkey是动作的快捷键。
            idaapi.action_desc_t(action.name, action.description, action, action.hotkey)
        )
 
    def initialize(self):
        pass
 
    def finalize(self):
        for action in self.__actions:
            idaapi.unregister_action(action.name)
 
# 可以通过 register函数注册动作
action_manager = ActionManager()
 
#这是一个基类。它继承自 idaapi.action_handler_t 类,并定义了 activate 和 update 方法作为接口,但是这些方法都是抛出了 NotImplementedError 异常,需要在子类中进行实现。此外,该类还定义了 name 属性,用于返回一个标识名称,方便在 IDA 中进行注册和调用
class Action(idaapi.action_handler_t):
    """
    Convenience wrapper with name property allowing to be registered in IDA using ActionManager
    """
    description = None
    hotkey = None
 
    def __init__(self):
        super(Action, self).__init__()
 
    @property
    def name(self):
        return "FridaIDA:" + type(self).__name__
 
    # 当从菜单、弹出菜单、工具栏或以编程方式触发操作时,会调用它
    def activate(self, ctx):
        # type: (idaapi.action_activation_ctx_t) -> None
        raise NotImplementedError
    # 当UI的上下文发生更改时会调用此函数
    def update(self, ctx):
        # type: (idaapi.action_activation_ctx_t) -> None
        raise NotImplementedError
 
############################################################################
import ida_funcs
import idc
import json
import os
 
from PyQt5 import QtCore
from PyQt5.Qt import QApplication
from PyQt5.QtWidgets import QDialog, QHBoxLayout, QVBoxLayout, QTextEdit
 
# [offset] => offset of target function in hex value format.
# [funcname] => function name
# [filename] => input file name of IDA. e.g. xxx.so / xxx.exe
default_template = """
(function () {
    // @ts-ignore
    function print_arg(addr) {
        try {
            var module = Process.findRangeByAddress(addr);
            if (module != null) return "\\n"+hexdump(addr) + "\\n";
            return ptr(addr) + "\\n";
        } catch (e) {
            return addr + "\\n";
        }
    }
    // @ts-ignore
    function hook_native_addr(funcPtr, paramsNum) {
        var module = Process.findModuleByAddress(funcPtr);
        try {
            Interceptor.attach(funcPtr, {
                onEnter: function (args) {
                    this.logs = "";
                    this.params = [];
                    // @ts-ignore
                    this.logs=this.logs.concat("So: " + module.name + "  Method: [funcname] offset: " + ptr(funcPtr).sub(module.base) + "\\n");
                    for (let i = 0; i < paramsNum; i++) {
                        this.params.push(args[i]);
                        this.logs=this.logs.concat("this.args" + i + " onEnter: " + print_arg(args[i]));
                    }
                }, onLeave: function (retval) {
                    for (let i = 0; i < paramsNum; i++) {
                        this.logs=this.logs.concat("this.args" + i + " onLeave: " + print_arg(this.params[i]));
                    }
                    this.logs=this.logs.concat("retval onLeave: " + print_arg(retval) + "\\n");
                    console.log(this.logs);
                }
            });
        } catch (e) {
            console.log(e);
        }
    }
    // @ts-ignore
    hook_native_addr(Module.findBaseAddress("[filename]").add([offset]), [nargs]);
})();
"""
 
# 配置类: 设置和存储配置信息的方法
class Configuration:
    def __init__(self) -> None:
        self.frida_cmd = """frida -U --attach-name="com.example.app" -l gen.js --no-pause"""
        self.template = default_template
        if os.path.exists("IDAFrida.json"):
            self.load()
 
    def set_frida_cmd(self, s):
        self.frida_cmd = s
        self.store()
 
    def set_template(self, s):
        self.template = s
        self.store()
 
    def reset(self):
        self.__init__()
 
    def store(self):
        try:
            data = {"frida_cmd": self.frida_cmd, "template": self.template}
            open("IDAFrida.json", "w").write(json.dumps(data))
        except Exception as e:
            print(e)
 
    def load(self):
        try:
            data = json.loads(open("IDAFrida.json", "r").read())
            self.frida_cmd = data["frida_cmd"]
            self.template = data["template"]
        except Exception as e:
            print(e)
 
 
global_config = Configuration()
 
# 修改配置UI 界面类
class ConfigurationUI(QDialog):
    def __init__(self, conf: Configuration) -> None:
        super(ConfigurationUI, self).__init__()
        self.conf = conf
        self.edit_template = QTextEdit()
        self.edit_template.setPlainText(self.conf.template)
        layout = QHBoxLayout()
        layout.addWidget(self.edit_template)
        self.setLayout(layout)
 
    def closeEvent(self, a0) -> None:
        self.conf.set_template(self.edit_template.toPlainText())
        self.conf.store()
        return super().closeEvent(a0)
 
# 脚本生成类
class ScriptGenerator:
    def __init__(self, configuration: Configuration) -> None:
        self.conf = configuration
        self.imagebase = idaapi.get_imagebase()
 
    @staticmethod
    def get_idb_filename():
        return os.path.basename(idaapi.get_input_file_path())
 
    @staticmethod
    def get_idb_path():
        return os.path.dirname(idaapi.get_input_file_path())
 
    # 根据地址获取函数的名字
    def get_function_name(self,
                          ea):  # https://hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml
        """
        Get the real function name
        """
        # Try to demangle
        function_name = idc.demangle_name(idc.get_func_name(ea), idc.get_inf_attr(idc.INF_SHORT_DN))
 
        # if function_name:
        #    function_name = function_name.split("(")[0]
 
        # Function name is not mangled
        if not function_name:
            function_name = idc.get_func_name(ea)
 
        if not function_name:
            function_name = idc.get_name(ea, ida_name.GN_VISIBLE)
 
        # If we still have no function name, make one up. Format is - 'UNKN_FNC_4120000'
        if not function_name:
            function_name = "UNKN_FNC_%s" % hex(ea)
 
        return function_name
 
    #替换模板中某些字符串
    def generate_stub(self, repdata: dict):
        s = self.conf.template
        for key, v in repdata.items():
            s = s.replace("[%s]" % key, v)
        return s
 
    # 根据地址列表来生成模板
    def generate_for_funcs(self, func_addr_list) -> str:
        stubs = []
        for func_addr in func_addr_list:
            dec_func = idaapi.decompile(func_addr)
            repdata = {
                "filename": self.get_idb_filename(),
                "funcname": self.get_function_name(func_addr),
                "offset": hex(func_addr - self.imagebase),
                "nargs": hex(dec_func.type.get_nargs())
            }
            stubs.append(self.generate_stub(repdata))
        return "\n".join(stubs)
 
    # 把生成frida脚本 保存到文件 并且置剪辑版
    def generate_for_funcs_to_file(self, func_addr_list, filename) -> bool:
        data = self.generate_for_funcs(func_addr_list)
        try:
            open(filename, "w").write(data)
            print("The generated Frida script has been exported to the file: ", filename)
        except Exception as e:
            print(e)
            return False
        try:
            QApplication.clipboard().setText(data)
            print("The generated Frida script has been copied to the clipboard!")
        except Exception as e:
            print(e)
            return False
        return True
 
 
class Frida:
    def __init__(self, conf: Configuration) -> None:
        self.conf = conf
 
# 菜单动作类 该方法首先判断当前窗口是否是函数列表、伪代码或反汇编窗口(通过检查 ctx.form_type 属性),如果是,则将当前菜单项关联到右键弹出菜单,并返回 AST_ENABLE_FOR_WIDGET;否则返回 AST_DISABLE_FOR_WIDGET。 如果菜单项与右键弹出菜单关联成功,用户就可以在相应的窗口右键单击并选择该菜单项来触发菜单操作。在 activate 方法中可以实现具体的菜单操作逻辑。
class IDAFridaMenuAction(Action):
    TopDescription = "IDAFrida"
 
    def __init__(self):
        super(IDAFridaMenuAction, self).__init__()
 
    def activate(self, ctx) -> None:
        raise NotImplemented
 
    def update(self, ctx) -> None:
        if ctx.form_type == idaapi.BWN_FUNCS or ctx.form_type==idaapi.BWN_PSEUDOCODE or ctx.form_type==idaapi.BWN_DISASM:
            idaapi.attach_action_to_popup(ctx.widget, None, self.name, self.TopDescription + "/")
            return idaapi.AST_ENABLE_FOR_WIDGET
        return idaapi.AST_DISABLE_FOR_WIDGET
 
 
# 继承菜单动作类
class GenerateFridaHookScript(IDAFridaMenuAction):
    description = "Generate Frida Script"
 
    def __init__(self):
        super(GenerateFridaHookScript, self).__init__()
 
    #在activate方法中,使用ScriptGenerator类生成Frida脚本,并将其写入到文件中。如果当前活动窗口类型是函数窗口(BWN_FUNCS),则选择被选中的函数,否则只选择当前屏幕所在函数作为目标。最后调用gen.generate_for_funcs_to_file方法生成脚本并写入到指定的文件中
    def activate(self, ctx):
        gen = ScriptGenerator(global_config)
        idb_path = os.path.dirname(idaapi.get_input_file_path())
        out_file = os.path.join(idb_path, "IDAhook.js")
        if ctx.form_type==idaapi.BWN_FUNCS:
            selected = [idaapi.getn_func(idx).start_ea for idx in ctx.chooser_selection]
            #from "idaapi.getn_func(idx - 1)" to "idaapi.getn_func(idx)"
        else:
            selected=[idaapi.get_func(idaapi.get_screen_ea()).start_ea]
        gen.generate_for_funcs_to_file(selected, out_file)
 
class ViewFridaTemplate(IDAFridaMenuAction):
    description = "View Frida Template"
 
    def __init__(self):
        super(ViewFridaTemplate, self).__init__()
 
    def activate(self, ctx):
        ui = ConfigurationUI(global_config)
        ui.show()
        ui.exec_()
 
class RunGeneratedScript(IDAFridaMenuAction):
    description = "Run Generated Script"
 
    def __init__(self):
        super(RunGeneratedScript, self).__init__()
 
    def activate(self, ctx):
        print("template")
 
# 设置执行命令
class SetFridaRunCommand(IDAFridaMenuAction):
    description = "Set Frida Command"
 
    def __init__(self):
        super(SetFridaRunCommand, self).__init__()
 
    def activate(self, ctx):
        print("template")
 
action_manager.register(GenerateFridaHookScript())
# action_manager.register(RunGeneratedScript())
action_manager.register(ViewFridaTemplate())
# action_manager.register(SetFridaRunCommand())

总的来说,这个脚本实现了一个较为完整的 IDA Pro 和 Frida 集成工具,并提供了一些 UI 功能和便捷的函数接口,使得逆向工程师能够更加高效地进行动态调试和分析。

MyFridaPlugin

通过上面两个例子,我们已经学习了frida模板的生成 以及如何和ida的ui交互 这样我们就可以自己写个插件 整合上面的代码 并且可以添加自己想要的功能

 

图片描述

 

源码: https://github.com/AnxiangLemon/MyIdaFrida


[培训]《安卓高级研修班(网课)》月薪三万计划

收藏
点赞7
打赏
分享
最新回复 (3)
雪    币: 10545
活跃值: (3936)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jgs 2023-3-16 12:23
2
0
收藏,学习,谢谢楼主分享
雪    币: 1382
活跃值: (3100)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小希希 2023-3-17 11:00
3
0
学习,感谢分享
雪    币: 40
活跃值: (1406)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
快乐的小跳蛙 2023-5-31 14:31
4
0

学习,感谢分享

最后于 2023-6-2 18:13 被快乐的小跳蛙编辑 ,原因:
游客
登录 | 注册 方可回帖
返回