【原创】学习笔记--函数在内存中的定位
// querySystemInfomation.cpp : 定义控制台应用程序的入口点。//
#include "stdafx.h"
#include "windows.h"
#include "malloc.h"
/*发一篇学习心得,本人很菜,自学编程 ,学习点东西都很费力,希望大牛不要笑我
/最近学到内核编程遇到了很多直接强类型缓存区,譬如从SSDT中定位api,ZwQuerySystemInformation 等....
以前学没考虑过这些,当遇到这些问题时候,思路还是很模糊的,这些天,不断的遇到这样的操作,于是我就
写了下面这个练习,证明,在内存中数据的存取是靠基址 + 偏移,有了基地之 就可以通过偏移改PE文件,idt,ssdt等等操作
*/
typedef enum _class
{
P_INFORMATION=1, //SYSTEM_PROCESS_INFORMATION
M_INFORMATION, //SYSTEM_MODULE_INFORMATION
T_INFORMATION //根据这个标志等下我们随意的一个偏移处写个函数放进去
}INFO_CLASS;
//进程结构
typedef struct _SYSTEM_PRCESS_INFORMATION
{
UINT pid;
char name[8];
}SYSTEM_PROCESS_INFORMATION,*PSYSTEM_PROCESS_INFORMATION;
//模块结构
typedef struct _SYSTEM_MODULE_INFORMATION
{
DWORD addr;
char name[8];
int flag;
}SYSTEM_MODULE_INFORMATION,*PSYSTEM_MODULE_INFORMATION;
//假设这个函数在ssdt中 偏移为16 也就是 index = 4 ===>baseAddress + index * 4 得到这个 函数地址
int add(int a,int b)
{
return a+b;
}
#pragma pack(4)
PVOID QueryInfo(INFO_CLASS cls,PVOID buffer)
{
//初始化,取一些信息,放在内存中,供查询使用
UINT pid=1234; //取进程id
DWORD pName = 0x4241 ;//取进程名字:ab --这里注意下字节序
DWORD mName = 0x4443 ; //取模块名字:cd
int flag = 888; //取模块标志
DWORD addr = 0xff; //取模块地址
PVOID functionAddr = (PVOID)add; //取函数地址
//下面我不用变量写入,直接用汇编写内存,避免变量概念的干扰
switch(cls)
{
case P_INFORMATION:
_asm
{
//这里写进程id 和进程名 pName
mov eax,buffer
mov ebx,pid
mov [eax],ebx
mov ebx,pName
mov [eax+4],ebx
}
break;
case M_INFORMATION:
_asm
{
//这里写模块地址addr,模块名字mName,模块标志flag
mov eax,buffer
mov ebx,addr
mov [eax],ebx
mov ebx,mName
mov [eax+4],ebx
mov ebx,flag
mov [eax+12],ebx
}
break;
case T_INFORMATION:
_asm
{
//我们把Add函数存在ssdt中,偏移16
mov eax,buffer
mov ebx,functionAddr
mov [eax+16],ebx
}
break;
}
return buffer;
}
#pragma pack()
int _tmain(int argc, _TCHAR* argv[])
{
PVOID buffer = malloc(36);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//读取一下进程信息
PVOID baseAddress = QueryInfo(P_INFORMATION,buffer);
if(baseAddress == NULL)
{
goto error;
}
PSYSTEM_PROCESS_INFORMATION pMessage = (PSYSTEM_PROCESS_INFORMATION)baseAddress;
printf("|查询进程信息pid:%d -- pName:%s\n|",pMessage->pid,pMessage->name);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//读取一下模块信息
baseAddress = QueryInfo(M_INFORMATION,buffer);
if(baseAddress == NULL)
{
goto error;
}
PSYSTEM_MODULE_INFORMATION mMessage = (PSYSTEM_MODULE_INFORMATION)baseAddress;
printf("|查询模块信息addr:0x%x -- mName:%s\--flag:%d\n|",mMessage->addr,mMessage->name,mMessage->flag);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//读取下偏移为16那里的函数
typedef int (*funs)(int a,int b);
funs add;
baseAddress = QueryInfo(T_INFORMATION,buffer);
if(baseAddress == NULL)
{
goto error;
}
PVOID fMessage = baseAddress;
_asm
{
mov eax,fMessage
mov eax,[eax+16]
mov add,eax
}
int c = add(50,20);
printf("add函数结果:%d\n",c);
//譬如说SSDT中偏移为16的地方存了一个函数入口地址 add(0x45645678)
//现在我们就可以typedef void (*funs)()
//funs f = (fun)[baseAddress+16]
//f();
//或者修改函数地址,这里要注意内存写保护的问题,和大小问题
//(*(ULONG *)(baseAddress+16)) = myfunc;
delete buffer;
return 0;
error:
printf("读取内存失败\n");
delete buffer;
return -1;
}
//系统的那个ZwQuerySystemInformation肯定比这个复杂很多,但是我感觉原理该差不多了,
//写完之后,感觉自己更加清晰了,可以很轻松的用KeServiecDescriptorTable + index *4 等去定位一些东西了.
//希望对那些和我一样菜的,还不是很明白的人能有所帮助,以后遇到譬如KeServiecDescriptorTable + index*4 ,ZwQuerySystemInformation 这样的东西不感到恐惧.
//2点了,睡觉。