首页
社区
课程
招聘
[原创][屠夫科普]R3枚举目标进程加载模块[C++标准版][Part.1][Part.2]
发表于: 2010-11-19 06:16 31543

[原创][屠夫科普]R3枚举目标进程加载模块[C++标准版][Part.1][Part.2]

2010-11-19 06:16
31543
收藏
免费 7
支持
分享
最新回复 (35)
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
既然对技术那么有热情,顺便也学学怎么写技术文档吧
2010-11-19 08:24
0
雪    币: 2134
活跃值: (14)
能力值: (RANK:170 )
在线值:
发帖
回帖
粉丝
3
关注一下,帖子思路说的浅显易懂,继续将序列进行下去。
不过楼主可以把部分措辞书面化一些,少加些'~',滴之类,看得晕,建议楼主编辑帖子
另外比如B&G之类的还是说全了比较好,google或者baidu,这样读者可以很容易看懂,不费力
2010-11-19 08:52
0
雪    币: 107
活跃值: (172)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
支持一下……
2010-11-19 10:56
0
雪    币: 474
活跃值: (96)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
楼主写文章挺风趣的,支持一下!
2010-11-19 11:20
0
雪    币: 132
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
感谢领导和大牛们的支持,因为是处女下海第一次发这种帖,没什么经验。

我会按照领导的指示认真修改的。争取质量越来越好
2010-11-19 13:42
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
不错不错,通俗易懂,希望看到后续的内容!
2010-11-20 11:46
0
雪    币: 132
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
【目录】
Part.1:R3枚举目标进程加载模块
Part.2:R3通过IAT修改HOOK目标进程API
Part.3:R3环境HOOK目标进程D3D输入接口
Part.4:R0恢复SSDT HOOK
Part.5:R0通过进程名称枚举系统进程得到PID
Part.6:R0通过内存搜索硬编码得到内核未导出函数
Part.7:R0通过拦截EIP实现调用R3进程内部函数


大家好,我们又见面啦,今天我将为各位讲述一个新故事,那就是IAT HOOK。再观看这个故事之前,需要观众确定具备两个基本能力:
1.对简单的数据结构在内存中的样子能有个宏观的理解。
2.理解运行在windows环境程序的工作原理。

导入地址表(IAT):Import Address Table 由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL 中.当PE 文件被装入内存的时候,Windows 装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成.其中导入地址表就指示函数实际地址。

比如我们想对目标程序的PeekMessage这个API函数进行HOOK,那么只需要找到他的IAT表,并把这个API的实际地址修改,这就完成了一个API HOOK。按常理,想执行此过程需要对PE格式有一定的理解,但是我认为并没有这个必要,毕竟这是一个很简单的工作。接下来我将用我的方法带领大家来实现它,那就是角色扮演。

首先幻想自己是30年代,蓝衣社的一名特务。今天接到上级的任务,去某地杀掉一名为敌对势力工作的人,然后经过易容,伪装成他来执行潜伏。那么目标是谁呢?他又住在哪里呢?拿起MSDN情报科送来的文件看看吧,目标叫PeekMessage,是姓“USER32.dll”家族的成员,它住在:

xxx.exe市
IMAGE_DOS_HEADER区
IMAGE_OPTIONAL_HEADER街
IMAGE_IMPORT_DESCRIPTOR小区
还有一张目标的相片
BOOL PeekMessage
(LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
)


资料就这么多啦,但是并没有说明目标住在几号楼几层几号啊。不过没有关系我们是特务嘛。这难不倒我们的,Let's begin
坐着蓝衣社为我们准备的专机SetWindowsHookEx把我们(DLL)注入到xxx.exe市,哈哈~下了飞机来到一个陌生的城市真是俩眼一摸黑呀,怎么找我们的目的地呢?最简单的办法:打车,于是我们拦了一辆GetModuleHandle(NULL)牌的出租车,谁知道一上车。司机看到我给他的地址后告诉我要去的地方需要过海,没办法直接到,只能先送我到IMAGE_DOS_HEADER区,然后再换船过海才行。听得我晕头转向的,没办法,走吧。。。闲来打量一下这个城市,满眼尽是一些由0和1搭建黑白2色的高楼大厦,颜色和款式是多么的单调枯燥哇,算了,还是闭目养神吧。不一会我们来到了IMAGE_DOS_HEADER区的水路码头,该下车换乘船过海啦。于是买了(ModuleAddress+ImageDosHearderPointer->e_lfanew+24)号渡轮的船票,继续这次无聊的旅程。晕船从头吐到尾,好不容易熬到了地方,原来一出了码头就来到了IMAGE_OPTIONAL_HEADER街,抬头一望在我们12点钟方向,有个小巴站台,走过去这么一看才知道原来坐
ModuleAddress+ImageOptionalHeaderPointer->DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
线小巴,可以直达我们的终点:IMAGE_IMPORT_DESCRIPTOR小区。二话不说,走着您嘚~~~~~马上要到目的地啦。趁坐小巴的这段时间我们先来整理下思绪,因为不知道具体的楼号和门牌号,所以我们要先想个什么办法,于是找同车的一位美丽小姐打听得知,IMAGE_IMPORT_DESCRIPTOR小区的每座楼都是同一样式的,那就是ImageImportDescriptorPointer->FirstThunk风格,又知道一楼大厅有个收信箱,上面登记着住户的姓名。有了这些线索我们就下车一栋楼,一栋楼的找吧,虽然这个方案毫无创意,但是记得有位大师曾经说过:往往通过复杂的数据结构和华丽的算法并不是解决问题的好方法,因为它加大了维护和调试的难度。所以我们就用这种直接而有效的土办法来老老实实的找吧:先逐楼搜索
while(ImageImportDescriptorPointer->FirstThunk!=0),然后找到一楼大厅的收信箱
TargetName=(LPCTSTR)((DWORD)ModuleAddress+ImageImportDescriptorPointer->Name),再逐户的查找姓“USER32.dll”的家族
if(TargetName.Compare(_T("USER32.dll"))==0),没过多久我们终于找到了,登记簿上写明了具体的门牌号,哈哈~这表示我们离成功只有一步之遥,于是迫不及待的走进ModuleAddress+ImageImportDescriptorPointer->FirstThunk号电梯,走向“USER32.dll”家,来到门口,我停住啦,静静地站在那里平复一下心绪,10秒后我飞起一脚踹开了大门走了进去,映入眼帘的是:坐在屋里男女老少,大大小小10几口子,全部一脸惊愕的盯着我这位不速之客。我拿出相片while(ImageThunkDataPointer->u1.Function)迅速搜索,
FunctionAddress=(PDWORD)&(ImageThunkDataPointer->u1.Function)定位目标,
if(*FunctionAddress==(DWORD)PeekMessageAddress)经过一轮的搜索,我找到了目标,他正傻傻的坐在角落里的电脑桌前,我走到它面前
VirtualQuery(FunctionAddress, &InforMation, sizeof(InforMation))细细的打量着他:凌乱的头发如同杂草,一架高度数眼镜戴在苍白消瘦的脸颊上,驼背,纤细却小腹异常隆起的身材,套着10几年前流行的服饰,HOHO~做技术典型的形象,我冷漠的从怀中掏出了枪:
VirtualProtect(FunctionAddress, sizeof(DWORD),PAGE_READWRITE,&BeforeProtect)顶在目标的眉心。此时此刻,在场的所有人都清楚了我的来意。死亡的恐惧笼罩在每个人的心头,我不在多做停留,无情的勾动了扳机,这就是一个特务的专业操守。嘿嘿
::WriteProcessMemory((HANDLE)-1,FunctionAddress,&FunctionOfSelf,sizeof(DWORD), NULL)目标应声倒地。在手枪的淫威下,其他人也只好无奈的接受这痛苦的事实,我也就成功的完成了此次任务。开始潜伏。

接下来换种语言再来讲述一下这个故事:C++

#include <winternl.h>
#include "DialogMain.h"

typedef BOOL (WINAPI* PEEKMESSAGE)(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);

PEEKMESSAGE FakePeekMessage=(PEEKMESSAGE)PeekMessage;
BOOL WINAPI MinePeekMessage(LPMSG lpMsg,HWND hWnd, UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg)
{
        AfxMessageBox(_T("你TM是不是调用我啦?"));
        return ((PEEKMESSAGE)FakePeekMessage)(lpMsg,hWnd,wMsgFilterMin,wMsgFilterMax,wRemoveMsg);
}

BOOL CDialogMain::ImportAddressTableHook(HMODULE ModuleAddress,LPCTSTR Library,LPCVOID TargetAddress,LPCVOID ReplaceAddress)
{
        IMAGE_DOS_HEADER* ImageDosHearderPointer=NULL;
        IMAGE_OPTIONAL_HEADER* ImageOptionalHeaderPointer=NULL;       
        IMAGE_IMPORT_DESCRIPTOR* ImageImportDescriptorPointer=NULL;
        IMAGE_THUNK_DATA* ImageThunkDataPointer=NULL;
        CString TargetName;
        DWORD Value=0;
        LPDWORD FunctionAddress=NULL;
        MEMORY_BASIC_INFORMATION InforMation;
        DWORD BeforeProtect=0;

        ImageDosHearderPointer=(IMAGE_DOS_HEADER*)ModuleAddress;
        ImageOptionalHeaderPointer=(IMAGE_OPTIONAL_HEADER*)((DWORD)ModuleAddress+ImageDosHearderPointer->e_lfanew+24);
        ImageImportDescriptorPointer=(IMAGE_IMPORT_DESCRIPTOR*)
                                                                 ((DWORD)ModuleAddress+ImageOptionalHeaderPointer->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
        while(ImageImportDescriptorPointer->FirstThunk!=0)   
        {   
                TargetName=(LPCTSTR)((DWORD)ModuleAddress+ImageImportDescriptorPointer->Name);
                if(TargetName.Compare(Library)==0)
                {   
                        Value=(DWORD)ModuleAddress+ImageImportDescriptorPointer->FirstThunk;
                        break;
                }
                ImageImportDescriptorPointer++;   
        }  
        if (Value==0)
        {
                AfxMessageBox(_T("获取导入地址表失败!"));
                return FALSE;
        }
        else
        {
                ImageThunkDataPointer=(IMAGE_THUNK_DATA*)Value;
                while(ImageThunkDataPointer->u1.Function)   
                {   
                        FunctionAddress=(LPDWORD)&(ImageThunkDataPointer->u1.Function);

                        if(*FunctionAddress==(DWORD)TargetAddress)
                        {   
                                DebugInfo.Format(_T("%x"),*FunctionAddress);
                                EditHeroBlood.SetWindowText(DebugInfo);
                                VirtualQuery(FunctionAddress,&InforMation,sizeof(InforMation));   
                                VirtualProtect(FunctionAddress, sizeof(DWORD),PAGE_READWRITE,&BeforeProtect);   
                                if (::WriteProcessMemory((HANDLE)-1,FunctionAddress,&ReplaceAddress,sizeof(DWORD),NULL)==FALSE)
                                {
                                        AfxMessageBox(_T("修改导入地址表失败!"));
                                        return FALSE;
                                }
                                else
                                {
                                        VirtualProtect(FunctionAddress,sizeof(DWORD),BeforeProtect,0);   
                                        return TRUE;
                                }
                        }   
                        ImageThunkDataPointer++;   
                }
        }
        return FALSE;
}

我再换一种语言来讲述:

大家都知道其实我上述我说地名都是一些结构体,而Windows就是靠无数个这样的结构体连接搭建起来的,你中有我,我中有你,这就好比一个生物由无数个细胞组成的一样。因为今天我们不是讲述windows体系,所以我也不做过多解释。

那么通过IAT修改来实现API HOOK的实现原理是什么呢?关键就在于如何在茫茫的内存中找到我们的目标,听起来好像很恐怖,像大海捞针,其实没那么难,因为windows就是通过那些结构体把数据在内存中全部线形连接起来啦。这就像玩大富翁,或者一种挖宝藏游戏,从起点出发走几步就会得到一个提示,告诉我们下一步该怎么走。现在我就带领大家来玩一次这种游戏

先来说明游戏规则
WORD=2步
DWORD=4步
指针=进入下一房间
数组=翻卡片得到答案
RVA=地图宝箱

IMAGE_DOS_HEADER STRUCT
{
这里就是我们的起点,可以通过GetModuleHandle(NULL)获得,比如起点地址是0x00000000,我们得到的第一个提示
(ModuleAddress+ImageDosHearderPointer->e_lfanew+24),怎么理解这个提示呢?就是从起点开始走36步,找到一个叫e_lfanew的门(因为它是指针,所以他就是通往下一个房间的大门),然后推门进去后再走24步,我们就找到了第二个将要给我们提示的地方

e_magic           WORD
e_cblp            WORD
e_cp              WORD
e_crlc            WORD
e_cparhdr         WORD
e_minalloc        WORD
e_maxalloc        WORD
e_ss              WORD
e_sp              WORD
e_csum            WORD
e_ip              WORD
e_cs              WORD
e_lfarlc          WORD
e_ovno            WORD
e_res             WORD
e_oemid           WORD
e_oeminfo         WORD
e_res2            WORD
e_lfanew          DWORD

}
IMAGE_DOS_HEADER ENDS

typedef struct _IMAGE_OPTIONAL_HEADER
{  
我们通过上一个提示来到了这个房间,同样我们得到了又一个提示
ModuleAddress+ImageOptionalHeaderPointer->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
理解下这个提示。这代表我们从起点走到e_lfanew大门进入这个房间再走96步找到DataDirectory开始翻卡片(他是一个结构体形数组),来找答案,而答案就在第IMAGE_DIRECTORY_ENTRY_IMPORT(表示1,也就是DataDirectory[1])张卡片内

WORD Magic;  
BYTE MajorLinkerVersion;  
BYTE MinorLinkerVersion;  
DWORD SizeOfCode;
DWORD SizeOfInitializedData;  
DWORD SizeOfUninitializedData;  
DWORD AddressOfEntryPoint;  
DWORD BaseOfCode;  
DWORD BaseOfData;  
DWORD ImageBase;  
DWORD SectionAlignment;  
DWORD FileAlignment;  
WORD MajorOperatingSystemVersion;  
WORD MinorOperatingSystemVersion;  
WORD MajorImageVersion;  
WORD MinorImageVersion;  
WORD MajorSubsystemVersion;  
WORD MinorSubsystemVersion;  
DWORD Win32VersionValue;  
DWORD SizeOfImage;  
DWORD SizeOfHeaders;  
DWORD CheckSum;  
WORD Subsystem;  
WORD DllCharacteristics;  
DWORD SizeOfStackReserve;  
DWORD SizeOfStackCommit;  
DWORD SizeOfHeapReserve;  
DWORD SizeOfHeapCommit;  
DWORD LoaderFlags;  
DWORD NumberOfRvaAndSizes;  
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
}
IMAGE_OPTIONAL_HEADER,

IMAGE_IMPORT_DESCRIPTOR STRUCT
{
通过上面的提示,我们来到了这个房间,那么下一个提示是什么呢?那就是while(ImageImportDescriptorPointer->FirstThunk!=0),再来理解下这个提示。它是说让我们找到FirstThunk这个地图宝箱(RAV)在里面找到我们需要的地图才能找到下一个房间,那我们就一张一张的翻看吧
我们要找的地图名就是“USER32.dll”
  union
    Characteristics
    OriginalFirstThunk
  ends
  TimeDateStamp
  ForwarderChain
  Name1
  FirstThunk
}
IMAGE_IMPORT_DESCRIPTOR ENDS

有了地图我们就来到了下一个房间
IMAGE_THUNK_DATA32 STRUCT
哈哈。这就使传说中的IAT表啦,就差一步就找到宝藏啦。我们看一下这次的提示while(ImageThunkDataPointer->u1.Function),它是说让后找到u1.Function这个卡片商来得到宝藏位置的答案,只是这次我们不知道第几张卡片里有我们要的答案。没办法挨个翻吧。指导找到
PeekMessage
    union u1
        ForwarderString
        Function
        Ordinal
        AddressOfData
    ends
IMAGE_THUNK_DATA32 ENDS

找到后,还用我教吗?关闭他的保护属性,然后改写他的地址吧。。。

【小结】

这里我钩了一个目标程序接收消息的函数PeekMessage,然后把它传进来的参数输出出来。看看他会不会自己给自己发消息。HOHO。。挺无聊的
但是如果你想钩点别的。比如目标程序的socket,哈哈~那你觉得这像什么呢?[/
SIZE]
2010-11-21 22:14
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
lz还有吗,写的真好。比其他的地方干巴巴的讲理论好多了。很容易懂
2010-11-22 00:15
0
雪    币: 248
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
好强的功底...膜拜
2010-11-22 00:22
0
雪    币: 397
活跃值: (397)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
11
有一种混搭的风范,赞
2010-11-22 10:58
0
雪    币: 2105
活跃值: (424)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
可以去写小说了 文笔超强  膜拜
2010-11-22 11:37
0
雪    币: 56
活跃值: (242)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
作者好厉害,学习了
2010-11-22 11:56
0
雪    币: 78
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
以后还是写小说吧,别再搞编程了,可惜人才了。 
2010-11-22 12:09
0
雪    币: 1259
活跃值: (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
stu
15
楼主很用心,非常感谢。
2010-11-22 12:23
0
雪    币: 78
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
没贴必看,写的好!
2010-11-22 12:52
0
雪    币: 132
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
为什么要合我帖子呢?
2010-11-23 21:20
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
仔仔细细的研究一下
2010-11-25 10:45
0
雪    币: 142
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
.   马了个可的
2010-11-25 17:43
0
雪    币: 85
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
我其实真的不想回贴,加贴就占用了楼主继续发贴的位置~!!!好期待楼主继续
2010-12-4 16:02
0
雪    币: 6092
活跃值: (744)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
21
mark一下 占领一个位置
2010-12-13 00:24
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
工整,顶一个了再看。
2010-12-13 00:36
0
雪    币: 10
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
楼主发文水平越来越好啦,支持一个
2010-12-13 23:02
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
楼主精神可嘉
2010-12-15 05:23
0
雪    币: 3116
活跃值: (1269)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
25
剧情吸引我。日了
2010-12-15 13:30
0
游客
登录 | 注册 方可回帖
返回
//