首页
实用工具
我的旅程
在线壁纸
更多
✒️ 问题反馈
📦 文章统计
🌍 国内镜像
🎬 次元视界
📒 流水账本
🎨 在线 PS
推荐
🕵️ 开源情报
🌆 图片压缩
🍭 资产清洗
💡 我的作品
👤 关于站长
⚔️ 次 元 剑
搜索
1
【工具分享】逆向工具箱 - 次元剑
89,537 阅读
2
【技术分享】PE文件结构分析 ( RVA转FOA )
7,350 阅读
3
【技术分享】NASM x86 Assembly Language
5,982 阅读
4
【技术分享】CK竞技之王游戏辅助制作
3,725 阅读
5
【每日随记】天涯明月刀无限飞修改思路
2,816 阅读
技术分享
CTF解题
英语笔记
数学笔记
网络通信
每日随记
攻防技术
工具分享
Search
标签搜索
Windows
Web安全
Python3
Linux
逆向工程
CTF
红队技术
人工智能
C/C++
黑客工具
Go
密码学
二进制安全
数学
漏洞挖掘
Android
eNSP
渗透测试
蓝队技术
黑客大会
发光的神
累计撰写
160
篇文章
累计收到
103
条评论
首页
栏目
技术分享
CTF解题
英语笔记
数学笔记
网络通信
每日随记
攻防技术
工具分享
页面
实用工具
我的旅程
在线壁纸
✒️ 问题反馈
📦 文章统计
🌍 国内镜像
🎬 次元视界
📒 流水账本
🎨 在线 PS
推荐
🕵️ 开源情报
🌆 图片压缩
🍭 资产清洗
💡 我的作品
👤 关于站长
⚔️ 次 元 剑
搜索到
52
篇与
的结果
2026-07-05
【工具分享】V免签支付 + vPay 自动发卡系统
简介V免签 是一款私有支付网关,配合 Android 手机监听微信/支付宝到账通知,无需企业资质即可接入支付。vPay 发卡 是本人从零开发的 PHP 原生虚拟商品自动售卡系统。两者独立开源,支持微信支付宝收款、可组合使用,部署后即可搭建一套完整的自动发卡平台。工作流程: 买家下单 → vPay 展示二维码 → 买家扫码支付 → V免签 Android 端监听到账 → 回调 vPay → vPay 自动匹配订单、扣减库存、发送卡密邮件。V免签 (vmq)基于 ThinkPHP 5.1 的免签支付回调服务端。配合 Android 监控端,通过辅助功能监听微信和支付宝通知栏,检测到账后推送至服务端完成订单匹配与回调,一台旧安卓手机即可作为私有支付网关。网站后台本项目基于 szvone/vmqphp 重写,主要改进:安全修复(9 项)SQL 注入:字符串拼接改为 PDO 参数化查询SSRF:CURL 出站增加内网 IP 黑名单校验,禁用重定向跟随XSS:param 参数增加 htmlspecialchars() 过滤订单号注入:正则限制 [a-zA-Z0-9_-],最长 100 字符存储型 XSS:回调 URL 写入前 FILTER_SANITIZE_URL调试模式:app_debug = false硬编码密钥:示例代码改为从数据库动态读取远程代码风险:移除 checkUpdate() 外部拉取逻辑CDN 供应链:jQuery、Vue 等前端资源全部本地化功能改进支持子目录部署,APK 扫码自动识别路径支付页重写,骨架屏加载 + 中英文切换系统运行时间改为数据库记录,跨平台通用APK 安装包内置在后台,可直接下载{abtn icon="fa-github" color="#000000" href="https://github.com/soevai/vmq" radius="" content="Github - vmq | 点击进入" /} {abtn icon="fa-book" color="#e10000" href="https://www.52tt.pro/repo/vmq/" radius="" content="在线 Demo 体验 | 点击进入"/}vPay 发卡 (vPayFaka)网站首页收款弹窗从零构建的 PHP 原生虚拟商品发卡系统,对接 V免签支付。零外部依赖——没有 composer、没有 npm、没有 CDN,文件上传到服务器即可运行,全栈自研,不基于任何现有框架或项目。主要特性商品管理:支持分类、多规格 SKU、库存自动统计自动发卡:SELECT ... FOR UPDATE 行级锁 + 事务,杜绝并发超卖一键安装:首次访问自动跳转安装向导,建库建表写配置邮件通知:支持 SMTP 直发和自定义 API(HMAC-SHA256 签名)超时取消:超时未支付自动取消并返还库存安全措施密码:bcrypt 哈希存储CSRF:random_bytes(32) 令牌 + hash_equals 常量时间比对SQL 注入:全 PDO 预处理 + 参数绑定回调校验:MD5 签名 + 300 秒时间戳窗口 + Nonce 文件去重频率限制:登录失败 5 次锁 3 分钟,购买每 IP 每小时 5 次HTTP 安全头:CSP、X-Frame-Options、X-Content-Type-Options{abtn icon="fa-github" color="#000000" href="https://github.com/soevai/vPayFaka" radius="" content="Github - vPayFaka | 点击进入" /} {abtn icon="fa-book" color="#e10000" href="https://www.52tt.pro/repo/vpay/" radius="" content="在线 Demo 体验 | 点击进入"/}协同方式两个项目独立开源,可单独或组合使用,对接只需在 vPay 后台填入 V免签的服务端地址和通讯密钥。V免签(监听收款)→ notify.php 回调 → vPay 发卡(匹配订单、提取卡密、发送邮件)详细的 API 文档、数据库结构、部署步骤和定时任务配置见各项目 GitHub README。免责声明两个项目均为开源学习用途,请勿用于任何违法违规场景,使用者自行承担相应责任。
2026年07月05日
255 阅读
0 评论
8 点赞
2026-02-24
【技术分享】IDA+Jadx+Trae MCP 配置教程
简介做逆向分析的时候,大家肯定都有这种体验:在 IDA Pro、Jadx 之间来回切,手动复制内存地址、对着代码逐行比对,不仅慢得让人着急,还特别容易出错。而 MCP(Model Context Protocol,模型上下文协议)就是来解决这个问题的!它能把 IDA Pro、Jadx 和 Trae AI 完美连起来,实现工具间的数据互通、操作联动。今天这篇教程就手把手教你配置这套环境,让你告别工具割裂的麻烦,用 AI 一站式搞定逆向分析。一、环境准备为了避免版本不兼容踩坑,建议大家直接照着下面的环境来搭,亲测稳定不翻车:操作系统:Windows 11版本控制:Git运行环境:Python 3.11.8(重点!别用其他版本)逆向工具:IDA Pro 8.3AI 客户端:Trae AI二、Git 安装Git 是安装 ida-pro-mcp 的必需品,主要用来从 GitHub 拉取插件源码,步骤超简单:1.打开浏览器,访问 Git 官网下载 Windows 版本:https://git-scm.com/install/windows2.双击安装包,一路点“下一步”,默认配置完全够用,不用折腾3.安装完成后,按 Win+R 输入 cmd 打开命令行,敲入 git --version,如果能显示版本号,就说明装好了{abtn icon="fa-git-square" color="#000" href="https://git-scm.com/install/windows" radius="" content="Git 官网"/}三、Trae 联动 IDA Proida-pro-mcp 是连接 IDA Pro 和 MCP 协议的核心,有了它,Trae AI 才能直接调用 IDA 的分析能力,我们一步步来:步骤 1:安装 ida-pro-mcp首先右键点击“命令提示符”,选择“以管理员身份运行”(权限不够会装失败),然后复制下面的命令敲进去:pip install --upgrade git+https://github.com/mrexodia/ida-pro-mcp这里提个醒:如果碰到网络超时、连接重置的情况,就是访问 GitHub 受限了。直接去仓库地址(https://github.com/mrexodia/ida-pro-mcp)下载 ZIP 包,解压后进入解压目录,执行 pip install . 就能本地安装了。步骤 2:安装 IDA 插件插件装完后,不用手动拷贝文件,在刚才的管理员命令行里继续敲:ida-pro-mcp.exe --install执行成功后,插件会自动放到 IDA Pro 的插件目录里,以后打开 IDA,MCP 服务就会自动加载,特别省心。步骤 3:Trae AI MCP 配置打开 Trae AI,找到 MCP 服务器的配置页面,把下面的 JSON 代码完整复制进去就行:四、联动使用流程1.如果是使用工具包,第一次运行 IDA 需要以管理员身份运行一次 IDA_Pro_8.3_绿化工具.exe 初始化下 IDA。2.然后去 IDA 安装目录里找 idapyswitch.exe 并运行,把 Python 版本切换到 3.11.8,这样 IDA 才能正常加载 MCP 插件。3.先打开 IDA Pro,加载你要分析的程序(比如 CrackMe),等底部状态栏显示“Ready”,就说明反汇编完成了。然后点击顶部的「Edit」→「Plugins」→「MCP」,开启 MCP 服务,这时 IDA 就会在 13337 端口等着 Trae 连接了。4.切回 Trae AI,在 MCP 配置页面确认一下之前的配置没错,完成连接设置。5.把执行 ida-pro-mcp --config 命令得到的配置信息,手动填到 Trae AI 的 MCP 配置里,确保参数都对得上。MCP 配置{ "mcpServers": { "ida-pro-mcp": { "command": "C:\\Program Files\\Python311\\python.exe", "args": [ "C:\\Program Files\\Python311\\Lib\\site-packages\\ida_pro_mcp\\server.py", "--ida-rpc", "http://127.0.0.1:13337" ] } } }简单说下配置里的关键点,别配错了:command:填你安装 Python 3.11.8 的路径,必须和装插件时的 Python 一致args:固定写法,不用改,作用是启动 MCP 服务端,连接 IDA 的 13337 端口保存之后,Trae AI 就会自动尝试连接 IDA 的 MCP 服务了6.在 Trae AI 里,把智能体切换成「Builder with MCP」模式,这个模式才支持调用 IDA 的服务。7.最后一步,在 Trae AI 的聊天框里输入下面这句话,让 AI 开始工作:我已经用ida打开了一个程序,请用mcp的方式分析并输出flag8.稍等一会儿,Trae AI 就会自动读取 IDA 里的代码信息,分析加密逻辑,最后直接把正确的 Flag 给你输出来,全程不用手动操作!五、Trae 联动 Jadx1.Jadx 插件可在线下载;若访问受限,前往仓库手动获取:https://github.com/zinja-coder/jadx-ai-mcp。手动安装(推荐)1.将 jadx-ai-mcp-6.1.0.jar、jadx-mcp-server-v6.1.0.zip 放入 Jadx 根目录,并手动选择安装 jadx-ai-mcp-6.1.0.jar。2.解压 jadx-mcp-server-v6.1.0.zip,命令行进入解压目录,执行:pip install -r requirements.txtMCP 配置{ "mcpServers": { "jadx-mcp-server": { "command": "C:\\PROGRA~1\\Python311\\python.exe", "args": [ "D:\\VoxShadow\\Jdax\\jadx-mcp-server-v6.1.0\\jadx_mcp_server.py" ] } } }2.按实际路径修改 command 与 args,复制到 Trae AI MCP 配置页(同 IDA 配置方式)。Trae AI 切换到「Builder with MCP」模式,发送指令:我已经用jadx打开了一个程序,请用mcp的方式分析并输出flag3.AI 自动读取 Jadx 代码、分析逻辑并输出 Flag,也可以进行黑盒代码审计了。六、整合包下载用到的软件我都已经打包好了,下载就能按照文章进行配置使用。{cloud title="IDA+Jadx+Trae工具包" type="bd" url="https://pan.baidu.com/s/1PP2sbVkREX8gvgKb8h4eHQ?pwd=2085" password="2085"/}
2026年02月24日
940 阅读
0 评论
339 点赞
2025-12-30
此内容被密码保护
加密文章,请前往内页查看详情
2025年12月30日
1,562 阅读
0 评论
567 点赞
2025-10-08
【工具分享】天阶藏宝阁 - 元创篇
由来在浮世万象的数据洪流之上,有一座隐于虚空的天阶藏宝阁。 传说,那是“发光的神”静坐的所在,代码化作灵气,程序如宝玉悬浮其间。 每一件神器(工具、系统、源码)皆被悉心收录,化作一页灵卷,闪烁着独属于创作者的灵光。 此阁不仅是展示,更是收藏与传承的容器。 它象征着「秩序与灵感」的平衡——让代码与艺术并存,让开发者的世界观得以具象化。 在未来,天阶藏宝阁将继续扩展更多形态,化为次元空间的文库,收纳万象神器,照亮无数创作者的道路。 「浮世万象,皆藏于此阁中。灵光不灭,万物归一。」—— 发光的神 · 天阶阁主{mtitle title="剑指九天,问道苍穹"/}神兵图鉴 .lantern { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 11px 10px; border-radius: 16px; color: #2a1c0a; font-weight: 900; letter-spacing: 0.16em; background: radial-gradient(120px 40px at 10% 20%, #fff7d6, #ffe08a 60%, #f3d27a 95%); border: 1px solid #e5c56a; box-shadow: 0 0 26px rgba(243,210,122,.38), inset 0 0 10px rgba(255,245,210,.7); cursor: pointer; text-decoration: none; transition: all .35s ease; } .lantern:hover { transform: translateY(-3px); box-shadow: 0 0 40px rgba(243,210,122,.55), inset 0 0 16px rgba(255,250,220,.9); background: radial-gradient(120px 40px at 15% 30%, #fff8d9, #ffd76e 60%, #f3d27a 90%); } .lantern:active { transform: translateY(0); box-shadow: 0 0 18px rgba(243,210,122,.4), inset 0 0 8px rgba(255,240,190,.7); } 🏮进入藏宝阁
2025年10月08日
1,651 阅读
3 评论
306 点赞
2024-11-21
【技术分享】PE文件结构分析 ( RVA转FOA )
简介在了解PE结构之前,先简要回顾下可执行文件的概念。可执行文件是可以被操作系统加载并运行的文件。Windows与Linux的可执行文件格式:Windows平台的可执行文件格式是 PE(Portable Executable) 文件结构。Linux平台的可执行文件格式是 ELF(Executable and Linking Format) 文件结构。命名含义PE 的全称是 Portable Executable,意为“便携的可执行文件”。ELF 的全称是 Executable and Linking Format,意为“可执行和链接格式”。差异点PE文件结构 是跨版本兼容的。例如,Windows 7上的可执行文件可以在Windows 8、Windows 10等版本中正常运行。ELF文件结构 则依赖于内核版本,使用不同内核编译的程序无法在不兼容的环境中运行。应用场景PE文件结构在以下领域具有重要意义:病毒和反病毒技术:病毒感染机制和杀毒软件的检测都需要深入理解PE结构。外挂与反外挂:外挂注入与反外挂保护的关键技术点通常基于PE文件操作。加壳与脱壳:保护软件代码的壳技术,以及针对壳保护的破解技术都依赖于对PE结构的掌握。逆向与修改:在没有源码的情况下对程序功能进行修改、汉化或调试也需要了解PE文件的内部结构。PE结构表PE文件结构.pdfPE结构分析DOS_HANDER32位操作系统,每个进程的虚拟地址空间只能访问4GB的内存,其中2GB是在用户空间,另外2GB是在内核空间,对于64位操作系统,每个进程的虚拟地址空间可以访问更大的内存,一般将用户空间分配给进程可用的全部内存,而内核空间仍然是2GB。(题外话)用16进制编辑器打开程序,一行16进制有16个字节,而DOS_HANDER是给16位计算机用的,到现在的32位和64位程序依然保留了这个数据结构,详细参数可参考以上的PE结构表,在 DOS_HANDER 唯一重要的两个参数是 e_magic 和 e_lfanew,一个是标记用来判断是否是可执行程序,第二个是用来定位PE头位置的,其余的参数都可以用 00,nop 填充因为用不到。DOS_SUBDOS_SUB是在DOS_HANDER和PE标准头的中间这一块,这里面的数据也可以00,nop填充掉当然也可以插入自己的数据,如恶意代码。PE_HANDER以上 DOS_HANDER 中的 e_lfanew 参数是存储的是 08 01 在PE结构中存在大小端,要反着来 08 01 反过来是 0108 也就是 108 对应的文件偏移定位到 50 48 00 00 这里就是PE头了,PE标识占 4 个字节,而标准PE头占 20 个字节,可以参考以上PE表,第一个参数是 Machine 这个参数可以区分程序是多少位的,可以看到是 4C 01 反过来是 014c 也就是 14c 那么是 32位程序了,64位程序就是 8664。PE扩展头65 C8 05 00 反过来 0005c865,就是这个 AddressOfEntryPoint 程序入口地址,ImageBase 是 00 00 40 00 反过来就是 00400000,剩下的结构可以参考PE结构表。PE地址转换介绍FOV (File Offset Value):文件偏移量值,指的是 PE 文件中某个数据在文件中的偏移地址。RVA (Relative Virtual Address):相对虚拟地址,指的是 PE 文件中某个数据在内存中的地址与该模块基地址之间的相对距离。VA (Virtual Address):虚拟地址,指的是 PE 文件中某个数据在内存中的绝对地址。FOV文件偏移计算FOV = RVA - Virtual Offset + Raw Offset在Windows可执行文件中,RVA(Relative Virtual Address)是一个虚拟地址相对于程序映像基址的偏移量,程序映像基址是可执行文件在内存中加载时分配的基地址,通常是0x400000或0x10000000,有了FOV偏移就能够定位,在PE文件中代码的位置,直接修改二进制文件实现程序逻辑的修改。好,我们先来找一下 AddressOfEntryPoint 程序入口,运行后机器码的位置。通过studyPE+这个工具,可以对程序的PE结构进行展示,这里的 0001500 就是RVA。然后就是区段了,Virtual Address 是用来描述PE文件中数据段在程序运行时被加载到内存中的虚拟地址,而 Raw Address 是用来数据段在PE文件中的物理地址或字节偏移量。通过上面的东西进行公式计算,就能得到二进制文件的FOV偏移,与动态调试的入口地址机器码所对应。VA虚拟地址计算VA(Virtual Address,虚拟地址)是指被操作系统分配给进程的内存地址空间中的地址,也称为“逻辑地址”,在现代操作系统中,每个进程都有自己的虚拟地址空间,该地址空间通常从0开始,可以映射到物理内存中的实际地址。只要明白有了VA就能定位到程序运行后的虚拟内存地址了。VA = FOV + Virtual Offset - Raw Offset # 两个都能用 VA = RVA + ImageBase通过上面的公式就能拿到 400000 + 1500 = 401500,在程序运行后的虚拟内存地址。地址重定位计算这是在逆向工程中地址计算的一种常见方法,通常用于调试和静态分析工具之间的地址转换。下面是具体的解释和场景说明:在动态调试器(如 x64dbg)和静态分析工具(如 IDA Pro)中,二进制文件的地址表示方式会有所不同:动态调试器动态调试时,程序运行在内存中,操作系统会为程序的模块分配一个动态的加载基址(Base Address)。具体地址通常由模块加载地址加上文件中的偏移量(Offset)计算得出。静态分析工具静态分析工具基于文件本身的结构,不依赖运行时的加载基址。它直接将文件的Offset视为相对地址,结合用户手动输入的加载基址,推算出实际的运行地址。x64dbg 中的地址计算当前运行的内存地址(Current Address)模块加载地址(Module Base Address)公式:偏移量(Offset) = 当前地址 - 模块加载地址意义在于,找到一个地址相对于模块加载基址的偏移值,这个偏移值与模块在静态文件中的位置一致。在 IDA 中的地址计算假设在调试器中计算出某个地址的偏移量(Offset)。在IDA中,我们需要知道模块加载基址(Module Base Address)来还原其运行时的实际内存地址。公式:当前地址 = 模块加载基址 + 偏移量(Offset)这个公式的意义是,利用偏移量和加载基址还原实际内存中动态分配的地址。假设我们正在调试一个程序,模块加载基址是 0x400000,并且我们在 x64dbg 中发现当前执行的指令位于地址 0x402345。Step 1: 计算Offset使用公式:Offset = 当前地址 - 模块加载地址 Offset = 0x402345 - 0x400000 Offset = 0x2345Step 2: 转换到IDA中的地址假设我们在IDA中对这个模块进行分析,并假设它的加载基址为 0x500000。则在IDA中对应的实际地址为:当前地址 = 模块加载基址 + Offset 当前地址 = 0x500000 + 0x2345 当前地址 = 0x502345改全局变量用C写了简单的例子,输出变量数值和虚拟内存地址。#include <stdio.h> int value = 1000; int main () { printf("Address = 0x%p \n", &value); printf("value = %d", value); getchar(); return 0; }有全局变量的虚拟内存地址,就可以直接可以计算出RVA相对虚拟内存地址,然后通过节表的内存偏移与RVA进行计算,就能得到文件的FOV。(为什么不修改局部变量?首先局部变量存储在堆栈中,需要通过动态修改值,在PE文件结构中并没有堆栈,堆栈是程序运行时分配出来的,所以不能通过修改PE文件结构来改变局部变量的数据。)RVA = VA - ImageBase FOV = RVA - Virtual Offset + Raw Offset通过上面的公式 403010 - 400000 = 3010 得到RVA,然后算区间按照上面的第二个公式就能得出FOV,对照区段的虚拟地址可以发现,是在 .data 节表中。拿到文件偏移就可以通过进行定位了,用二进制的方式修改全局变量的数据。小知识范围 = 基址 + 大小通过基址 + 大小就可以计算出这个 exe 中的内存范围,现在验证下这里面的内存地址是否全是静态地址。当然,除了exe 也有 dll 的,有了这些静态地址,就可以借助加偏移找到动态地址。2024-12-20 08:53:14 星期五 更新过
2024年11月21日
7,350 阅读
0 评论
940 点赞
2024-11-20
【技术分享】CK竞技之王游戏辅助制作
简介以下教程是自我总结出的,我会很详细的进行编写,配详细的步骤和视频教程,本文章用到的工具会有 Cheat engine、x64dbg,本文章只用C语言编写代码,用单机游戏 CK竞技之王 进行辅助的制作,该游戏是CS进行魔改仿穿越火线,在此声明并不是穿越火线游戏,做这个教程目的只有一个让你们明白它的原理和搜法,在游戏中不管是网络游戏还是单机游戏,他们的共同点都是一样的,能够被本地修改达到开挂的目的(在此声明只是作为学习交流)。基本了解游戏它是程序,因为运行程序就会创建进程,通过进程就可以访问程序中的内存地址,通过地址可以访问到存储的数据,举个简单的例子:比如游戏开发者创建了个变量 float speed = 2.5; 来定义人物速度,那么就会在内存中开辟一个空间,然后会有一个内存地址像 0x12345 这样的(只是打比方),我们可以通过这个地址来访问到里面的数据(知道C语言指针的就很好理解),也就是 2.5,通过微软给的系统 API 接口 kernel32.dll 里的 ReadProcessMemory 函数就能办到,然后就是写内存数值了,可以通过 WriteProcessMemory 函数,要用到的API函数我都会写在下面的。取窗口句柄HWND FindWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName );取窗口句柄因为 ReadProcessMemory,第一个参数 hProcess 要的是进程句柄(注意了 FindWindow 取的是窗口句柄而不是进程句柄哦)。取进程线程DWORD GetWindowThreadProcessId( HWND hWnd, LPDWORD lpdwProcessId );通过 GetWindowThreadProcessId,来获取进程 PID 当然你直接获取进程 PID 就省去了找窗口。取进程句柄HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );通过 OpenProcess,打开进程来取进程句柄,这样就拿到进程句柄了。读内存数据BOOL ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead );通过 ReadProcessMemory 来读进程内存数据。写内存数据BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten );通过 WriteProcessMemory 来写进程内存数据,以上是简单介绍让大家有个基本了解(注意以上只能对 32 程序进程操作)。32位、64位内存读写内核级进程内存读NTSTATUS ZwWow64ReadVirtualMemory64( HANDLE Process, PVOID64 BaseAddress, PVOID Buffer, ULONG64 BufferSize, PULONG64 NumberOfBytesRead );内核级进程内存写NTSTATUS ZwWow64WriteVirtualMemory64( HANDLE Process, PVOID64 BaseAddress, PVOID Buffer, ULONG64 BufferSize, PULONG64 NumberOfBytesWritten );内核级别的进程内存读写,可以用在32和64位程序上,微软不公开的但是可以用,这两个函数不属于动态链接库,感兴趣的可以自己去研究。完整代码(读写子弹数量)// 读写内存简单例子: #include <Windows.h> // 包含头文件 Windows.h #include <stdio.h> int main() { DWORD processid; // 保存进程PID HANDLE hProcess; // 保存进程句柄 HWND hwnd = FindWindow("Valve001", NULL); // 取窗口句柄 GetWindowThreadProcessId(hwnd, &processid); // 取进程PID hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid); // 取进程句柄 if (hProcess == NULL) { printf("打开进程失败\n"); return 1; } int values; // 返回读取数据 SIZE_T bytesRead; // 存读取字节数 LPVOID Addresses = (LPVOID)0x7000FA4; // 临时地址读取 ReadProcessMemory(hProcess, Addresses, &values, sizeof(int), &bytesRead); // 读取内存地址数据 printf("子弹数量:%d\n", values); values = 100; SIZE_T bytesWritten; WriteProcessMemory(hProcess, Addresses, &values, sizeof(int), &bytesWritten); // 写内存地址数据 100 CloseHandle(hProcess); // 关闭句柄 return 0; }以上函数都在这个目录下:C:\Windows\System32\,这里面是存放的是 32 位动态链接库,上面的函数是 kernel32.dll 里面的,常用的 dll 我写在下面了,感兴趣的可以自己去看看。user32.dll // 提供了用户界面方面的功能,包括窗口、菜单、鼠标、键盘等。 kernel32.dll // 包含了许多核心的操作系统功能,如进程和线程管理、内存管理、文件操作等。 advapi32.dll // 提供了高级的系统和安全功能,如访问控制、认证等。 gdi32.dll // 提供了图形设备接口相关的功能,包括绘图和字体操作。 shell32.dll // 提供了与 Windows shell 相关的功能,如文件和文件夹操作、图标处理等。 comdlg32.dll // 提供了用于打开和保存对话框等通用的常见对话框。人物自瞄(C语言)#include <stdio.h> #include <math.h> #include <windows.h> #define PI 3.1415926 float GetDistance(float x1, float y1, float x2, float y2) { float distance = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); // 计算二维距离 return distance / 20.0f; } int main() { DWORD processid; HANDLE hProcess; HWND hwnd = FindWindow("Valve001", NULL); GetWindowThreadProcessId(hwnd, &processid); hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid); if (hProcess == NULL) { printf("打开进程失败\n"); return 1; } LPVOID myAddr[] = { // 临时地址 (LPVOID)0x0B466A44, // 自己x (LPVOID)0x0B466A4C, // 自己y (LPVOID)0x0B466A48, // 自己z }; LPVOID emAddr[] = { // 临时地址 (LPVOID)0x0B466D68, // 敌人x (LPVOID)0x0B466D70, // 敌人y (LPVOID)0x0B466D6C, // 敌人z }; LPVOID aimAddr[] = { // 临时地址 (LPVOID)0x02de10c8, // 准星 x (LPVOID)0x02de10c4, // 准星 y }; SIZE_T bytesRead; float Aimvalues[2]; float myvalues[3]; float envalues[3]; float differ_x, differ_y, distan; float AiMBot_x, AiMBot_y, differ; while (TRUE) { for (int i = 0; i < 3; i++) { ReadProcessMemory(hProcess, aimAddr[i], &Aimvalues[i], sizeof(float), &bytesRead); ReadProcessMemory(hProcess, myAddr[i], &myvalues[i], sizeof(float), &bytesRead); ReadProcessMemory(hProcess, emAddr[i], &envalues[i], sizeof(float), &bytesRead); } differ_x = envalues[0] - myvalues[0]; // 计算敌我坐标差 differ_y = envalues[2] - myvalues[2]; distan = sqrt(pow(differ_x, 2) + pow(differ_y, 2)); differ = GetDistance(myvalues[0], myvalues[2], envalues[0], envalues[2]); // 计算二维距离 AiMBot_y = atan((myvalues[1] - envalues[1]) / distan) / PI * 180 + 3; // 计算y轴方向上的角度 if (differ_x > 0 && differ_y > 0) // 判断第一象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI; // 计算x轴方向上的角度 } if (differ_x < 0 && differ_y > 0) // 判断第二象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI + 180; } if (differ_x < 0 && differ_y < 0) // 判断第三象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI + 180; } if (differ_x > 0 && differ_y < 0) // 判断第四象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI; } WriteProcessMemory(hProcess, aimAddr[0], &AiMBot_x, sizeof(float), &bytesRead); // 将计算出的敌人位置x写入准星x WriteProcessMemory(hProcess, aimAddr[1], &AiMBot_y, sizeof(float), &bytesRead); // 将计算出的敌人位置y写入准星y } CloseHandle(hProcess); return 0; }{dplayer src="https://www.52tt.pro/usr/uploads/2023/08/Aim.mp4"/}自瞄代码详解1.首先通过 Cheat Engine 获取人物在游戏中的 x,y,z 坐标地址,有了这些数据才能为后期做准备。Tips:使用CE搜未知初始值浮点数,人物坐标是浮点数,人物往左移动 也就是 x 轴 那么就是减少了,往右走就是增加了,数轴的基本常识左减右增,来来回回过滤用二分排除法很快就能找到x坐标了。Tips:一旦找到 x 坐标那么 y,z 就都找到了,x坐标的内存地址是 0x2458fa44 只要 0x2458fa44 + 4 就是 z 坐标 + 8 就是 y 坐标,这样就全部拿到了,可以看到我额外标出HP,因为这里是跟人物相关的数据一般都在附近,就像一个对象或者结构体里面的数据都会存放在一起的。2.计算 x,y 轴方向的差值(水平与垂直距离),假设自己的位置是 (0, 0),敌人的位置是 (3, 5),那么 x 轴方向的差值是 3,y 轴方向的差值是 5。√(3^2 + 5^2) = √34 ≈ 5.83 // 勾股定理水平距离:3(敌人的 x 坐标)- 0(自己 x 坐标) = 3垂直距离:5(敌人的 y 坐标)- 0(自己 y 坐标) = 5float differ_x = enemy_x - self_x float differ_y = enemy_y - self_y3.计算两点之间的直线距离,可以使用勾股定理计算,本教程中我使用了欧氏距离计算。d = √((x2 - x1)^2 + (y2 - y1)^2) // 二维欧氏距离// 计算二维欧氏距离 float GetDistance(float x1, float y1, float x2, float y2) { float distance = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); return distance / 20.0f; }4.计算自己与敌人之间的角度(相对于 x 轴)用反正切函数来计算角度。θ = arctan(5 / 3)AiMBot_y = atan((myvalues[1] - envalues[1]) / distan) / PI * 180 + 3; // 计算y轴方向上的角度5.使用 atan 函数计算 y 轴方向上的角度,作为左后自瞄的 y 角度。if (differ_x > 0 && differ_y > 0) // 判断第一象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI; // 计算 x 轴方向上的角度 } // 代码省略AiMBot_x = atan(differ_y / differ_x) * 180 / PI; // 计算x轴方向上的角度6.通过差值正负判断象限,用 atan 函数计算 x 轴方向上的角度,作为左后自瞄的 x 角度。WriteProcessMemory(hProcess, aimAddr[0], &AiMBot_x, sizeof(float), &bytesRead); // 准星x写入计算出的敌人位置x WriteProcessMemory(hProcess, aimAddr[1], &AiMBot_y, sizeof(float), &bytesRead); // 准星y写入计算出的敌人位置y7.获取游戏准星 x,y 移动坐标,最终配合 x 角度与 y 角度,用来移动准星实现自瞄效果。(以上方式是最原始的自瞄,用三角函数进行实现,只是单个人自瞄哦,多人只要遍历敌人数据x,y,z,加入到数组中通过距离判断优先级再自瞄,这样就完成了暂时不做比较懒。)无限急速雷思路很简单,先找雷数量右键看谁访问了这个地址,就可以追踪到访问它的代码段,这里面就会有很多对手雷的功能操作。(比如投掷距离,投掷速度,引爆时间。)看到这个地方,有 #fire_in_the_hole 这里就是调用扔雷的声音,再往下就是扔雷的操作。我分析过了,扔雷的速度就在这个反汇编代码,引用的这个地址附近,通过访问内存就能看到 + 4 的位置是 1.75 修改它扔雷速度就无冷却。{dplayer src="https://www.52tt.pro/usr/uploads/2023/08/bom.mp4"/}方框透视(C语言)绘制方框透视的方式也就两种,内部绘制 和 外部绘制,内部就是嵌入到了游戏窗口内,本文章用的是外部绘制,也就是说创建了一个窗口,设置透明并覆盖在游戏窗口上方,读游戏中的数据计算出来再进行绘制。我这里用的是 Windows API 进行绘制,会存在一点小问题,只是提供个思路给你们,在C语言也可以用 D3D9、SDL、Imgui 等等。#include <Windows.h> #include <tlhelp32.h> // 根据进程ID和模块名获取模块句柄 HMODULE GetModuleHandleFromProcess(DWORD processId, const char *moduleName) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId); MODULEENTRY32 moduleEntry; moduleEntry.dwSize = sizeof(moduleEntry); if (Module32First(hSnapshot, &moduleEntry)) { do { if (stricmp(moduleEntry.szModule, moduleName) == 0) { CloseHandle(hSnapshot); return moduleEntry.hModule; } } while (Module32Next(hSnapshot, &moduleEntry)); } CloseHandle(hSnapshot); return NULL; } // 窗口消息处理函数 LRESULT CALLBACK MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_SIZING: return TRUE; case WM_NCHITTEST: return HTTRANSPARENT; default: return DefWindowProc(hWnd, msg, wParam, lParam); } } // 绘制矩形 void DrawRectangle(HDC hdc, int x, int y, int width, int height) { RECT rect = {x, y, x + width, y + height}; HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH); HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); HGDIOBJ hOldPen = SelectObject(hdc, hPen); HGDIOBJ hOldBrush = SelectObject(hdc, hBrush); Rectangle(hdc, x, y, x + width, y + height); SelectObject(hdc, hOldBrush); SelectObject(hdc, hOldPen); DeleteObject(hPen); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { // 获取进程ID和句柄 DWORD processid; HANDLE hProcess; HWND Gamehwnd = FindWindow(NULL, "Compete King"); GetWindowThreadProcessId(Gamehwnd, &processid); hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid); // 获取模块句柄 const char *moduleName = "amxmodx_mm.dll"; HMODULE hModule = GetModuleHandleFromProcess(processid, moduleName); // 注册窗口类 WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, TEXT("D3D9"), NULL}; RegisterClassEx(&wc); // 创建窗口 RECT rects; GetWindowRect(Gamehwnd, &rects); int left = rects.left; int top = rects.top; int right = rects.right; int bottom = rects.bottom; int sWidth = right - left; int sHeight = bottom - top; HWND hWnd = CreateWindowEx( WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, TEXT("D3D9"), WS_POPUP, left, top, sWidth - 6, sHeight - 3, NULL, NULL, wc.hInstance, NULL); SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 0, LWA_COLORKEY); ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); MSG msg; float MatData; float Matarray[4][4]; HDC hdc = GetDC(hWnd); ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { // 获取游戏窗口大小 if (!GetWindowRect(Gamehwnd, &rects)) { PostQuitMessage(0); break; } int left = rects.left; int top = rects.top; int right = rects.right; int bottom = rects.bottom; int sWidth = right - left; int sHeight = bottom - top; SetWindowPos(hWnd, HWND_TOPMOST, left + 3, top, sWidth, sHeight, SWP_NOSIZE); int initNumber = -4; for (int i = 0; i < 4; i++) // 遍历存储 4 x 4 矩阵 { for (int j = 0; j < 4; j++) { initNumber += 4; LPVOID Addresses = (LPVOID)0x2C20100 + initNumber; ReadProcessMemory(hProcess, Addresses, &MatData, sizeof(float), NULL); Matarray[i][j] = MatData; } } int GameWinWidth = 1030 / 2; int GameWinHeight = 797 / 2; float vorx, vory, vorz, vory2, FanHeight, FanWidth, row; long long offset = 0xFFFFFFFFFFFFFCDC; RECT clientRect; GetClientRect(hWnd, &clientRect); FillRect(hdc, &clientRect, (HBRUSH)GetStockObject(BLACK_BRUSH)); for (int enemy_num = 0; enemy_num < 2; enemy_num++) // 遍历敌人数组 { offset += 0x324; LPVOID baseAddress = (LPVOID)((DWORD_PTR)hModule + 0x001CFE08); int baseaddr; ReadProcessMemory(hProcess, baseAddress, &baseaddr, sizeof(int), NULL); float enemyHP, enemyX, enemyY, enemyZ; ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x1E0 + offset, &enemyHP, sizeof(float), NULL); // enemyHP ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x88 + offset, &enemyX, sizeof(float), NULL); // enemyX ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x8C + offset, &enemyY, sizeof(float), NULL); // enemyY ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x90 + offset, &enemyZ, sizeof(float), NULL); // enemyZ /* 三维图形的投影算法 */ vorz = Matarray[0][2] * enemyX + Matarray[1][2] * enemyY + Matarray[2][2] * enemyZ + Matarray[3][2]; row = 1 / vorz; vorx = GameWinWidth + (Matarray[0][0] * enemyX + Matarray[1][0] * enemyY + Matarray[2][0] * enemyZ + Matarray[3][0]) * row * GameWinWidth; vory = GameWinHeight - (Matarray[0][1] * enemyX + Matarray[1][1] * enemyY + Matarray[2][1] * (enemyZ - 50) + Matarray[3][1]) * row * GameWinHeight; vory2 = GameWinHeight - (Matarray[0][1] * enemyX + Matarray[1][1] * enemyY + Matarray[2][1] * (enemyZ + 30) + Matarray[3][1]) * row * GameWinHeight; if (vorz < 0) { continue; } FanHeight = vory - vory2; FanWidth = FanHeight * 0.6; DrawRectangle(hdc, vorx - FanWidth / 2, vory2, (int)FanWidth, (int)FanHeight); // 绘制方框矩形 } ReleaseDC(hWnd, hdc); } } UnregisterClass(wc.lpszClassName, wc.hInstance); return (int)msg.wParam; }gcc main.c -s -lgdi32 -ld3d9 -mwindows // 编译成 exe 并隐藏 DOS 窗口方框透视详解先理一下思路,要用到的功能有,通过窗口句柄取进程ID,打开进程句柄,读游戏敌人 x y z 内存坐标数据,本文章代码用到的是外部窗口绘制,所以找个可以创建图形窗口能画图的库就行,设置窗口无边框,设置窗口透明,设置鼠标穿透,设置窗口置顶和实时位置覆盖,这样就能绘制了。三维投影变换1.主要用到三维图形的投影算法,把游戏里的数据进行计算后把敌人位置绘制到屏幕上显示,所以要在游戏里找到相机矩阵。在游戏里能影响到相机的方向的只有视角,用 CE 搜浮点数未知初始值,人物不动鼠标转动视角,再次搜变动了数值,以此类推一个一个进去看用内存视图观察特征,当然嫌麻烦可以找矩阵验证工具,可以自己网上找这里就不提供了。常见相机矩阵有 4 x 4,其他的还有 4 x 3 等等,如果矩阵有变化那么三维图像投影变换算法也需要进行更改。 矩阵特征:规律总结1:跳动时,有三个数据在变动规律总结2:晃动鼠标时,有一个数据保持 0 不变规律总结3:存在3对两两相同2.当我在游戏里人物跳起来的时候,可以发现只有三个值不停的发送变化,其他的都没变而且存在3对两两相同数:-0.11 -0.11 -0.99 -0.99 0.04 0.043.当我在游戏里旋转游戏视角的时候,可以发现有一个 0 不变。相机z = 矩阵[0][2] x 敌人x坐标 + 矩阵[1][2] x 敌人y坐标 + 矩阵[2][2] x 敌人z坐标 + 矩阵[3][2] 比例 = 1 / 相机z 相机x = 游戏窗口宽度 + (矩阵[0][0] x 敌人x坐标 + 矩阵[1][0] x 敌人y坐标 + 矩阵[2][0] x 敌人z坐标 + 矩阵[3][0]) x 比例 x 游戏窗口宽度 相机y = 游戏窗口高度 - (矩阵[0][1] x 敌人x坐标 + 矩阵[1][1] x 敌人y坐标 + 矩阵[2][1] x (敌人z - 50) + 矩阵[3][1]) x 比例 x 游戏窗口高度 相机y2 = 游戏窗口高度 - (矩阵[0][1] x 敌人x坐标 + 矩阵[1][1] x 敌人y坐标 + 矩阵[2][1] x (敌人z + 30) + 矩阵[3][1]) x 比例 x 游戏窗口高度投影公式相机 Z 计算:比例因子:屏幕上的 X 坐标:屏幕上的 Y 坐标:这里还有Python3的版本:Python3 透视自瞄外挂制作 ,仅供学习参考。2024-12-20 09:58:10 星期五 更新过
2024年11月20日
3,725 阅读
1 评论
764 点赞
2024-09-30
【技术分享】NASM x86 Assembly Language
留言复习x86汇编基础,首先为什么要学习汇编语言呢?在二进制程序逆向中是必不可少的,在二进制程序在没有源代码的情况下,只能进行反汇编逆向分析程序的执行流程。所谓 "学而不思则罔,思而不学则殆",只有真正的理解了是如何运作的,才能更好的进行后面的分析。本教程使用 intel 语法来进行编写,编译器使用的是开源的 NASM,就你叫x86汇编呀,直接干就完了!简介本文章会简单的讲解下Windows下的x86汇编,内容比较简短都是示例,x86汇编语言编程也很混乱。因为有很多不同的汇编器:MASM、NASM、gas、as86、TASM、a86、Terse 等,导致语法都不相同。出于对 汇编语言发展 的兴趣,本教程不是为了教你汇编,让你大概学习下x86汇编基础语法方便为后期逆向做准备。当然感兴趣的可以自己研究,这里推荐几个好的教程网站: Linux 汇编语言教程Putting the "You" in CPU汇编器与链接器不管使用哪种汇编器、目标文件格式、链接器或操作系统,编程过程始终是相同的。Nasm 编译器安装Nasm win64 下载 - | - MinGW 下载 首先需要下载编译环境,Nasm 与 MinGW 下载好后解压出来,配置好环境变量方便终端进行调用。这里我推荐几个链接器:ld、alink、golink、gcc 各有秋色,我会演示他们的用法,前三者打包都是程序最小的状态,而 gcc 会使程序大小变大,因为它加了一些东西。NASM 汇编语法输出 Hello, World! - Windowsextern _printf ; 声明外部的 printf 函数 section .data msg db 'Hello, World!', 0 ; 定义字符串常量 section .text global _main ; 设置入口函数为 _main _main: push msg ; 将字符串地址推入栈中,作为参数传递给 printf call _printf ; 调用 printf 函数 add esp, 4 ; 清理栈上的参数 ret ; 函数返回随便创建一个文件,名称.asm 以.asm 后缀结尾,当然后缀什么的都不重要,汇编代码是对的一切都是 ok 的,通过以下代码在 Windows 进行生成再到链接最终生成可执行程序。/* ------ 参数说明 -f win32 指定 32位 生成目标文件 test.o -lmsvcrt 指定链接器需要 C运行库 要用到 printf ------ 编译命令 nasm -f win32 demo.asm -o test.o ld test.o -o test.exe -lmsvcrt */生成完毕后,可以发现程序大小只有 4kb 左右,非常小那是因为用汇编进行编写的,没有其他一些没用的函数打包进去。通过 IDA 打开生成的程序,发现只有 _main 和 _printf 函数,而且代码也是我们写的那一部分。通过 x64dbg 发现,运行直接到 OEP 时就能看到我们写的代码, 程序入口点是 401000 从这里开始到 40100D 结束(自己写的代码范围)。反汇编代码中 push 0x402000 是把内存地址存储的字符串压入到堆栈中,然后通过 call Printf 调用进行输出 Hello, World!。输出 Hello, World! - Linuxsection .data ; 定义一个名为 hello 的字符串,用 db 伪指令来定义 ; db 是 define bytes,用来指定一组字节 hello db 'Hello, World!', 0 ; 字符串用单引号或双引号括起来,末尾要添加一个零终止符 \0 section .text global _start ; 设置入口函数为 _start _start: ; 往标准输出 (stdout) 写入字符串 ; 用系统调用 write mov eax, 4 ; 系统调用号 4 (write) 存储到寄存器 eax mov ebx, 1 ; 描述符 1 (stdout) 存储到寄存器 ebx mov ecx, hello ; 字符串的地址存储到寄存器 ecx mov edx, 13 ; 把字符串的长度(以字节为单位)存储到寄存器 edx int 0x80 ; 系统调用 sys_write ; 退出程序 ; 用系统调用 exit mov eax, 1 ; 系统调用号 1 (exit) 存储到寄存器 eax xor ebx, ebx ; 寄存器 ebx 设置为 0,表示返回值为 0 int 0x80 ; 系统调用 sys_exit/* ------ 参数说明 -f elf32 指定32位生成目标文件main.o -m elf_i386 指定生成32位可执行elf程序 ------ 编译命令 nasm -f elf32 main.asm -o main.o ld -m elf_i386 main.o -o main */通过IDA打开,可以看到只有一个 _start 主入口函数,也是没有其他任何函数,通过 sys_write 输出再到 sys_exit 退出结束。前面的操作让我们实现了编译环境搭建,和第一个汇编程序的编写到运行,现在让我们正式开始吧。(本教程在 Windows 操作系统进行讲解)视频教程{dplayer src="https://www.52tt.pro/usr/uploads/2023/09/10.1.mp4"/}PE文件节section .text // 代码段:用来存储程序的可执行指令(代码)。 section .data // 数据段:用来存储已经初始化的静态变量和全局变量。 section .rdata // 只读数据段:用来存储只读数据,字符串常量等。 section .bss // 未初始化数据段:用来存储未初始化的静态变量和全局变量。 section .data // 可读写数据段:用来存储可以被程序读取或写入的数据。 section .data? // 未定义的数据段 /* 可执行数据段: */ section .text // 代码段:用来存储程序的可执行指令(代码)。 section .idata // 导入数据段:用来存储从其他动态链接库(DLL)中导入的符号等信息。 section .edata // 导出数据段:用来存储导出给其他模块的符号等信息。 section .rsrc // 资源数据段:用来存储可执行文件中的资源数据,如图标、位图、菜单和对话框等。代码注释; 单行注释(分号)字符串输出 - utf-8extern _printf, _system section .data cmd db 'chcp 65001', 0 ; 设置终端输出编码为 utf-8 msg db '你好,世界!', 10, 0 msg2 db 'Hello, World!', 0 section .text global _main ; 设置入口函数为 _main _main: push cmd call _system push msg ; 将字符串地址推入栈中,作为参数传递给 printf call _printf ; 调用 printf 函数 push msg2 call _printf add esp, 12 ; 清理栈上的参数 ret ; 函数返回局部变量 - local varsection .text global _main extern _printf _main: push ebp ; 保存旧的基址寄存器值 mov ebp, esp ; 设置新的基址寄存器值 sub esp, 4 ; 分配 4 字节大小的空间作为局部变量 mov dword [ebp-4], 5 ; 将值 5 存储到局部变量中 mov eax, dword [ebp-4] ; 将局部变量值加载到寄存器 eax 中 push eax ; 将局部变量值推入栈中,作为参数传递给 printf push msg ; 将格式化字符串的地址也推入栈中 call _printf ; 调用 printf 函数 add esp, 12 ; 清理栈上的参数 pop ebp ; 恢复旧的基址寄存器值 ret ; 函数返回 section .data msg db 'local variable is %d', 10, 0 ; 格式化字符串.数据类型 - Data type整型(Integer) 浮点型(Float) 布尔型(Boolean) 字符型(Character)section .data format db "int: %d", 0 ; 格式化输出模板,%d 整数占位符 num dd 100 ; 存储整数值100 msg db "string: Hello", 0 ; 要输出的字符串 newline db 10, 0 ; 换行符和结尾符 section .text global _main extern _printf _main: push ebp ; 保存当前的ebp值 mov ebp, esp ; 把当前的堆栈指针esp的值赋给ebp,建立函数的堆栈帧 push dword [num] ; 压入num变量的值,准备进行格式化输出 push format ; 压入format变量的地址,作为格式化输出的模板 call _printf ; 调用外部函数_printf进行格式化输出 add esp, 8 ; 清除堆栈上的两个参数,恢复堆栈指针 push dword newline ; 压入newline变量的值,输出换行符 call _printf ; 调用_printf函数输出换行符 add esp, 4 ; 清除堆栈上的一个参数 push msg ; 压入msg变量的地址,准备输出字符串 call _printf ; 调用_printf函数输出字符串 Hello add esp, 4 ; 清除堆栈上的一个参数 mov esp, ebp ; 恢复函数调用前的堆栈状态,将堆栈指针esp的值设为ebp的值 pop ebp ; 恢复之前保存在堆栈中的ebp寄存器的值 ret ; 函数返回条件语句 - if ... elsesection .data format db "%d", 0 num dd 1 format2 db "%d", 0 num2 dd 0 section .text global _main extern _printf _main: push ebp mov ebp, esp mov eax, 5 mov ebx, 5 cmp eax, ebx ; 比较是否相等 je print_num push dword [num2] push format call _printf add esp, 8 jmp end print_num: ; 成立则跳转到这 mov eax, dword [num] push eax push format2 call _printf add esp, 8 end: mov esp, ebp pop ebp ret ; eax, ebx 寄存器值 都等于 5 所以成立输出 1条件循环 - forsection .data count equ 10 msg db "Loop iteration.", 10, 0 section .text global _main extern _printf _main: mov ebx, count ; 设置计数器为循环次数 loop_start: cmp ebx, 0 ; 检查计数器是否为零 je loop_end ; 如果为零,跳转到循环结束处 push msg call _printf ; 输出消息 add esp, 4 ; 清除栈上的参数 dec ebx ; 计数器减一 jmp loop_start ; 跳转回循环起始点 loop_end: mov eax, 0 ret条件循环 - whilesection .data msg db "Loop iteration.", 10, 0 section .text global _main extern _printf _main: loop_start: push msg call _printf ; 输出消息 add esp, 4 ; 清除栈上的参数 jmp loop_start ; 跳转回循环起始点整型数组 - Arraysection .data array dd 1, 2, 3, 4, 5, 10 ; 整数数组 len equ ($ - array) / 4 ; 数组长度 section .text global _main extern _printf _main: xor esi, esi ; 将数组索引初始化为 0 loop: cmp esi, len ; 检查是否到达数组末尾 jge end_loop mov eax, [array + esi * 4] ; 获取数组元素的值 push eax ; 将元素值推入堆栈 push format ; 将格式化字符串推入堆栈 call _printf ; 调用 printf 函数来打印元素值 add esp, 8 ; 清理堆栈 inc esi ; 增加数组索引 jmp loop end_loop: ret ; 返回函数 section .data format db "%d ", 0 ; 格式化字符串,用于打印整数switch 语句extern _printf ; 引入 printf 函数 section .data value db 2 ; 定义 value 变量,初始值为 2 msg1 db "1", 0 ; 定义三个输出信息 msg2 db "2", 0 msg3 db "3", 0 section .text global _main _main: cmp byte [value], 1 je case_1 ; 比较 value 和 1 的值,如果相等,跳转到 case_1 标签处 cmp byte [value], 2 je case_2 ; 比较 value 和 2 的值,如果相等,跳转到 case_2 标签处 cmp byte [value], 3 ; 比较 value 和 3 的值,如果相等,跳转到 case_3 标签处 je case_3 ; 如果都不相等,跳转到 case_default 标签处 jmp case_default case_1: push msg1 ; 将 msg1 的地址压入栈中,并调用 printf 函数 call _printf add esp, 4 ; 回收栈空间 jmp end_switch ; 跳转到 end_switch 标签处 case_2: push msg2 ; 将 msg2 的地址压入栈中,并调用 printf 函数 call _printf add esp, 4 ; 回收栈空间 jmp end_switch ; 跳转到 end_switch 标签处 case_3: push msg3 ; 将 msg3 的地址压入栈中,并调用 printf 函数 call _printf add esp, 4 ; 回收栈空间 jmp end_switch ; 跳转到 end_switch 标签处 case_default: jmp end_switch ; 跳转到 end_switch 标签处 end_switch: mov eax, 0 ; 将返回值设为 0,保证程序正常退出 ret ; 返回上一级调用函数调用约定方法名采用这样的命名方式,就像 _GetStdHandle@4,是因为在 Win32 中,函数名称会经过编码(mangled 或 decorated)以符合特定的约定。这种约定是为了确保函数在编译后可以正确地对外提供和使用。Win32 使用的调用约定称为 __stdcall,它规定了函数在被调用时参数的传递方式、栈的清理方式等。根据 __stdcall 的规定,函数名前面会加上一个下划线(_),然后是函数名本身,后面紧跟着一个 at 符号(@),最后是参数列表中的字节数(以十进制表示)。Cdecl(C Declaration)参数从右向左入栈。调用者负责清理堆栈。支持可变参数。在多数 C 编译器和 Microsoft Windows、Linux 系统上广泛使用。Stdcall(Standard Call)参数从右向左入栈。被调用函数负责清理堆栈。常用于 Win32 API。Fastcall一部分参数通过寄存器传递,其余的参数从右向左入栈。被调用函数负责清理堆栈。具体实现可能因编译器和平台而异。HANDLE OpenProcess( [in] DWORD dwDesiredAccess, [in] BOOL bInheritHandle, [in] DWORD dwProcessId );函数参数是从右向左压入到堆栈的,那么第一个参数在汇编中就是 dwProcessId(进程id),以此类推压入完成进行调用。extern _OpenProcess@12 ; 设置调用API加@12,说明函数有3个参数都是 dword(4字节)。 section .data PROCESS_ALL_ACCESS equ 0x1F0FF section .text global _main _main: push dword 0 ; dwProcessId,进程 ID push dword 0 ; bInheritHandle,不继承句柄 push dword PROCESS_ALL_ACCESS ; dwDesiredAccess,使用标准读取权限 call _OpenProcess@12 ; @12 这么写会自动清空堆栈参数,无需写清除参数代码。以上是加了 @12 参数自动从堆栈清除的过程,可以发现当执行完 call 时堆栈参数自动被清除。标准用户输入 - scanfsection .data prompt db "Please enter a string: ", 0 format db "%s", 0 input db 255 dup(0) ; 申请长度为 255 字节内存字符串数组,初始值都为 0; section .text global _main extern _printf, _scanf, _puts _main: push ebp mov ebp, esp push dword prompt call _printf ; 输出提示信息 add esp, 4 ; 接收用户输入 push dword input push dword format call _scanf add esp, 8 push dword input call _puts ; 显示输入的字符串 add esp, 4 end: mov esp, ebp pop ebp ret调用系统命令 - systemsection .data cmd db 'cmd /c dir', 0 ; 存储 cmd 执行命令 section .text global _main extern _system _main: push ebp mov ebp, esp push dword cmd ; 压入参数到堆栈 call _system ; 调用 system add esp, 4 ; 清除参数 end: mov esp, ebp pop ebp ret调用弹窗 - MessageBoxAsection .data caption db "Hello", 0 message db "Welcome to the MessageBox!", 0 section .text global _main extern _MessageBoxA@16 _main: push 0 ; 确定:0 消息:48 警告:64 push caption push message push 0 ; NULL (HWND) call _MessageBoxA@16 ; 调用MessageBox函数显示消息框nasm -f win32 demo.asm -o test.obj ld -m i386pe -o test.exe test.obj -luser32 -lkernel32 // -luser32 -lkernel32 导入动态链接库终止进程 - TerminateProcesssection .data PROCESS_ALL_ACCESS equ 0x1F0FF section .text global _main extern _OpenProcess@12, _TerminateProcess@8, _printf _main: push dword 21108 ; dwProcessId,进程 ID push dword 0 ; bInheritHandle,不继承句柄 push dword PROCESS_ALL_ACCESS ; dwDesiredAccess,使用标准读取权限 call _OpenProcess@12 mov ebx, eax ; 将句柄保存到 EBX 中 push ebx ; 将句柄值压入堆栈 push format_str ; 格式化字符串 call _printf ; 调用 printf 函数 add esp, 8 push 0 push ebx call _TerminateProcess@8 ; 调用 TerminateProcess 函数 xor ebx, ebx ; 清空 EBX 寄存器 ret section .data format_str db "Handle value: %d", 0 ; 使用 %d 格式输出整数伪代码指令 - import再次提到,对于编译后一个 Win32 函数的名称如何被修饰或装饰这个问题,在这个级别上了解似乎要求过高了。毕竟所有的 C 程序员都可以愉快地写出 "ExitProcess",而不是 "_ExitProcess@4"。很赞同这个说法,有的时候 下划线(_)加艾特(@)的确不是很好,所以可以借助下伪指令,这样就可以直接导入 Windows api 函数了,不需要再修改名称。Alink链接器这个 Alink 链接器上一次修改还是在 1999 年,真的是上古链接器了,估计作者也不更新了不过好在还可以用。Alink 下载 伪指令这些语法,必须生成 obj 然后用 alink 链接生成才行,对于 gcc 和 ld 都是会报错的导致编译不成功,就像以下这样:简单调用 printfextern printf import printf msvcrt.dll section .data msg db "hey Actdream!", 0 section .text use32 ..start: push msg call [printf] add esp, 4 ret输出 Hello, World!extern ExitProcess ; 声明 ExitProcess 外部函数。 import ExitProcess kernel32.dll ; 引入 ExitProcess 函数在 kernel32.dll 动态链接库里。 extern GetStdHandle import GetStdHandle kernel32.dll extern WriteFile import WriteFile kernel32.dll segment .data use32 ; 定义 32 位数据段,用来存储程序中使用的数据。 Text db "Hello, World!",0 ; 在数据段里定义了一个以空字符结尾的字符串 "Hello, World!"。 segment .code use32 ; 定义一个 32 位代码段,在这里定义了程序的主要逻辑。 ..start: ; 定义一个程序入口点标签 push -11 ; 把参数 -11 压入栈中 call [GetStdHandle] ; 调用 GetStdHandle 函数,并将返回值存储在 EAX 寄存器里 mov ebx, eax ; 把句柄存储在 EBX 寄存器里 push dword 0 ; 压入参数 0 lea eax, [ebp-4] ; 指向文本字符串的指针存储在 EAX 寄存器里 push eax ; 把 EAX 寄存器中的指针地址压入栈中 push (message_end - message) ; 把字符串的长度压入栈里(不包括结尾的空字符) push message ; 把字符串的起始地址压入栈里 push ebx ; 获取到的标准输出句柄压入栈中 call [WriteFile] ; 最后调用 WriteFile 函数,把字符串写入到标准输出。 push dword 0 ; 压入参数 0 call [ExitProcess] ; 调用 ExitProcess 函数,来终止程序的运行 hlt ; 暂停 CPU 的运行 message: ; 定义消息字符串的起始标签 db 'Hello, World!', 10 ; 在数据段里中定义了一个字符串 "Hello, World!"。 message_end: ; 定义消息字符串的结束标签/* ------ 参数说明 -fobj 指定输出格式为 obj 目标文件格式 -subsys console 指定生成的是控制台可执行文件 -oPE 指定输出文件格式 PE 可执行文件 ------ 编译命令 nasm -fobj main.asm alink -subsys console -oPE main.obj */调用弹窗 - MessageboxA[BITS 32] ; 声明代码为32位模式 extern ExitProcess import ExitProcess kernel32.dll ; 导入ExitProcess函数 extern MessageBoxA import MessageBoxA user32.dll ; 导入MessageBoxA函数 segment .data use32 Caption db 'From Assembly',0 ; 定义标题字符串 Text db "Hello, World",0 ; 定义消息字符串 segment .code use32 ..start: push dword 0 ; 参数0(消息框类型)压入栈中 push dword Caption ; 标题字符串的地址压入栈中 push dword Text ; 消息字符串的地址压入栈中 push dword 0 ; 参数0(父窗口句柄)压入栈中 call [MessageBoxA] ; 调用MessageBoxA函数显示消息框 push dword 0 ; 参数0(退出代码)压入栈中 call [ExitProcess] ; 调用ExitProcess函数终止程序的运行读进程内存数据BOOL WINAPI ReadProcessMemory( _In_ HANDLE hProcess, _In_ LPCVOID lpBaseAddress, _Out_ LPVOID lpBuffer, _In_ SIZE_T nSize, _Out_ SIZE_T *lpNumberOfBytesRead );读内存要用到 ReadProcessMemory 系统提供的API函数。编译后只有一个 start 入口直接就是我们写的代码,很干净的一个程序只有 2.5 kb 大小。extern OpenProcess import OpenProcess kernel32.dll extern ReadProcessMemory import ReadProcessMemory kernel32.dll extern printf import printf msvcrt.dll extern CloseHandle import CloseHandle kernel32.dll %define PROCESS_ALL_ACCESS 0x1F0FF section .data processId dd 27676 desiredAccess dd PROCESS_ALL_ACCESS inheritHandle dd 0 lpBaseAddress dd 0x4a4610 nSize dd 128 lpNumberOfBytesRead dd 0 readSuccess db 'Read bytes: %d', 0 section .bss buffer resb 128 hProcess resd 1 section .text use32 ..start: push dword [processId] push dword [inheritHandle] push dword [desiredAccess] call [OpenProcess] ; 打开进程句柄 mov [hProcess], eax lea eax, [lpNumberOfBytesRead] push eax push dword [nSize] lea eax, [buffer] push eax push dword [lpBaseAddress] push dword [hProcess] call [ReadProcessMemory] ; 读内存数据 push dword [buffer] push readSuccess call [printf] ; 输出数据 add esp, 8 push dword [hProcess] call [CloseHandle] ; 关闭句柄 ret写进程内存数据BOOL WriteProcessMemory( [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten );extern OpenProcess import OpenProcess kernel32.dll extern WriteProcessMemory import WriteProcessMemory kernel32.dll extern CloseHandle import CloseHandle kernel32.dll extern ExitProcess import ExitProcess kernel32.dll %define PROCESS_ALL_ACCESS 0x1F0FFF section .data processId dd 27676 desiredAccess dd PROCESS_ALL_ACCESS inheritHandle dd 0 lpBaseAddress dd 0x4a4610 nSize dd 4 writeValue dd 10 section .bss hProcess resd 1 bytesWritten resd 1 section .text use32 ..start: push dword [processId] push dword [inheritHandle] push dword [desiredAccess] call [OpenProcess] mov [hProcess], eax push 0 push dword [nSize] lea eax, [writeValue] push eax push dword [lpBaseAddress] push dword [hProcess] call [WriteProcessMemory] push dword [hProcess] call [CloseHandle] retGolink链接器Goasm 与 GoLink,都是由 Jeremy Gordon 开发,是用来与 GoAsm 配合使用的链接器。主要特点:轻量级高效:专为快速和高效的链接过程设计。兼容性:它可以链接其他汇编器(比如 NASM 或 FASM)还有某些 C/C++ 编译器生成的对象文件。格式的支持:GoLink 对 Windows PE 格式有良好的支持。用 NASM 原生语法,导入动态链接库函数不需要以下划线 _ 开头,比如以下:这里插一嘴:我推荐使用这个链接器,方便且轻巧。官网地址 | Golink 下载extern printf section .data msg db 'Hello, World!', 0 section .text global main main: push msg call printf add esp, 4 ret/* ------ 参数说明 /entry 指定入口函数 /console 指定为控制台程序 ------ 编译命令 nasm -f win32 main.asm -o main.obj golink /entry main main.obj msvcrt.dll /console */简单的窗口程序{collapse}{collapse-item label="NASM汇编代码" close}extern printf extern ExitProcess extern RegisterClassExA extern CreateWindowExA extern MessageBoxA extern GetModuleHandleA extern PeekMessageA extern TranslateMessage extern DispatchMessageA extern PostQuitMessage extern DefWindowProcA extern ShowWindow extern UpdateWindow extern GetStockObject extern BeginPaint extern EndPaint extern Rectangle extern Ellipse extern MoveToEx extern LineTo extern DrawTextA extern SetBkMode extern SetTextColor extern CreateSolidBrush extern CreatePen extern SelectObject extern DeleteObject extern LoadIconA extern LoadCursorA %define WS_OVERLAPPED 0x00000000 %define WS_POPUP 0x80000000 %define WS_CHILD 0x40000000 %define WS_MINIMIZE 0x20000000 %define WS_VISIBLE 0x10000000 %define WS_DISABLED 0x08000000 %define WS_CLIPSIBLINGS 0x04000000 %define WS_CLIPCHILDREN 0x02000000 %define WS_MAXIMIZE 0x01000000 %define WS_CAPTION 0x00C00000 %define WS_BORDER 0x00800000 %define WS_DLGFRAME 0x00400000 %define WS_VSCROLL 0x00200000 %define WS_HSCROLL 0x00100000 %define WS_SYSMENU 0x00080000 %define WS_THICKFRAME 0x00040000 %define WS_GROUP 0x00020000 %define WS_TABSTOP 0x00010000 %define WS_MINIMIZEBOX 0x00020000 %define WS_MAXIMIZEBOX 0x00010000 %define WS_TILED WS_OVERLAPPED %define WS_ICONIC WS_MINIMIZE %define WS_SIZEBOX WS_THICKFRAME %define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW %define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ WS_CAPTION | \ WS_SYSMENU | \ WS_THICKFRAME | \ WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX) %define PM_REMOVE 1h %define WM_QUIT 0x0012 %define WM_PAINT 0x000F %define WM_CLOSE 0x0010 %define CS_VREDRAW 0x0001 %define CS_HREDRAW 0x0002 %define CS_DBLCLKS 0x0008 %define CS_OWNDC 0x0020 %define CS_CLASSDC 0x0040 %define CS_PARENTDC 0x0080 %define CS_NOCLOSE 0x0200 %define CS_SAVEBITS 0x0800 %define CS_BYTEALIGNCLIENT 0x1000 %define CS_BYTEALIGNWINDOW 0x2000 %define CS_GLOBALCLASS 0x4000 %define IDI_APPLICATION 32512 %define IDC_ARROW 32512 section .text global START START: push 0 call GetModuleHandleA mov dword [Instance], eax mov dword[OurWindowclass], 48 mov dword[OurWindowclass+4], CS_OWNDC|CS_HREDRAW|CS_VREDRAW mov dword[OurWindowclass+8], WindowProc mov dword[OurWindowclass+12], 0 mov dword[OurWindowclass+16], 0 mov eax, dword[Instance] mov dword[OurWindowclass+20], eax push IDI_APPLICATION push 0 call LoadIconA mov dword[OurWindowclass+24], eax push IDI_APPLICATION push 0 call LoadCursorA mov dword[OurWindowclass+28], eax mov dword[OurWindowclass+32], 5 mov dword[OurWindowclass+36], 0 mov dword[OurWindowclass+40], Windowclassname mov dword[OurWindowclass+44], 0 lea eax, [OurWindowclass] push eax call RegisterClassExA cmp eax, 0 jne Lcreate push 0 push Windowname push string_reg_failed push 0 call MessageBoxA jmp exit Lcreate: push 0 push dword[Instance] push 0 push 0 push 250 ; Height push 380 ; Width push 140 ; Top push 360 ; Left push WS_VISIBLE+WS_OVERLAPPEDWINDOW push Windowname push Windowclassname push 0 call CreateWindowExA mov dword[Windowhandle], eax push 1 push dword[Windowhandle] call ShowWindow push dword[Windowhandle] call UpdateWindow messloop: push PM_REMOVE push 0 push 0 push 0 push MessageBuffer call PeekMessageA cmp eax, 0 je nextloop cmp dword[MessageBuffer+4], WM_QUIT je exit push MessageBuffer call TranslateMessage push MessageBuffer call DispatchMessageA nextloop: jmp near messloop exit: push dword 0 call ExitProcess ret global WindowProc WindowProc: push ebp mov ebp, esp cmp dword[ebp+12], WM_CLOSE jne Ldefault push 0 call PostQuitMessage Ldefault: push dword[ebp+20] push dword[ebp+16] push dword[ebp+12] push dword[ebp+8] call DefWindowProcA mov esp, ebp pop ebp ret section .data Windowclassname db 'MyClass', 0 Windowname db 'My Window', 0 string_reg_failed db 'Failed to register main window', 0 Instance dd 0 Windowhandle dd 0 section .bss MessageBuffer resb 28 OurWindowclass resb 48{/collapse-item}{/collapse}/* ------ 编译命令 nasm -f win32 window.asm -o window.obj golink window.obj kernel32.dll user32.dll */隐藏DOS窗口/* ------ 参数说明 -subsystem windows 即可隐藏DOS窗口 ------ 编译命令 nasm -f win32 main.asm -o main.o ld main.o -o main.exe -lkernel32 -lmsvcrt -luser32 -subsystem windows */ 生成图标程序/* ------ 文件生成 创建文件 resource.rc 内容:IDI_ICON ICON "mu.ico" 内容写到里面 使用 windres 生成资源目标文件 打包资源命令:windres resource.rc resource.o ------ 编译命令 nasm -f win32 main.asm -o main.obj ld main.obj resource.o -o main.exe -lmsvcrt */添加UAC清单创建UAC清单文件:创建一个XML文件名为 nac.manifest 内容填写以下代码。<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges> <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> </assembly>创建资源文件:编写一个资源脚本文件名为: src.rc 用来引用清单文件,内容填写以下代码。#include "winuser.h" 1 RT_MANIFEST "nac.manifest" IDI_ICON ICON "00.ico"这里我添加了自定义图标在 src.rc 写了 IDI_ICON 当然不需要也可以去掉。/* ------ 文件生成 nasm -f win32 main.asm -o main.obj windres src.rc -O coff -o src.res ------ 编译命令 ld -o main.exe main.obj src.res -lkernel32 -luser32 -lmsvcrt */vmp加壳问题如果你尝试用 alink 进行链接生成程序,就会发现区段的命名方式,不以 . 开头,导致加壳的时候识别错误。而使用 ld、gcc、golink 进行连接就不会出现这种情况了,就能正常加壳了。调用第三方DLL这里我写了一个自定义的dll,里面就导出了一个 initGo 函数,功能是弹出一个弹窗。section .data dllName db "Bin32.dll", 0 functionName db "initGo", 0 section .text extern LoadLibraryA, GetProcAddress global main main: push dllName call LoadLibraryA mov ebx, eax ; DLL 句柄 push functionName push ebx call GetProcAddress mov esi, eax ; 函数指针 call esi ; 调用 initGo 函数 mov ebx, 0 ret/* ------ 编译命令 nasm -f win32 main.asm -o main.obj golink /entry main main.obj Bin32.dll Kernel32.dll /console */
2024年09月30日
5,982 阅读
0 评论
958 点赞
2023-08-23
【每日随记】Ollama 使用教程
简介比较喜欢的一个开源项目(是Meta公司开发的),为什么要介绍它,我一直都想要一个不需要联网的AI大语言模型,由于性能和一些问题,在笔记本上跑的会非常吃力,而Ollama却完美的解决了。主要用起来方便,让它自己通过大量的数据进行学习,模型微调出自己的一个AI助手,想想就激动。网络搜索介绍: Ollama是一个为本地运行开源大型语言模型而设计的简化工具,它通过将模型权重、配置和数据集整合到一个由Modelfile管理的统一包中,极大地简化了LLM的部署过程。Meta公司,即元宇宙平台公司,公开发布了LLaMA,并对商业用途提出了许可要求。2023年7月,Meta公司发布了人工智能模型LLaMA 2的开源商用版本,意味着大模型应用进入了“免费时代”,初创公司也能够以低廉的价格来创建类似ChatGPT这样的聊天机器人。2024年7月23日,Meta发布了LLaMA 3.1 405B开源人工智能模型。Ollama Github下载安装没什么好说的,用什么操作系统就下载哪个版本,直接图形化无脑下一步安装。官网下载基本使用自己找吧,我比较喜欢用 gemma2,看个人喜好了吧。模型下载 复制这句代码,到Windows终端上,输入下载模型运行ollama run gemma2 // 交互式 ollama run gemma2 "你好" // 传参式自定义GGUF模型FROM ./vicuna-33b.Q4_0.gguf // 创建一个名为的文件 Modelfile,包含FROM要导入的模型的本地文件路径的指令。 ollama create example -f Modelfile // 在 Ollama 中创建模型 ollama run example // 运行模型GGUF 训练项目 Unsloth多态模型ollama run llava "What's in this image? /Users/jmorgan/Desktop/smile.png" The image features a yellow smiley face, which is likely the central focus of the picture.提示作为参数传递ollama run llama3.1 "Summarize this file: $(cat README.md)" Ollama is a lightweight, extensible framework for building and running language models on the local machine. It provides a simple API for creating, running, and managing models, as well as a library of pre-built models that can be easily used in a variety of applications.模型微调FROM gemma2 # set the temperature to 1 [higher is more creative, lower is more coherent] PARAMETER temperature 1 # set the system message SYSTEM """ You are Mario from Super Mario Bros. Answer as Mario, the assistant, only. """ollama create mario -f ./Modelfile ollama run mario >>> hi Hello! It's your friend Mario.接口调用curl http://localhost:11434/api/generate -d '{ "model": "gemma2", "prompt":"你好呀" }'curl http://localhost:11434/api/chat -d '{ "model": "gemma2", "messages": [ { "role": "user", "content": "你好呀" } ] }'Python 例子异步聊天import mysql.connector import ollama import asyncio async def main(): client = ollama.AsyncClient() messages = [] while True: if content_in := input('>>> '): messages.append({'role': 'user', 'content': content_in}) message = {'role': 'assistant', 'content': ''} async for response in await client.chat(model='gemma2', messages=messages, stream=True): if response['done']: messages.append(message) content = response['message']['content'] print(content, end='', flush=True) message['content'] += content print() try: asyncio.run(main()) except (KeyboardInterrupt, EOFError): pass模型创建all_content = "你是一个很牛的程序员" modelfile=''' FROM gemma2 PARAMETER temperature 1 SYSTEM %s '''.strip() % all_content ollama.create(model='maon', modelfile=modelfile)流式输出from ollama import chat messages = [ { 'role': 'user', 'content': '你好呀', }, ] for part in chat('gemma2', messages=messages, stream=True): print(part['message']['content'], end='', flush=True) print()解决网络搜索待更新...
2023年08月23日
400 阅读
0 评论
42 点赞
2023-08-16
【技术分享】RTLO 伪装程序后缀名
简介Unicode RTLO(Right-to-Left Override)是Unicode字符编码中的一种特殊字符,字符的作用是改变文本的文字流方向,可以使原本从左向右的文字流变为从右向左,我们可以通过这种办法伪装恶意可执行程序,这种办法会有小小的弊端自己发现吧。后缀伪装首先把文件扩展给勾上,方便我们看文件后缀,这里我做了个简单的 exe 程序做演示(可以是木马程序)。然后我们对程序 重命名,注意光标的位置是在 .exe 的前面,这个时候我们就可以插入特殊字符了。我们输入 gnp 也就是 png 的字母反转,重命名好后就可以了。你就会发现,变成了 png 图片格式了,而且还可以运行,再看看任务管理器,哈哈哈 是不是挺意外的进程中直接跑 png,其实有很多小例子,比如:exe 改 bat,改 dll 都是可以被执行的,在任务管理器中都可以看到的(这里就不过多演示了)。scr // 可以显示软件图标 com // 不能显示软件图标 bat cmd // 不能显示软件图标可以发现在内容中会有 exe 字样,是不是感觉很敏感,不怕不怕,以上是我收集到了一些可执行后缀(推荐 scr)。Go 实现package main import ( "fmt" "os" ) func main() { var oldfile string = "Panda.scr" var newfile string = "Panda\xe2\x80\xaegnp.scr" err := os.Rename(oldfile, newfile) if err != nil { fmt.Println("incorrect file naming.", err) } else { fmt.Println("Successful file renaming!") } }Python 实现import os oldfile = "Panda.scr" newfile = "Pandagnp.scr" try: os.rename(oldfile, newfile) print("Successful file renaming!") except OSError as err: print("Incorrect file naming.", err)利用总结1.利用这个办法伪装木马程序,如:pdf,zip,png 等文件后缀。2.利用压缩包自解压执行的功能,配合 RLO 实现压缩包执行伪装。Tips:内嵌一个执行,比如打开伪装的程序,程序运行后木马上线,那么再打开内置的 pdf文档,就不会被怀疑了。
2023年08月16日
614 阅读
0 评论
37 点赞
2023-07-15
【技术分享】工学云定位打卡工具 ( 去版本失效 )
简介蘑菇钉打卡工具,它是个32位程序是用易语言写的,主要针对学工云实习打卡,之前实习的时候打比赛去了所以免实习了,但这个软件我也用过一下,主要填写账号和密码能够自定义打卡位置定时打卡之类的,看了下原理是利用Web官网的API进行提交自定义位置来欺骗服务器实现的。今天看到群里说失效了,但我觉得是没钱赚关掉了而已,只会弹出个失效窗口然后关闭程序,对普通人来说就是用不了,反正没事给群友做点好事吧。程序分析通过IDA分析发现,有部分是学工云web上的API接口,直接进去看代码的实现逻辑。这里用了官方的登录接口,来获用户取关键返回信息进行下一个打卡API的参数提交。这里是定义的参数,可以发现 lastAddress 这个参数如果猜的不错的话,就是最后位置如果错了就当我没说,主要目的是来去弹出的,说点废话凑点页数而已(哈哈哈)。弹窗去除正常运行的情况下,是进不去的这个软件是完全关闭了,直接用x64dbg去下弹窗吧。。。最简单的办法就是,字符串搜索了在主模块中进行搜索,可以右键选择也可以 shift + d,看到什么字符串就搜什么吧,这里我搜的是 版本失效。0040D36C | 0F84 3E000000| je 0x40D3B0 | 0040D372 | BB 06000000 | mov ebx,6 | 0040D377 | E8 883CFFFF | call 0x401004 | 0040D37C | 68 01030080 | push 80000301 | 0040D381 | 6A 00 | push 0 | 0040D383 | 68 00000000 | push 0 | 0040D388 | 68 04000080 | push 80000004 | 0040D38D | 6A 00 | push 0 | 0040D38F | 68 9D044D00 | push 0x4D049D | 4D049D:"版本已失效,请进通知群687082138更新" 0040D394 | 68 04000000 | push 4 | 0040D399 | BB 40934100 | mov ebx, 0x419340 | 0040D39E | E8 859E0000 | call 0x417228 | 0040D3A3 | 83C4 34 | add esp,34 | 0040D3A6 | 6A 00 | push 0 | 0040D3A8 | E8 9F9E0000 | call 0x41724C | 0040D3AD | 83C4 04 | add esp,4 |找到 je 改成 jne 就不跳转到弹窗代码上了,也就正常运行软件了。好啦然后打个补丁,软件就可以出院啦,经过测试是可以正常打卡的,我的猜测是正确的。。。{cloud title="蘑菇钉打卡工具(去版本失效 )" type="default" url="https://www.52tt.pro/usr/uploads/2023/07/35926594.zip" password=""/}
2023年07月15日
162 阅读
0 评论
17 点赞
2023-06-01
【信息溯源】Todesk | 向日葵信息溯源
简介在实际的攻防演练中,常常会出现追踪攻击来源并反制取证的情景,假设我们已经成功连上了目标主机,目标主机如果安装了Todesk,那么恭喜你可以通过查看Todesk目录下的 config.ini 文件来获取一些登录等相关信息。Todesk 配置文件downloadtimes 下载Todesk的时间updatePassTime 最近一次使用时间Version Todesk版本号clientid 客户端IDLoginPhone 手机号LoginEmail 邮箱账户只要登录过Todesk,它会把一些信息写入到根目录下的,config.ini配置文件中。这个跟版本没关系的,即使是最新版的Todesk,也会写到配置文件中,向日葵 配置文件向日葵,感兴趣的可以去看一下,里面也有一些信息。Go Todesk 第一版(默认路径)// main.go package main import ( "bufio" "fmt" "os" "strings" ) func main() { config_file := "C:/Program Files/ToDesk/config.ini" file, err := os.Open(config_file) if err != nil { fmt.Printf("Failed to read config file: %v\n", err) return } defer file.Close() config := make(map[string]string) scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if len(line) == 0 || strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") { continue } parts := strings.SplitN(line, "=", 2) if len(parts) != 2 { continue } key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) config[key] = value } download_times := config["downloadtimes"] version := config["Version"] client_id := config["clientId"] temp_auth_pass_ex := config["tempAuthPassEx"] resolution := config["Resolution"] update_pass_time := config["updatePassTime"] private_data := config["PrivateData"] login_phone := config["LoginPhone"] login_email := config["LoginEmail"] fmt.Println("*********** 第一版 ***********") fmt.Printf("电子邮件账户:%s\n", login_email) fmt.Printf("手机号:%s\n", login_phone) fmt.Printf("下载时间:%s\n", download_times) fmt.Printf("最近一次使用ToDesk时间:%s\n", update_pass_time) fmt.Printf("当前屏幕尺寸:%s\n", resolution) fmt.Printf("Todesk版本号:%s\n", version) fmt.Printf("客户端ID:%s\n", client_id) fmt.Printf("私密数据:%s\n", private_data) fmt.Printf("临时认证密钥:%s\n", temp_auth_pass_ex) }你肯会说如果他没安装到默认路径怎么办呢?很简单以下是改进版自动查找路径。Go Todesk 第二版(自动搜索)// main.go package main import ( "bufio" "fmt" "os" "path/filepath" "strconv" "strings" ) const ( targetFilename = "config.ini" ) type ConfigInfo struct { DownloadTimes string Version string ClientID string TempAuthPass string Resolution string UpdatePassTime string PrivateData string LoginPhone string LoginEmail string } func visit(path string, info os.FileInfo, err error) error { if !info.IsDir() && info.Name() == targetFilename && strings.Contains(path, "ToDesk") { configFile, err := os.Open(path) if err != nil { fmt.Println(err) return nil } defer configFile.Close() configInfo := ConfigInfo{} scanner := bufio.NewScanner(configFile) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") { continue } pair := strings.SplitN(line, "=", 2) if len(pair) != 2 { continue } key := strings.TrimSpace(pair[0]) value := strings.TrimSpace(pair[1]) switch key { case "downloadtimes": configInfo.DownloadTimes = value case "Version": configInfo.Version = value case "clientId": configInfo.ClientID = value case "tempAuthPassEx": configInfo.TempAuthPass = value case "Resolution": configInfo.Resolution = value case "updatePassTime": configInfo.UpdatePassTime = value case "PrivateData": configInfo.PrivateData = value case "LoginPhone": configInfo.LoginPhone = value case "LoginEmail": configInfo.LoginEmail = value default: } } fmt.Println("*********** 第一版 ***********") fmt.Printf("电子邮件账户:%s\n", configInfo.LoginEmail) fmt.Printf("手机号:%s\n", configInfo.LoginPhone) fmt.Printf("下载时间:%s\n", configInfo.DownloadTimes) fmt.Printf("最近一次使用ToDesk时间:%s\n", configInfo.UpdatePassTime) fmt.Printf("当前屏幕尺寸:%s\n", configInfo.Resolution) fmt.Printf("Todesk版本号:%s\n", configInfo.Version) fmt.Printf("客户端ID:%s\n", configInfo.ClientID) fmt.Printf("私密数据:%s\n", configInfo.PrivateData) fmt.Printf("临时认证密钥:%s\n", configInfo.TempAuthPass) os.Exit(0) } return nil } func isNumeric(s string) bool { _, err := strconv.ParseFloat(s, 64) return err == nil } func main() { driveLetters := []string{"C", "D", "E", "F", "G"} for _, driveLetter := range driveLetters { drivePath := fmt.Sprintf("%s:\\", driveLetter) err := filepath.Walk(drivePath, visit) if err != nil { fmt.Printf("访问 %s 目录时出错:%v\n", drivePath, err) } } }好啦,打包一下就能做一些骚操作了。(小声:自己发挥吧)Python 版本(自动搜索)import os import configparser target_filename = 'config.ini' target_strings = ['ToDesk'] drive_letters = ['C', 'D', 'E', 'F', 'G'] for drive_letter in drive_letters: drive_path = f"{drive_letter}:\\" for dirpath, dirnames, filenames in os.walk(drive_path): if target_filename in filenames and all(s in dirpath for s in target_strings): config_path = os.path.join(dirpath, target_filename) config = configparser.ConfigParser() config.read(config_path) download_times = config.get('ConfigInfo', 'downloadtimes') version = config.get('ConfigInfo', 'Version') client_id = config.get('ConfigInfo', 'clientId') temp_auth_pass = config.get('ConfigInfo', 'tempAuthPassEx') resolution = config.get('ConfigInfo', 'Resolution') update_pass_time = config.get('ConfigInfo', 'updatePassTime') private_data = config.get('ConfigInfo', 'PrivateData') login_phone = config.get('ConfigInfo', 'LoginPhone') login_email = config.get('ConfigInfo', 'LoginEmail') print(f"电子邮件账户:{login_email}") print(f"手机号:{login_phone}") print(f"下载时间:{download_times}") print(f"最近一次使用ToDesk时间:{update_pass_time}") print(f"当前屏幕尺寸:{resolution}") print(f"Todesk版本号:{version}") print(f"客户端ID:{client_id}") print(f"私密数据:{private_data}") print(f"临时认证密钥:{temp_auth_pass}") quit()运行效果
2023年06月01日
202 阅读
0 评论
14 点赞
2023-04-25
【技术分享】Frida Hook篇
简介Frida是一款基于Python + JavaScript 的hook框架,本质是一种动态插桩技术。可以用于Android、Windows、iOS等各大平台,其执行脚本基于Python或者Node.js写成,而注入代码用JavaScript写成,所以有必要了解一些这些语言的语法。本文简单讲述Frida框架的使用。Windows修改函数返回#include <stdio.h> int license() { return 0; } int main() { if(license() == 1) { printf("%s", "你做到了~"); }else{ printf("%s", "你失败了!"); } return 0; }这里我用C语言编写一个简单的例子,尝试hook函数的返回值修改为 1。objdump -M intel -d Demo | grep call这里我用 objdump 进行过滤反汇编代码,我的习惯用全局过滤 call 看看都调用了哪些函数,因为有的编译是不显示函数名称的,也就无法判断其函数的用途。这里通过调用 license() 函数判断返回值,那么只要 hook 0x1139 这个地址的返回值,就能改变判断执行的流程。var moduleName = "demo"; var baseAddress = Module.findBaseAddress(moduleName); var absoluteAddress = baseAddress.add(0x1139); Interceptor.attach(absoluteAddress, { onLeave: function(retval) { retval.replace(1); console.log("\n"); } });frida -l code.js -f demo因为程序是一运行就执行,这里用这种办法来带动程序执行并进行hook。frida-tracefrida-trace -f demo -i "printf"可以利用 frida-trace 监听函数调用和获取参数等等一系列操作。WSASend Hook// 自适应旧数据大小 Interceptor.attach(Module.getExportByName('Ws2_32.dll', 'WSASend'), { onEnter: function (args) { var hexDataWithSpaces = "00 00 00 BD 00 14 00 02 "; function hexStringWithSpacesToBytes(hexStr) { var hexValues = hexStr.replace(/\s+/g, '').match(/.{1,2}/g); var bytes = []; for (var i = 0; i < hexValues.length; i++) { bytes.push("0x" + hexValues[i]); } return bytes; } var socket = args[0].toInt32(); var sockaddr_in = Memory.alloc(16); var sockaddr_in_size = Memory.alloc(4); Memory.writeU32(sockaddr_in_size, 16); var getpeername = Module.getExportByName('Ws2_32.dll', 'getpeername'); var result = new NativeFunction(getpeername, 'int', ['int', 'pointer', 'pointer'])(socket, sockaddr_in, sockaddr_in_size); if (result === 0) { var ip = Memory.readU32(sockaddr_in.add(4)); var ipStr = ((ip & 0xFF)) + '.' + ((ip >> 8) & 0xFF) + '.' + ((ip >> 16) & 0xFF) + '.' + ((ip >> 24) & 0xFF); console.log("[+] IP Address: " + ipStr); } else { console.log("[-] Failed to retrieve IP address"); } var lpBuffers = args[1]; var dwBufferCount = args[2].toInt32(); for (var i = 0; i < dwBufferCount; i++) { var lpBuffer = lpBuffers.add(i * Process.pointerSize * 2); var bufLen = Memory.readU32(lpBuffer); if (bufLen === 173) { console.log("[+] Length=" + bufLen); var newData = hexStringWithSpacesToBytes(hexDataWithSpaces); var newDataLength = newData.length; var newBufPtr = Memory.alloc(1024); Memory.writeByteArray(newBufPtr.add(24), newData); Memory.writePointer(lpBuffer.add(Process.pointerSize), newBufPtr.add(24)); Memory.writeU32(lpBuffer, newDataLength); var modifiedData = Memory.readByteArray(newBufPtr.add(24), newDataLength); console.log(modifiedData); } } } });WSARcev Hook// 自适应旧数据大小 var buff = Memory.alloc(0x6) Memory.writeByteArray(buff, [0x32,0x32,0x32,0x32,0x32,0x32]); Interceptor.attach(Module.getExportByName('Ws2_32.dll', 'WSARecv'), { onEnter: function (args) { this.lpBuffersPtr = args[1]; this.lpNumberOfBytesRecvd = args[3]; }, onLeave: function (retval) { var lenPtr = Memory.readPointer(this.lpNumberOfBytesRecvd); var buflen = parseInt(lenPtr, 16); if (buflen == 2) { Memory.writePointer(this.lpBuffersPtr.add(Process.pointerSize), buff); ptr(this.lpNumberOfBytesRecvd).writeUInt(0x6); } } });frida -p 2085 -l WSARcev.jsJmp Hookvar baseAddress = ptr('0x4010F5'); var jmpAddress = ptr('0x4010FB'); var offset = jmpAddress.sub(baseAddress.add(5).toInt32()); var jmpOpcode = [ 0xE9, offset & 0xFF, (offset >> 8) & 0xFF, (offset >> 16) & 0xFF, (offset >> 24) & 0xFF ]; Memory.protect(baseAddress, 8, 'rw-'); Memory.writeByteArray(baseAddress, jmpOpcode);系统提权var path = 'C:/Users/28729/Desktop/main.exe'; // 替换你要执行的程序路径(不能包含中文字符) var module = Process.getModuleByName('kernel32.dll'); var WinExec = module.findExportByName('WinExec'); var WinExec = new NativeFunction(WinExec,'int', ['pointer', 'uint32']); WinExec(Memory.allocUtf8String(path), 5); console.log(WinExec.toString());端口修改Interceptor.attach(Module.getExportByName('ws2_32.dll', 'connect'), { onEnter: function(args) { var sockaddr = args[1]; var ip = Memory.readU32(sockaddr.add(4)); var ipStr = ((ip & 0xFF)) + '.' + ((ip >> 8) & 0xFF) + '.' + ((ip >> 16) & 0xFF) + '.' + ((ip >> 24) & 0xFF); // 修改端口为 80 (0x0050) Memory.writeU16(sockaddr.add(2), 0x0050); var modifiedPort = Memory.readU16(sockaddr.add(2)); console.log("[+] Modified to connect to: " + ipStr + ":" + modifiedPort); } });变速齿轮变速原理变速齿轮的原理 变速齿轮(变速精灵)通过修改游戏运行时的时间流逝速度,实现加速或减速效果。其核心思路是拦截并修改游戏调用的时间相关函数的返回值。关键时间函数 大部分游戏依赖Windows提供的计时器API来获取时间流逝数据,常用的函数包括:GetTickCount: 获取系统启动后经过的毫秒数。timeGetTime: 获取自系统启动以来的时间,单位为毫秒。这些函数在游戏中用于时间逻辑和动画刷新等。Hook技术的应用 为实现变速功能,需要使用Hook技术拦截上述API函数:在返回值的基础上加上变速比例。例如,将时间流逝的速度乘以一个比例系数(如2倍速或0.5倍速)。修改后的返回值会被游戏程序使用,从而达到加速或减速效果。实现效果 修改计时器函数返回值后:游戏中的时间会按照设定的速度流逝。动画、技能冷却、任务时间等都会受到影响。注意事项对API函数的Hook需要精确,避免引起其他程序崩溃。修改速度可能会影响游戏网络同步或反作弊机制,因此需要谨慎使用。通过Hook系统的计时器API(如GetTickCount、timeGetTime),改变游戏对时间的感知速度,从而实现游戏变速功能。这种方式通用于大多数基于Windows计时器的游戏程序。推荐下 Cheat Engine,这个工具也可以实现。Cheat Engine 此源码仅支持 64位 Python。{cloud title="变速精灵" type="bd" url="https://pan.baidu.com/s/1byEIfi3HHoZaMKy5jibVyw?pwd=2085" password=""/}AndroidHook okhttp3Java.perform(function () { var RequestBuilder = Java.use("okhttp3.Request$Builder"); var Buffer = Java.use("okio.Buffer"); RequestBuilder.build.implementation = function () { var request = this.build(); var body = request.body(); if (body != null) { var buffer = Buffer.$new(); body.writeTo(buffer); var raw_body = buffer.readUtf8(); console.log("=== HTTP Request ==="); console.log("URL: " + request.url().toString()); console.log("Body: " + raw_body); } return request; }; });so 加载检测function hook_dlopen() { Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { onEnter: function(args) { var pathptr = args[0]; if (pathptr !== undefined && pathptr != null) { var path = ptr(pathptr).readCString(); console.log("Loaded: " + path); } } }); }var dlopen = Module.findExportByName(null, "dlopen"); // 6.0 var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); // 8.1+ Interceptor.attach(dlopen, { onEnter: function (args) { var path_ptr = args[0]; this.path = ptr(path_ptr).readCString(); console.log("[+] dlopen onEnter => " + this.path + " > 开始加载"); }, onLeave: function (retval) { console.log("[-] dlopen onLeave => " + this.path + " > 加载完成"); } }); Interceptor.attach(android_dlopen_ext, { onEnter: function (args) { var path_ptr = args[0]; this.path = ptr(path_ptr).readCString(); console.log("[+] dlopen_ext onEnter => " + this.path + " > 开始加载"); }, onLeave: function (retval) { console.log("[-] dlopen_ext onLeave => " + this.path + " > 加载完成"); } });其它-JS逆向Hook 解析参数// === JSON.parse (解密后) === var _json_parse = JSON.parse; JSON.parse = function (text, reviver) { console.log("JSON.parse:", text); debugger; return _json_parse.call(this, text, reviver); }; // === JSON.stringify (加密前) === var _json_stringify = JSON.stringify; JSON.stringify = function (value, replacer, space) { console.log("JSON.stringify:", value); debugger; return _json_stringify.call(this, value, replacer, space); }; // === XMLHttpRequest (发送 & 接收) === (function() { var _open = XMLHttpRequest.prototype.open; var _send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url) { this._url = url; this._method = method; return _open.apply(this, arguments); }; XMLHttpRequest.prototype.send = function(body) { console.log(`XHR Request [${this._method}] ${this._url}`, body); this.addEventListener("load", function() { console.log(`XHR Response [${this._method}] ${this._url}`, this.responseText); }); return _send.apply(this, arguments); }; })(); // === fetch (发送 & 接收) === var _fetch = fetch; fetch = function(input, init) { console.log("fetch Request:", input, init && init.body); return _fetch.apply(this, arguments).then(async res => { let clone = res.clone(); let text = await clone.text(); console.log("fetch Response:", text); return res; }); }; // === WebSocket (发送 & 接收) === var _ws_send = WebSocket.prototype.send; WebSocket.prototype.send = function(data) { console.log("WS Send:", data); return _ws_send.apply(this, arguments); }; var _WebSocket = window.WebSocket; window.WebSocket = function(...args) { var ws = new _WebSocket(...args); ws.addEventListener("message", function(event) { console.log("WS Message:", event.data); }); return ws; };Hook 原生加密解密// === CryptoJS AES 加密 / 解密 === if (window.CryptoJS && CryptoJS.AES) { var _aes_encrypt = CryptoJS.AES.encrypt; CryptoJS.AES.encrypt = function (msg, key, cfg) { console.log("AES encrypt input:", msg); debugger; return _aes_encrypt.apply(this, arguments); }; var _aes_decrypt = CryptoJS.AES.decrypt; CryptoJS.AES.decrypt = function (ciphertext, key, cfg) { console.log("AES decrypt input:", ciphertext); var result = _aes_decrypt.apply(this, arguments); console.log("AES decrypt output:", result.toString(CryptoJS.enc.Utf8)); debugger; return result; }; } // === CryptoJS RC4 加密 / 解密 === if (window.CryptoJS && CryptoJS.RC4) { var _rc4_encrypt = CryptoJS.RC4.encrypt; CryptoJS.RC4.encrypt = function (msg, key) { console.log("RC4 encrypt input:", msg); debugger; return _rc4_encrypt.apply(this, arguments); }; var _rc4_decrypt = CryptoJS.RC4.decrypt; CryptoJS.RC4.decrypt = function (ciphertext, key) { console.log("RC4 decrypt input:", ciphertext); var result = _rc4_decrypt.apply(this, arguments); console.log("RC4 decrypt output:", result.toString(CryptoJS.enc.Utf8)); debugger; return result; }; } // === Base64 === var _atob = atob; window.atob = function (str) { console.log("Base64 decode input:", str); var result = _atob(str); console.log("Base64 decode output:", result); debugger; return result; }; var _btoa = btoa; window.btoa = function (str) { console.log("Base64 encode input:", str); debugger; return _btoa(str); };Hook 网络通信函数// ===== Hook fetch API ===== // 拦截并打印 fetch 请求和响应 function hookFetch() { var _fetch = fetch; fetch = function (input, init) { console.log("fetch request:", input, init?.body); return _fetch.apply(this, arguments).then(async res => { let clone = res.clone(); console.log("fetch response:", await clone.text()); return res; }); }; } // ===== Hook XMLHttpRequest API ===== // 拦截并打印 XHR 请求和响应 function hookXHR() { var _open = XMLHttpRequest.prototype.open; var _send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { this._method = method; this._url = url; return _open.apply(this, arguments); }; XMLHttpRequest.prototype.send = function (body) { console.log(`XHR [${this._method}] ${this._url}`, body); this.addEventListener("load", () => { console.log(`XHR response [${this._method}] ${this._url}`, this.responseText); }); return _send.apply(this, arguments); }; } // ===== Hook WebSocket API ===== // 拦截并打印 WebSocket 收发数据 function hookWebSocket() { var _ws_send = WebSocket.prototype.send; WebSocket.prototype.send = function (data) { console.log("WS send:", data); return _ws_send.apply(this, arguments); }; var _WebSocket = window.WebSocket; window.WebSocket = function (...args) { var ws = new _WebSocket(...args); ws.addEventListener("message", function (event) { console.log("WS message:", event.data); }); return ws; }; } // ===== Hook WebRTC send ===== // 拦截 WebRTC 数据 function hookWebRTC() { if (window.RTCPeerConnection) { var _send = RTCDataChannel.prototype.send; RTCDataChannel.prototype.send = function (data) { console.log("WebRTC send:", data); return _send.apply(this, arguments); }; } }
2023年04月25日
488 阅读
0 评论
33 点赞
1
2
...
5
0:00