Wker_EXEDebug
最近没什么特别多的事情,渗透也玩的有点疲惫了,应该换点东西玩玩了,然后我就想起来了之前有点想写一个逆向的工具,所以我就开始了。
安卓我玩的还是不是很溜,所以我目前只做了Windows版本的逆向工具,目前的话呢我只做了两个页面。
-
内存搜索
-
反汇编
目前只做了这两个版块,类似于DLL查询注入,页面之类的我还没写,我也会通过这款工具分享一下三个断点具体的实现方法,因为看到网络上面只有很粗糙的介绍,这里我会详细介绍的。
首先:我在内存首次搜索页面做了两种搜索,精确搜索和模糊搜索。
简单的浏览下互联网,网上没有关于模糊搜索的介绍,只有精确搜索的介绍,精确搜索相对而言比较简单。
精确搜索
这个搜索的类型其实比较简单,我们根据用户的输入,也就是用户想要搜索的内存区域进行遍历,我这里从0开始,先获取页面属性,当页面属性可读可写的时候我们就进行字节的判断,这里用户还会告诉我们是4字节还是字节或者是浮点,由于我们Read是一个Buffer,所以我们强制要转换一下,比如四字节,我这里用的是int,因为我发现无符号的时候差别有点特别的大(负数有点多),先取byte的地址然后转换为int指针,最后取这个指针的值就实现了byte强转了,然后进行对比,我这里用的是链表的保存形式,因为我们在之后要进行特别多的删除查询操作,数组实在是难以处理,而且很难和之后的模糊搜索进行衔接。
做个简单的演示,这里我提供两种附加的选项,一种是选择硬盘的文件,然后打开,另一种是附加进程,对于游戏的话呢一般选择后者,对于破解的小程序来说一般选择前者。
我们附加完毕游戏之后,就可以进行搜索了,我们比如想要修改阳光,我们可以首先搜索我们的阳光数:
然后可以看到有很多结果,然后我们改变一下,这个样子才能缩小范围,最后我们会锁定到这个数值:
这里也说一下,我在再次搜索中增加了这三个再次搜索类型:
看名字也就看明白了,也就是字面的意思。
模糊搜索
这个搜索类型,网上并没有相关的思路,所以我是自己写的,首先我们先看下具体的操作。
就拿冷却时间进行说明,首先我们先模糊搜索:
可以看到,有这么多结果,所以我并没有采取精确搜索的那种简单的链表存储方式,我下面说我具体怎么实现,先来具体的介绍一下这个效果。
我们回到游戏,让冷却改一下。
然后搜索改变的数值,会得到如下结果:
然后经过多次的搜索,这里需要一个小技巧,等他冷却完毕,搜索一次改变的数值之后,我们就要改为未改变的,反正大家理解这个就好,最后我们能够锁定这么一个很像的地址:
简单发现,他是每次冷却完毕变成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