今天通过两个调试例子:Windows的exe程序和Android的so库,来了解IDA的调试界面和一些调试命令。
调试 Windows 程序
在静态分析篇中 ,我们在示例文件中猜测 sub_140001834
这个函数是将用户的输入存放到一个字符数组中,现在我们就可以使用动态调试来验证一下我们的猜想。
IDA 不仅可以调试汇编,还可以调试它反编译的伪代码,这就意味着我们可以将目标对象当作一个吊毛同事写的代码,然后进行调试。
首先, 我们在需要断点的地方下一个断点,就跟平时调试一样,没啥区别:
在想要下断点的行数左边那个青色小点那里点一下,当前行就会变红,表明这行下了一个断点,这个操作和普通的 IDE 是一样的,或者你也可以按 F2。
由于 IDA 可以调试的程序类型非常之多(但是每个的操作都是统一的,没有学习成本,很吊啊),所以在调试前,我们需要选择调试器的类型:
选择调试器: 菜单项 [Debugger] → [Select debugger…] → 选择 [Local Windows debugger]
选择了之后,我们再看菜单项 [Debugger] :
首先,最下面是切换调试器,当你不小心点错了,那么就选这个切换一下就行。
然后我们介绍两个最常用的选项:
- Start process,这个会重新运行目标程序
- Attach to process,这个选项会根据不同的调试程序类型有不同的行为。
-
- 如果是一个可执行文件,IDA将显示所有与被调试程序同名的进程列表。如有一个都没有的话,会显示每一个进程列表让你自己选择。
- 如果是一个共享库,在 Windows 平台上,IDA 会过滤出所有加载了该共享库的进程。在其他平台上,不会过滤,会让你自行选择。
Start to process 与 Attach to process,还有一个区别,就是我们平时调试经常会遇到的,当我们想调试一个启动阶段的代码时,就只能选择 Start to process。
Process options,这个就是给程序设置一些启动参数,远程调试的时候,可能还需要设置一下密码等。
暂时先介绍这些,我们选择 Start process 重新启动一个程序,在我们的例子中,选择两个都行,有兴趣的可以自己都试试。
当我们点击 Start to process 后,会弹出一个警告窗口,不要慌,不是我们哪里操作错了,而是 IDA 给了一个友善的提醒:
这是因为,当使用IDA来分析恶意软件的时候,调试机器很容易受到感染。所以任何时候,以 Start to process 的方式启动新进程,都会弹这个窗。
如果你比较慌,选择了 NO,那么菜单里面的 Debugger 选项会消失,直到你重新启动 IDA,所以我们直接 Yes,虚拟机怕啥,而且还有快照功能,大不了时光回溯。
点击 Yes 之后,IDA启动了这个程序,我们输入用户名和密码:
按 enter 键,等待程序运行到断点位置:
可以看到,这一行变色了,那么我们如何看 v8 与 v9 的值呢?非常的简单,对准变量,双击左键就行了:
我们可以看到栈上有存我们输入的用户名(得了解C/C++变量在内存中储存方式),以同样的方式也可以看到密码。所以,我们的猜测是得到了验证。
IDA 调试界面的窗口
当IDA进入调试模式的时候,将会显示出6个默认窗口和一个调试工具栏。
7 窗口是调试工具栏,在调试模式中,调试工具栏会替换反汇编工具栏。它里面有很多标准工具可以使用,包括进程控制工具与断点操纵工具。
1 窗口是默认的反汇编窗口,我们从伪代码模式切换成文本模式可以看到当前指令的左边标记了一个 RIP 寄存器:
这是因为 IDA 检测到了有寄存器的指向了该窗口的一个内存位置。
默认情况下,IDA 以红色突出显示断点,以蓝色突出显示下一条要执行的指令,上图中,标记的就是下一条待执行的指令。
2 窗口是栈窗口,主要用于显示进程运行时栈的数据内容。所有指向栈的寄存器,在窗口3中也都会标记出来:
如果栈里面是内存地址,IDA会尝试将该地址解析为函数地址。如果栈里面是数据,则会显示引用数据。
如果我们想跳转到栈顶,可以点击通用寄存器中的 rsp 的蓝色拐弯箭头,栈底就是 rbp 咯。
3 窗口是寄存器窗口,默认显示通用寄存器,但是我们可以在窗口空白位置右键,查看其他寄存器:
寄存器的右边有的会有一个蓝色的拐弯箭头,这个可以用来跳转到具体的地址位置,比如,我先在 IDA View-RIP 窗口里鼠标左键点一下:
然后点一下寄存器 RDX 的蓝色箭头:
我们跳转到了 RDX 指向的位置。这个操作对 4 窗口也生效。
双击寄存器,还可以修改该寄存器的值:
4 窗口是十六进制窗口,表示的是程序的内存。使用频率会很高。
数据窗口跟随寄存器自动切换地址:右键 → [sycnc with] → 选择跟随寄存器,当我们发现某个寄存器一直在操作内存,我们就可以使用该方式看它搞了啥。
5 窗口是当前进程中加载的模块列表,双击其中任何一个,可以看到该模块的导出函数。
6 窗口是当前进程中的线程列表,双击其中任何一个线程,可以跳转到所选线程中的当前指令,同时寄存器窗口会更新为选中线程的。
进程控制
其实和我们平时调试程序是差不多的。
- 继续/F9,继续执行一个暂停的进程,直到遇到下一个断点,用户暂停/终止或者进行自行终止
- 暂停,这个需要在工具栏上操作,暂停一个正在运行的进程。
- 终止(Ctrl + F2),终止一个正在运行的进程。
- 步过(F7):执行一条指令(反汇编窗口) / 执行一行语句(伪代码),不进入函数调用
- 步入(F8):执行一条指令(反汇编窗口) / 执行一行语句(伪代码),进入函数调用
- 执行到返回(Ctrl+F7):执行到当前函数的返回后位置(步入了xx函数,快速步出的方法)
- 执行到光标(F4):执行到光标位置停下来(适用于跳出循环等操作…)
- 运行直到断点(F9):运行
- 设置程序计数器(Ctrl + N):强制代码从光标位置处开始执行,不太清楚会不会导致程序错误,暂未尝试,因为只设置了 RIP,没正确设置其他寄存器,当进入函数时,有可能因为参数等问题报错,除非是用于函数内部走分支。
调试动态链接库
动态链接库调试很简单,只需要调试动态链接库的宿主程序即可,IDA 会自动识别动态链接库。
我们以 Android so 调试为例,首先进入 ida 的 dbgsrv 目录,这是因为IDA它不能直接调试Android程序,而是需要用代理的方式来调试,这个和 frida 是差不多的。
我们选择一个对应平台的文件,我这里是 arm64:
上传到手机的 /data/local/tmp(这个目录没要求,想放哪里放哪里)。
上传完成后,进入该目录,切换到 root 用户(因为调试其他程序需要root权限),添加可执行权限,运行起来:
bash复制代码./android_server64
输出结果如下:
scss复制代码IDA Android 64-bit remote debug server(ST) v7.7.27. Hex-Rays (c) 2004-2022
Listening on 0.0.0.0:23946 (my ip 172.217.160.106)...
如果我们的手机与IDA在同一个局域网内,是可以直接通过 ip + 23946 端口连上并调试的,但是网络调试的方式还是比较慢的,所以我们可以搞一个端口转发,让 adb 自动帮我们传输调试数据,这样就不用通过网络传输,而是通过 adb 传输。
复制代码adb forward tcp:23946 tcp:23946
我们先使用 IDA 加载需要分析的 so 文件,最好是等 IDA 分析完成后再调试比较好,因为这样反汇编代码会更精确。
调试程序放到了 p16
我们选择调试下面这个函数,因为它点击按钮后会运行,比较好操作:
IDA 分析完 so 之后,我们在上面的函数里面,选择一个感兴趣的地方打上断点,在 debugger 菜单下选择附加到进程(因为这里我暂时还不清楚如何启动一个android程序),这里还需要设置启动参数,看下一篇内容:
Ctrl + F 搜索目标进程名,选中进程,点击OK即可。经过一段时间的等待(主要是我用的 wifi adb),IDA 弹出一个窗口,询问它在进程找到的 so 是不是你想调试的那个 so,点击 same 就ok了。
Android 的 so 调试时,会先在 libc.so 的这里停下,暂时不知道为啥,我们继续程序。
操作 app,发现程序运行到了我们下断点的位置:
整个过程还是比较简单的。
之前一篇文章《Model Context Protocol(MCP) 编程极速入门》我们讲的都是如何使用本地的 MCP 服务,但是有时我们希望直接把 MCP 服务部署到云端来直接调用,就省去了本地下载启动的烦恼了。此时,我们就需要来使用 MCP 的 SSE 的协议来实现了。
此时,我们先来写 SSE 协议的 MCP 服务。实现起来很简单,只需要将我们最后的 run
命令中的 transport
参数设置为 sse
即可。下面还是以上面的网络搜索为例子,来实现一下 ,具体代码如下:
python 代码解读复制代码# sse_web_search.py
import httpx
from mcp.server import FastMCP
app = FastMCP('web-search', port=9000)
@app.tool()
async def web_search(query: str) -> str:
"""
搜索互联网内容
Args:
query: 要搜索内容
Returns:
搜索结果的总结
"""
async with httpx.AsyncClient() as client:
response = await client.post(
'https://open.bigmodel.cn/api/paas/v4/tools',
headers={
'Authorization': 'YOUR API KEY'},
json={
'tool': 'web-search-pro',
'messages': [
{'role': 'user', 'content': query}
],
'stream': False
}
)
res_data = []
for choice in response.json()['choices']:
for message in choice['message']['tool_calls']:
search_results = message.get('search_result')
if not search_results:
continue
for result in search_results:
res_data.append(result['content'])
return '\n\n\n'.join(res_data)
if __name__ == "__main__":
app.run(transport='sse')
在 FastMCP
中,有几个可以设置 SSE 协议相关的参数:
- host: 服务地址,默认为
0.0.0.0
- port: 服务端口,默认为 8000。上述代码中,我设置为
9000
- sse_path:sse 的路由,默认为
/sse
此时,我们就可以直接写一个客户端的代码来进行测试了。具体代码如下:
python 代码解读复制代码import asyncio
from mcp.client.sse import sse_client
from mcp import ClientSession
async def main():
async with sse_client('http://localhost:9000/sse') as streams:
async with ClientSession(*streams) as session:
await session.initialize()
res = await session.call_tool('web_search', {'query': '杭州今天天气'})
print(res)
if __name__ == '__main__':
asyncio.run(main())
我们可以看到,他正常工作了,并搜索到了内容:
当然,我们也可以使用 mcp dev sse_web_search.py
的方式来测试。这里要注意的是,Transport Type
需要改成 SSE
,然后下面填写我们的本地服务地址。
当一切都测试没有问题后,我们就来将他通过 severless 的方式来部署到云端。这里我们选择的是阿里云的函数计算服务。首先我们先进入到阿里云的 函数计算 FC 3.0
的 函数
菜单,并点击 创建函数
来创建我们的服务。地址是:fcnext.console.aliyun.com/cn-hangzhou…
我们这里选择 Web函数
,运行环境我们选择 Python 10
。代码上传方式这里可以根据大家需求来,因为我这里就一个 python 文件,所以我这里就直接选择使用示例代码
了,这样我后面直接把我的代码覆盖进去了就行了。启动命令和监听端口我这里都保留为默认(端口需要和代码中一致)。
环境变量大家可以将代码中用到的 apikey 可以设置为一个环境变量,这里我就不设置了。最后设置完成截图如下:
在高级设置中,为了方便调试,我启动了日志功能。
设置完成后,点创建即可。他就跳转到代码编辑部分,然后我们把之前的代码复制进去即可。
完成后,我们来安装下依赖。我们点击右上角的编辑层
。这里默认会有个默认的 flask 的层,因为开始的模板用的是 flask,这里我们就不需要了。我们删除他,再添加一个 mcp 的层。选择添加官方公共层
,然后搜索 mcp
就能看到了一个 python 版的 MCP 层,里面包含了 MCP 所有用到的依赖。
如果你还有其他第三方的,可以先搜索下看看公共层中是否有,没有就可以自行构建一个自定义的层。点击这里就可以,只需要提供一个 requirements
列表就可以了,这里就不赘述了。
当我们都设置完成后,点击右下角的部署即可。
然后我们又回到了我们代码编辑的页面,此时,我们再点击左上角的部署代码。稍等一两秒就会提示代码部署成功。此时,我们的 MCP 服务就被部署到了云端。
然后,我们切换到配置
的触发器
中,就可以看到我们用来访问的 URL 地址了。当然,你也可以绑定自己的域名。
然后,我们就可以用我们上面的客户端代码进行测试了。
python 代码解读复制代码import asyncio
from mcp.client.sse import sse_client
from mcp import ClientSession
async def main():
async with sse_client('https://mcp-test-whhergsbso.cn-hangzhou.fcapp.run/sse') as streams:
async with ClientSession(*streams) as session:
await session.initialize()
res = await session.call_tool('web_search', {'query': '杭州今天天气'})
print(res)
if __name__ == '__main__':
asyncio.run(main())
如果我们发现在客户端有报也不用慌,我们可以直接在日志中找到对应出错的请求点击请求日志
查看报错来修复。
到这里,我们的 MCP 服务就被部署到了云端,我们就可以在任何地方直接来使用它了。
比如,在 Cherry-Studio
中,我们可以这样来设置:
在 Cline
中:
在 Cursor
中:
json 代码解读复制代码{
"mcpServers": {
"web-search": {
"url": "https://mcp-test-whhergsbso.cn-hangzhou.fcapp.run/sse"
}
}
}
更多教程请查看:github.com/liaokongVFX…
评论记录:
回复评论: