// querySystemInfomation.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"
#include "malloc.h"

/*发一篇学习心得,本人很菜,自学编程 ,学习点东西都很费力,希望大牛不要笑我
/最近学到内核编程遇到了很多直接强类型缓存区,譬如从SSDT中定位api,ZwQuerySystemInformation 等....
以前学asp.net没考虑过这些,当遇到这些问题时候,思路还是很模糊的,这些天,不断的遇到这样的操作,于是我就
写了下面这个练习,证明,在内存中数据的存取是靠基址 + 偏移,有了基地之 就可以通过偏移改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点了,睡觉。