Wker_EXEDebug逆向工具

Wker_EXEDebug

最近没什么特别多的事情,渗透也玩的有点疲惫了,应该换点东西玩玩了,然后我就想起来了之前有点想写一个逆向的工具,所以我就开始了。

安卓我玩的还是不是很溜,所以我目前只做了Windows版本的逆向工具,目前的话呢我只做了两个页面。

  • 内存搜索

  • 反汇编

目前只做了这两个版块,类似于DLL查询注入,页面之类的我还没写,我也会通过这款工具分享一下三个断点具体的实现方法,因为看到网络上面只有很粗糙的介绍,这里我会详细介绍的。

首先:我在内存首次搜索页面做了两种搜索,精确搜索模糊搜索

简单的浏览下互联网,网上没有关于模糊搜索的介绍,只有精确搜索的介绍,精确搜索相对而言比较简单。

精确搜索

这个搜索的类型其实比较简单,我们根据用户的输入,也就是用户想要搜索的内存区域进行遍历,我这里从0开始,先获取页面属性,当页面属性可读可写的时候我们就进行字节的判断,这里用户还会告诉我们是4字节还是字节或者是浮点,由于我们Read是一个Buffer,所以我们强制要转换一下,比如四字节,我这里用的是int,因为我发现无符号的时候差别有点特别的大(负数有点多),先取byte的地址然后转换为int指针,最后取这个指针的值就实现了byte强转了,然后进行对比,我这里用的是链表的保存形式,因为我们在之后要进行特别多的删除查询操作,数组实在是难以处理,而且很难和之后的模糊搜索进行衔接。

做个简单的演示,这里我提供两种附加的选项,一种是选择硬盘的文件,然后打开,另一种是附加进程,对于游戏的话呢一般选择后者,对于破解的小程序来说一般选择前者。

我们附加完毕游戏之后,就可以进行搜索了,我们比如想要修改阳光,我们可以首先搜索我们的阳光数:

然后可以看到有很多结果,然后我们改变一下,这个样子才能缩小范围,最后我们会锁定到这个数值:

这里也说一下,我在再次搜索中增加了这三个再次搜索类型:

4

看名字也就看明白了,也就是字面的意思。

模糊搜索

这个搜索类型,网上并没有相关的思路,所以我是自己写的,首先我们先看下具体的操作。

就拿冷却时间进行说明,首先我们先模糊搜索:

可以看到,有这么多结果,所以我并没有采取精确搜索的那种简单的链表存储方式,我下面说我具体怎么实现,先来具体的介绍一下这个效果。

我们回到游戏,让冷却改一下。

然后搜索改变的数值,会得到如下结果:

然后经过多次的搜索,这里需要一个小技巧,等他冷却完毕,搜索一次改变的数值之后,我们就要改为未改变的,反正大家理解这个就好,最后我们能够锁定这么一个很像的地址:

简单发现,他是每次冷却完毕变成0,然后我们中下之后,又从0增加,直到到一个上限,又变成了0,我们可以将他锁定到一个比较大的值,就可以实现无冷却。

好了,简单的操作了解之后,我介绍一下我实现的一个思路,用CE很久,所以我也很清楚模糊搜索结果有多大,所以如果我们还是和精确搜索一样那肯定是不可能的,因为你想,几千万的数据,当再次搜索的时候遍历几千万的数据通过ReadProcessMemory,这个的一个用户体验是十分差的,所以我这里是保存了这个内存页的整个内存,保存到一个链表中,然后在再次搜索的时候读链表中的值,进行每个比较:

我链表的结构:

typedef struct _MemoryLinkList

{

DWORD Address;

void * Value;//不要忘记删除链表的时候删除

int ValeSize;

UINT uFlag;

LPVOID NextMemory;

}MemoryLinkList,*pMemoryLinkList;

是这个样子保存的。在下次搜索的时候通过重新构造链表进行保存。这样会大大减少时间的消耗,但是需要注意的是释放链表的时候释放void*,那个是我new的。

反汇编:

这个目前我写了三个断点类型:INT3断点,内存断点(写入读取),硬件断点(写入读取运行)。

我来介绍一下具体的实现,还有就是内存断点为什么消耗很大(这个东西确实消耗很大,能用硬件最好,因为这个是最快的)。

内存断点

首先我们对这个阳关下内存写入断点,然后种植植物,程序会断下:

可以看到当前EIP在41BA76,进行了一个mov操作,发现esi的值就是我们的阳光值(已经减去当前消耗的值)

我们尝试修改汇编语句:

但是我们发现他会种不下去。因为它会导致下面的操作收到影响,这里我只是做演示。其实可以进行一个内敛的path,jmp到下面也是可以的,这里不演示了。

但是我们可以修改ESI的值(也可以通过这个5560偏移找到基地址)

双击寄存器修改:

一样可以达到修改阳光,注意这里是16进制显示的。

讲一下为什么内存断点很不建议,首先我们要知道为什么他会断下来,其实是因为我们将他这个地址对应的内存页面改变了页面属性:

PAGE_EXECUTE_READ

例如这个,这就是只可以执行和读取,所以我们进成读取这个页面的时候,会造成这个样子的一个异常:

EXCEPTION_ACCESS_VIOLATION

这个异常在随之对应的结构体中有我们异常操作的内存和EIP,问题就在这里,由于我们是修改了一个页面!所以有很大的可能是并不是我们想要改变的地址,有可能是这个页面其他的内存被读取了,所以我们不能断下来,这个时候怎么办呢?我这里是这么做的,在下一条EIP下INT3断点,然后修改我们的内存属性变回正常,然后到下一条EIP的时候修改回去我们的内存断点,不要觉得这也没什么啊,在我测试中,发现太频繁了,这个修改回去的操作基本上当我们的鼠标一进去窗口就开始了,1s就要有5,6个,所以这种来回的消耗是不太可取的。

我实现的代码:

BOOL DoMemoryBreakPoint( HANDLE hProcess, DWORD MemoryAdd, DWORD bFlag) 

//bFlag将会影响全局标志变量的值,`PAGE_EXECUTE_READ`内存写入断点

{

if(MemoryPoint != 0)

CancleMemoryBreakPoint(hProcess,MemoryAdd);

dwMemoryPointFlag = bFlag;

MemoryPoint = MemoryAdd;

return *VirtualProtectEx* (hProcess,( *LPVOID* )MemoryPoint,4,bFlag,&oldMemorySectionPro);

}

我现在只允许一个内存的断点,如果太多的话呢实在是太卡了

INT3断点

这个断点在OD中是最常见的,其实就是修改下断地址的第一个字节,改为CC,然后程序运行的时候会出现这个样子的一个异常:

EXCEPTION_BREAKPOINT

这个异常中,我们需要做的就是判断是不是第一次出现,不是第一次出现的话呢我们就断下,让用户操作,然后等待用户是否运行(运行,单步步过,单步步入)。

这里也说一下步过和步入,这里步过我是在下一个EIP下INT3断点,步入的话呢我将Eflags的第八位置1,然后在第二次异常的时候置入,所以有的时候步过会跑飞,而步入基本不会出现。

如果发现是第一次异常我们先修改一下CC,改为我们的内容,这个有需要一个链表,用来存储用户下的INT3断点。

我实现的代码:

BOOL DoBreakPoint( HANDLE hProcess, DWORD StartAdd, BOOL bFlag)

//标志TRUE代表是不是内存临时的断点

{

char buf[24];

memset (buf,0,sizeof(buf));

DWORD dwRead;

ReadProcessMemory (hProcess,( LPCVOID )StartAdd,( LPVOID )buf,sizeof(buf),&dwRead);

Disasm_Data.EIP = (UIntPtr)buf;

pMemoryInstruction tmpStruct = (pMemoryInstruction) malloc (sizeof(MemoryInstruction));

tmpStruct->dwAddr = StartAdd;

tmpStruct->len = Disasm_Func(&Disasm_Data);//赋值这个语句的长度

tmpStruct->FirstByte = buf[0];

tmpStruct->bFlag = bFlag;

tmpStruct->pNext = NULL;

if (g_FirstMemory == NULL )

{

g_FirstMemory = (pMemoryInstruction) malloc (sizeof(MemoryInstruction));//111

g_FirstMemory->pNext = tmpStruct;

g_FirstMemory->dwAddr = -1;

g_EndMemory = tmpStruct;

}else

{

g_EndMemory->pNext = tmpStruct;

g_EndMemory = tmpStruct;

}

Int3BreakPointCount++;

g_EndMemory->pNext = NULL ;//必须要有

byte btmp = 0xcc;

return WriteProcessMemory (hProcess,( LPVOID )StartAdd,&btmp,1, NULL );

}

硬件断点

这个断点是我非常建议使用的,而且读写和执行是都支持的,具体原理是CPU有个调试寄存器,Dr0-Dr3,Dr6和Dr7,为什么说只有4个呢,其实就是因为Dr0-Dr3的值是我们需要下段的地址,而Dr7是配置这个断点,Dr6是结果。

他会触发: EXCEPTION_SINGLE_STEP

这里我不太想说太多,因为网上很多,而且不难。

我这里分享一下我实现的代码:

int SetHardWareBreakPointDr(int OldValue,int which)

{

int SetValue = OldValue;

setbit(SetValue,10);//初始这个位必须为1

setbit(SetValue,which*2);

return SetValue;

}

int SetHardWareBreakPointStyle(int OldValue,int which/*0-3*/,int BreakStyle,int LenStyle)

{

int SetValue = OldValue;

setbit(SetValue,10);//初始这个位必须为1

switch(BreakStyle)

{

case Hard_Excue:

clrbit(SetValue,(16+which*4));

clrbit(SetValue,(17+which*4));

setbit(SetValue,8);//这个位置是精确

break;

case Hard_Read:

setbit(SetValue,(16+which*4));

setbit(SetValue,(17+which*4));

break;

case Hard_Write:

setbit(SetValue,(16+which*4));

clrbit(SetValue,(17+which*4));

break;

}

switch(LenStyle)

{

case Hard_OneByte:

clrbit(SetValue,(18+which*4));

clrbit(SetValue,(19+which*4));

break;

case Hard_TwoByte:

setbit(SetValue,(18+which*4));

clrbit(SetValue,(19+which*4));

break;

case Hard_FourByte:

setbit(SetValue,(18+which*4));

setbit(SetValue,(19+which*4));

break;

}

return SetValue;

}

我用了枚举。

这里我贴一下我的,太具体的,等我把这个程序写的完全一点之后会开源的。

单步步过:

我实现的原理是在下一条语句下INT3然后执行的时候改回去。

单步步入:

就是修改EFlags的第8位,然后等待 EXCEPTION_SINGLE_STEP

响应就好了。

其实程序写起来没有说的这么轻描淡写,还是有一定难度的,尤其是模糊搜索衔接再次扫描,还有那些链表的操作(数据结构没好好学,只能再下功夫了)。

之后有时间的话呢我是会更新的,例DLL的操作,内存创建写Call,HOOKAPI,D3D钩子,抓TCP UDP包之类的,会慢慢写大的。目前由于我还在上大二(学计算机两年了,嘻嘻),最近还在写Java安卓项目所以程序不一定会在近期写了,而且我还是玩渗透多一些,所以不会太快更新了,等我觉得写的差不多了,我会在GitHub开源的,可以关注我的博客。

我也知道IDA,OD,X64,CE很强,但是我希望自己写一个,这样之后我可以根据自己的需要去对某种游戏和壳进行定制。

下载地址:链接:https://pan.baidu.com/s/1cSZ9VxJOLbMcEalBDP68Gw

提取码:4zq7

6 个赞

这个格式怎么有点乱,管理员稍微给改一下呗 :nerd_face:

1 个赞

已改