Win32汇编学习笔记10.OD插件

news/2025/2/20 19:35:14/文章来源:https://www.cnblogs.com/weiyuanzhang/p/18717485

原文链接:https://www.bpsend.net/thread-223-1-1.html

筛选器异常插件

被调试程序: 📎TestUnh.zip

我们用OD条试试发现,无法断下 筛选器异常

异常产生之后 异常首先会给调试器 调试器不处理就会给 SEH , SEH 不处理的话有又给调试器, 调试器不处理的话就会给筛选器 , 筛选器不处理的就会给操作系统,直接退进程

这里面是2次的异常派发流程 第一次 是 到 给SHE 之后属于第二次 派发流程

第二次派发是一个分支,会判断是否有调试器,有的话就给调试器,没有的话直接给筛选器,因此系统会检查软件是否处于调试状态 即检查调试端口是否存在,如果存在,异常就会给调试器,否则给筛选器,所以我们在用 OD 调试筛选器异常时,是不可能再筛选器这下断点的,因此系统检测到了调试器,异常就不会给筛选器

因此我们可以欺骗系统,告诉系统,不存在调试器,直接给筛选器,因此我们需要找到 异常派发时,系统在哪判断是否有调试器存在的,找到后我们可以更改判断结果 或者跳转 的地方

这个在 msdn中有明确的说明 UnhandledExceptionFilter 来判断的

img

img

img

img

接下来我们只需要 更改条件跳转 ,看 筛选器异常能否断下来 ,就可以判断是在哪 判断是否有 调试器的

img

img

img

img

跳过测试可知,当 call 函数 返回值 为0 就可以收到 筛选器异常,为1就无法收到,那么位置我们已经找到了,接下来我们只需要改 判断 函数下一行 把 eax 的值 改为0 就可以 收到 筛选器异常了 即改成 xor eax,eax

img

因此我们只需要写一个插件让他自己去改

img

img

img

接下来就是写插件了

#include <windows.h>
#include "Plugin.h"int ODBG_Plugindata(char* shortname) {strcpy_s(shortname, 32,"筛选器异常修复");return PLUGIN_VERSION;}int ODBG_Plugininit(int ollydbgversion, HWND hw, ulong* features) {return 0;
}

生成解决方案时

img

img

把生成的 dll 放到指定目录,再打开 OD,发现我们的插件已经 加载进来了

img

既然插件可以识别那下面就是增加功能了

img

我们的目标把红框的代码改成 xor ,eax,eax 即 把 76A6D810 处的 值 改成 33 ,如果直接写固定地址,那么电脑重启后dll 的路径就发生过了改变,即 该处地址也变化了,Name插件也用不了了,因此我们需要用相对地址,可以函数地址+偏移 或者 模块地址 + 偏移

img

我们可以看到 kernelbase 的 地址是 768B0000 那么 跟 76A6D81B 的偏移值位为 0x1BD81B

#include <windows.h>
#include "Plugin.h"void  FixUnHandledException();int ODBG_Plugindata(char* shortname) 
{strcpy_s(shortname, 32,"筛选器异常修复");FixUnHandledException() ;return PLUGIN_VERSION;}int ODBG_Plugininit(int ollydbgversion, HWND hw, ulong* features) 
{return 0;
}void  FixUnHandledException() 
{//1.定位 test eax,eax的地址HMODULE hKernelbase = GetModuleHandle("Kernelbase");   LPBYTE pDst = (LPBYTE)hKernelbase + 0x1BD81B;//2.修改   test eax,eax  位 xor eax,eaxDWORD dwOldPro = 0;//修改内存属性VirtualProtect(pDst, 1, PAGE_EXECUTE_READWRITE, &dwOldPro);*pDst = 0x33;//还原内存属性VirtualProtect(pDst, 1, dwOldPro, &dwOldPro);
}

img

运行发现并没有被修改,猜想可能是因为 修改的太早,程序还没有运行

img

img

#include <windows.h>
#include "Plugin.h"void  FixUnHandledException();
int ODBG_Paused(int reason, t_reg* reg);int ODBG_Plugindata(char* shortname) 
{strcpy_s(shortname, 32,"筛选器异常修复");return PLUGIN_VERSION;}int ODBG_Plugininit(int ollydbgversion, HWND hw, ulong* features) 
{return 0;
}int ODBG_Paused(int reason, t_reg* reg)
{if (reason == PP_EVENT){FixUnHandledException();}return 1;
}void  FixUnHandledException() 
{//1.定位 test eax,eax的地址HMODULE hKernelbase = GetModuleHandle("Kernelbase");   LPBYTE pDst = (LPBYTE)hKernelbase + 0x1BD81B;//2.修改   test eax,eax  位 xor eax,eaxDWORD dwOldPro = 0;//修改内存属性VirtualProtect(pDst, 1, PAGE_EXECUTE_READWRITE, &dwOldPro);*pDst = 0x33;//还原内存属性VirtualProtect(pDst, 1, dwOldPro, &dwOldPro);//用于判断dll有没有被加载MessageBox(NULL, "这是我的插件", "提示", MB_OK);
}

调试发现还是没改成功

那是因为 OD 调试时有2个进程 , 我们修改时 修改的是 OD 的进程 , 并没有修改到被调试的进程,但是地址是对的,因为在同一台电脑, kernelbase 的 地址是固定的 , 因此我们需要跨进程修改 内存 OD 提供了 对应的 api

内存函数

结构

OILYDBG为被调试程序保存了一份已分配内存块的信息表,它由TMG

MEMORY

MEMORY*FINDMEMORY(ULONG ADDR);

VOID HAVECOPYOFMEMORY(CHAR *COPY,ULONG BASE,ULONG SIZE);

ULONG READMEMORY(VOID *BUF.ULONG ADDR.ULONG SIZE.INT MODE):

WRITEMEMORY(VOID*BUF ULONG ADDR,ULONG SIZE,INT MODE):

ULONG W

INT LISTMEMORY(VOID);

image.png

image.png

#include <windows.h>
#include "Plugin.h"#pragma comment(lib,"Ollydbg.lib")
void  FixUnHandledException();
int ODBG_Paused(int reason, t_reg* reg);int ODBG_Plugindata(char* shortname) 
{strcpy_s(shortname, 32,"筛选器异常修复");  return PLUGIN_VERSION;}int ODBG_Plugininit(int ollydbgversion, HWND hw, ulong* features) 
{return 0;
}int ODBG_Paused(int reason, t_reg* reg)
{if (reason == PP_EVENT){FixUnHandledException();}return 1;
}void  FixUnHandledException() 
{//1.定位 test eax,eax的地址HMODULE hKernelbase = GetModuleHandle("Kernelbase");   LPBYTE pDst = (LPBYTE)hKernelbase + 0x1BD81B;#if 0//2.修改   test eax,eax  位 xor eax,eaxDWORD dwOldPro = 0;//修改内存属性VirtualProtect(pDst, 1, PAGE_EXECUTE_READWRITE, &dwOldPro);*pDst = 0x33;//还原内存属性VirtualProtect(pDst, 1, dwOldPro, &dwOldPro);
#endif // 0BYTE btCode = 0x33;Writememory(&btCode, (ulong)pDst, 1, MM_SILENT);//用于判断dll有没有被加载;MessageBox(NULL, "这是我的插件", "提示", MB_OK);
}

img

再次调试,可以发现我们成功修改了,筛选器异常也可以成功调试了

上面虽然成功了,但是兼容性还存在问题,要求二进制文件必须是一样,但是不同的电脑,编译出来的二进制文件是可能不一样的,到时兼容性还是出问题,这种处理方式是搜机器码, 先找到特征码(离修改代码最近的唯一值) ,再去寻找要修改的代码,不然直接搜要修改代码可能会有多个 ,类似于切割出指定字符串

窗口过程函数地址错误

OD 如果没有安装修复插件啊,有些 窗口的过程函数地址是错误的 看雪下载的已经修复

img

获取过程函数的地址有2种

一种是通过 GetWindowLong 获得 一种是通过 GetClassLong

通过OD 调 OD 对 GetWindowLong 和 GetClassLong 下断点

可以知道是通过 GetClassLongA 拿到的,因此可以猜想 是因为 GetClassLongA 拿的是 多字节的窗口过程函数 而Unicode 窗口 获取 过程函数需要用 GetClassLongW,因此只需要在获取 Unicode 窗口 过程函数是是 用 GetClassLongW 就可以了

跟窗口相关的函数都在 user32 里面 ,跟界面相关的 都在 user32 里面

img

调试可知 GetClassLong 调取的都是该处的 GetClassLongA ,因此我们只需要改动这里就可以了

img

img

jmp是一个间接调,先取地址,在跳到目标位置,因此我们改该处地址就可以了,

我们自己可以实现一个 MyGetClassLong 然后把地址传过来这样他就会调我们的函数,我们只需要做一下判断是 多字节窗口还是 Unicode 就可以了

该处地址的我们还是可以 通过算偏移得到 ,因为 OD 版本一样,所以偏移也是一样的

img

所以偏移是 0x0050D858 - 0x00400000 = 0x0010D858

#include <windows.h>
#include "Plugin.h"#pragma comment(lib,"Ollydbg.lib")
void  FixUnHandledException();
int ODBG_Paused(int reason, t_reg* reg);
void FixGetClassLong();int ODBG_Plugindata(char* shortname) 
{strcpy_s(shortname, 32,"筛选器异常修复");  return PLUGIN_VERSION;}int ODBG_Plugininit(int ollydbgversion, HWND hw, ulong* features) 
{FixGetClassLong();return 0;
}int ODBG_Paused(int reason, t_reg* reg)
{if (reason == PP_EVENT){FixUnHandledException();}return 1;
}void  FixUnHandledException() 
{//1.定位 test eax,eax的地址HMODULE hKernelbase = GetModuleHandle("Kernelbase");   LPBYTE pDst = (LPBYTE)hKernelbase + 0x1BD81B;#if 0//2.修改   test eax,eax  位 xor eax,eaxDWORD dwOldPro = 0;//修改内存属性VirtualProtect(pDst, 1, PAGE_EXECUTE_READWRITE, &dwOldPro);*pDst = 0x33;//还原内存属性VirtualProtect(pDst, 1, dwOldPro, &dwOldPro);
#endif // 0BYTE btCode = 0x33;Writememory(&btCode, (ulong)pDst, 1, MM_SILENT);//用于判断dll有没有被加载//MessageBox(NULL, "这是我的插件", "提示", MB_OK);
}LONG MyGetClassLong(HWND hWnd,int nIndex)
{if (IsWindowUnicode(hWnd))  // 判断一个窗口是否是 unicode窗口{return GetClassLongW(hWnd, nIndex);}else{return GetClassLongA(hWnd, nIndex);}}void FixGetClassLong()
{//1.定位地址HMODULE Hod = GetModuleHandle(NULL);  //获取主模块基址(OD)LPDWORD pAddr =(LPDWORD)((LPBYTE)Hod + 0x10D858);//2.修改为自己的地址DWORD dwOldPro = 0;//修改内存属性VirtualProtect(pAddr, 1, PAGE_EXECUTE_READWRITE, &dwOldPro);*pAddr = (DWORD)MyGetClassLong;//还原内存属性VirtualProtect(pAddr, 1, dwOldPro, &dwOldPro);}

调试会发现会崩, 代码没崩,但是运行了一段时间就崩一般情况下是栈歪了

经过调试,还有2个参数在栈上

系统的api 一般都是标准调用约定, 但是我们自己的代码需要自己去平栈, 因此要改动调用约定 改成 stdcall

#include <windows.h>
#include "Plugin.h"#pragma comment(lib,"Ollydbg.lib")
void  FixUnHandledException();
int ODBG_Paused(int reason, t_reg* reg);
void FixGetClassLong();int ODBG_Plugindata(char* shortname) 
{strcpy_s(shortname, 32,"筛选器异常修复");  return PLUGIN_VERSION;}int ODBG_Plugininit(int ollydbgversion, HWND hw, ulong* features) 
{FixGetClassLong();return 0;
}int ODBG_Paused(int reason, t_reg* reg)
{if (reason == PP_EVENT){FixUnHandledException();}return 1;
}void  FixUnHandledException() 
{//1.定位 test eax,eax的地址HMODULE hKernelbase = GetModuleHandle("Kernelbase");   LPBYTE pDst = (LPBYTE)hKernelbase + 0x1BD81B;#if 0//2.修改   test eax,eax  位 xor eax,eaxDWORD dwOldPro = 0;//修改内存属性VirtualProtect(pDst, 1, PAGE_EXECUTE_READWRITE, &dwOldPro);*pDst = 0x33;//还原内存属性VirtualProtect(pDst, 1, dwOldPro, &dwOldPro);
#endif // 0BYTE btCode = 0x33;Writememory(&btCode, (ulong)pDst, 1, MM_SILENT);//用于判断dll有没有被加载//MessageBox(NULL, "这是我的插件", "提示", MB_OK);
}LONG WINAPI MyGetClassLong(HWND hWnd,int nIndex)
{if (IsWindowUnicode(hWnd))  // 判断一个窗口是否是 unicode窗口{return GetClassLongW(hWnd, nIndex);}else{return GetClassLongA(hWnd, nIndex);}
}void FixGetClassLong()
{//1.定位地址HMODULE Hod = GetModuleHandle(NULL);  //获取主模块基址(OD)LPDWORD pAddr =(LPDWORD)((LPBYTE)Hod + 0x10D858);//2.修改为自己的地址DWORD dwOldPro = 0;//修改内存属性VirtualProtect(pAddr, 1, PAGE_EXECUTE_READWRITE, &dwOldPro);*pAddr = (DWORD)MyGetClassLong;//还原内存属性VirtualProtect(pAddr, 1, dwOldPro, &dwOldPro);}

再去调试,发现可以获取正确的 窗口过程函数地址了

img

x32dbg 也可以写插件

img

img

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/884447.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

异步接口如何做接口自动化测试?

异步接口的自动化测试与同步接口有所不同,因为异步接口在发起请求后不会立即返回最终结果,通常需要额外的机制来等待和验证结果。 以下为你详细介绍异步接口自动化测试的实现步骤和方法: 1. 理解异步接口的工作模式 在进行测试之前,需要了解异步接口的工作原理。常见的异步…

【草原之歌】:守护绿色的律动与《草原法》的力量

在广袤的地球上,有一种风景以其独有的辽阔与壮美占据着人们的心灵,那就是——草原。它不仅是大自然赋予的生态宝库,也是许多民族文化的摇篮。然而,草原的保护与管理面临着诸多挑战,因此,《草原法》应运而生,成为了守护这片绿色律动的重要法律保障。今天,让我们一同探索…

清华大学最新力作:DeepSeek从入门到精通

清华大学最新力作:DeepSeek从入门到精通,文章中提到了微博、小红书、抖音、微信公众号等平台的提示语设计方案写文案、做策划、创作各平台内容都能用它找灵感、提效率。文章还讲了选模型和设计提示语的技巧真心建议大家读一读,学会用 DeepSeek,搞创作、做运营都能更上一层楼…

【每日一题】20250215

我只懂得一种活法,那就是找一件事坚持下去,以战胜生命的虚无。【每日一题】(多选)实验表明,可见光通过三棱镜时各色光的折射率 \(n\) 随着波长 \(\lambda\) 的变化符合科西经验公式,\(n=A+\frac{B}{\lambda^2}+\frac{C}{\lambda^4}\),其中 \(A\)、\(B\)、\(C\) 是正的常…

用IRFZ44N MOS管制作一个开关电路并测试其导通电压

用IRFZ44N NMOS管当开关管,设计一个开关电路,控制小灯泡的亮灭 下面是电路图,小灯泡大小为24V 10W图中R1是泄放电阻,因为GS间有结电容,如果没有加R1,在V2关断时,小灯泡不会立即熄灭,还会维持很长一段时间,明显不合理。 相比于三极管,MOS管能够承载的电流更大,更适用…

撑起计算机视觉半边天的ResNet【论文精读】

ResNet论文精读:深度残差学习如何重塑深度学习一、技术演进背景:深度网络的困境与突破 消失的梯度与退化问题 在ResNet提出之前,深度学习领域已通过VGGNet、GoogLeNet等模型验证了网络深度的重要性。然而,当网络深度超过20层时,研究者发现了一个反直觉现象:更深的网络反而…

2025多校冲刺省选模拟赛13

2025多校冲刺省选模拟赛13\(T1\) A. 逆序对 \(56pts\)原题: luogu P5972 [PA 2019] Desant部分分\(56pts\) :爆搜。点击查看代码 int a[50]; pair<int,ll>ans[50]; struct BIT {int c[50];int lowbit(int x){return x&(-x);}void add(int x,int val){for(int i=x;i…

使用 Git 命令和 Github 前须了解的知识

本文不包括 Git 命令的介绍与使用,只分享 Git 的关键概念与 Github 项目的基本工作流程。作者相信先了解它们对后续的学习和工作大有裨益。(如有错误和建议请大家评论告知)版本控制系统 VCS → Version Control System,版本控制系统 → 一个跟踪文件变化、记录修订情况、协…

leetcode hot 02

解题思路:找祖先从底向上递归后序遍历查找,遇到p,q或者空节点就直接返回对应值,当某个节点的左子树、右子树都返回了值,那么就说明该节点就是最近祖先节点,然后把该节点的值继续往上传,直到根节点返回结果。 /*** Definition for a binary tree node.* public class Tre…

Linux介绍及使用

一、linux介绍 1、Linux是一个免费、开源的操作系统,能多用户、多任务、支持多线程和多CPU的操作系统,相对windows更加稳定,在unix系统的基础上开发的系统; 注解:(1)免费:不要钱 (2)源代码公开 (3)多用户 :可以在不同用户操作 (4)多任务:同时执行多个任务 …

Maven 生命周期 Test 阶段遇到的一些问题

Q:无法使用@Test注解,报错 A:最初pom.xml中使用的Junit版本为3,Java 5于 2004 年发布,引入了注解作为语言的一部分。而Junit 3 是在这之前发布的,因此它无法使用注解,将pom.xml中的版本号改为<version>4.13.2</version>后问题解决,可使用@TestQ:在Maven启…