能力值:
( LV12,RANK:300 )
|
-
-
3 楼
该循环通过搜索PsLookupProcessByProcessId函数的代码,找到其中使用PspCidTable的那行语句,从而得到PspCidTable的位置。
想要看明白这段代码,首先要了解PsLookupProcessByProcessId的函数代码内容:
lkd> uf PsLookupProcessByProcessId
nt!PsLookupProcessByProcessId:
805d40ea 8bff mov edi,edi
805d40ec 55 push ebp
805d40ed 8bec mov ebp,esp
805d40ef 53 push ebx
805d40f0 56 push esi
805d40f1 64a124010000 mov eax,dword ptr fs:[00000124h]
805d40f7 ff7508 push dword ptr [ebp+8]
805d40fa 8bf0 mov esi,eax
805d40fc ff8ed4000000 dec dword ptr [esi+0D4h]
805d4102 ff35c0495680 push dword ptr [nt!PspCidTable (805649c0)]
805d4108 e877ad0300 call nt!ExMapHandleToPointer (8060ee84)
从以上内容可见,只要从PsLookupProcessByProcessId函数的开头开始搜索,找到以下代码
805d4102 ff35c0495680 push dword ptr [nt!PspCidTable (805649c0)]
就可得到PspCidTable的位置。
具体来说是以这行代码开头的0x35FF和下一句代码开头的0xE8为判断标志,当
((*(PUSHORT)p==0x35ff)&&(*(p+6)==0xe8)
满足时,说明此时p正是这行代码的开头。
而此时p+2处保存的这个32位DWORD值,正是PspCidTable的位置。
因此有cid=*(PULONG)(p+2);
至于以分页大小PAGE_SIZE为范围,是指定一个搜索范围,以免因某些没有预测到的问题,导致搜索不到相应特征,而出现循环过久乃其访问到不可用内存而导致异常。
以上就是这个循环的原理,即利用导出函数中的特征码搜索来定位未导出的函数或内核结构。
|
能力值:
( LV2,RANK:10 )
|
-
-
4 楼
多谢楼上的帮助。还有两个不懂的地方想请教一下。一个是805d4102 ff35c0495680 push dword ptr [nt!PspCidTable (805649c0)]这句。此时(PUSHORT)p应该是ff35呀(我知道cpu存储数据的时候是倒着存的,存储指令的时候也是倒着存的吗?)。还有最后cid应该是32位的。但是用*(PULONG)(p+2)取得的是64位的呀
|
能力值:
( LV12,RANK:300 )
|
-
-
5 楼
[QUOTE=benmozhou;665737]多谢楼上的帮助。还有两个不懂的地方想请教一下。一个是805d4102 ff35c0495680 push dword ptr [nt!PspCidTable (805649c0)]这句。此时(PUSHORT)p应该是ff35呀(我知道cpu存储数据的时候是倒着存的,存储指令的时候也是倒着...[/QUOTE]
CPU存储数据是先存低位字节再存高位字节,而存储指令的时候不是倒着来的。
但是,当你要把这个指令的机器码当成数据的时候:
(PUSHORT)p把p转换成了USHORT的指针,因此*(PUSHORT)p就是把p看成USHORT指针时该处保存的USHORT的值,在内存中是FF 35,相应的USHORT值当然就是0x35FF
(PULONG)(p+2),PULONG就是ULONG型数据的指针,因此*(PULONG)(p+2)就是将(p+2)当成是一个指向ULONG的指针时,该处保存的ULONG值,而ULONG是32位无符号整数(哪有64位……)。
楼主还是得学好C的基础啊……
|
能力值:
( LV2,RANK:10 )
|
-
-
6 楼
呵呵,谢谢了。。。讲的很详细。顺便问个小问题,遍历PspCidTable获取进程时候,对于pid用了for(i=0x0;i<0x4e1c;i++)的循环。并且当pid属于0到0x800的时候在第一级句柄表中搜索,这个我懂,因为一张表最多容纳0x800个表项。然后在pid大于0x800时候就在第二张表中搜索,这儿我就有点不懂了。既然一张表中只能容纳0x800个表项,那么0x800到0x4e1c的范围是相当大的,第二张表怎么能容纳下呢?
|
能力值:
( LV12,RANK:300 )
|
-
-
7 楼
搜了一下相关代码,如http://bbs.pediy.com/showthread.php?t=59680中给出的,发现有些代码还真的是默认最多只有两张1级表,这当然是不对劲的,如果真的要到0x4e1c的话。
实际上gz1X的文章(http://hi.baidu.com/gz1x/blog/item/d99aeefa4d1c92ddb48f31b9.html)中以0x4e1c这个值作为上限是不对的,记得驱网有一帖子里曾经有人试过PID超过这个值的。
所以不偷懒也不会太累赘的作法,我想应该是:
1.使用HANDLE_TABLE结构中的NextHandleNeedingPool,这个值是说明在CID(PID)超过哪个值时就必须再加入一个新表,说明此时有效的CID(PID)值尚未超过这个值,因此使用这个值为上限就可以。
2.或者直接看看二级表中有多少个非0指针,有n个非0指针对应n个1级表,也就对应当前有效CID(PID)值的上限为n*0x800
上面两种方法得到的当前有效PID的上限值应该是一样的,而且只会比真正的当前最大CID(PID)值大而不会小。
|
能力值:
( LV12,RANK:210 )
|
-
-
8 楼
//======================================获取pspcidtable地址CODE1=======================
//利用PsLookupXX函数搜索特征码0x35ff和0xe8获取
ULONG GetAddrFromProcessId()
{
UNICODE_STRING pslookup;
PUCHAR addr; //单字节指针
PUCHAR p; //单字节指针
ULONG q; //四字节
RtlInitUnicodeString(&pslookup,L"PsLookupProcessByProcessId");
addr=(PUCHAR)MmGetSystemRoutineAddress(&pslookup);
KdPrint(("[PsLookupProcessByProcessId] addr:0x%x\n",addr));
for(p=addr;p<addr+PAGE_SIZE;p++)
{
if((*(PUSHORT)p==0x35ff)&&(*(p+6)==0xe8))
{
q=*(PULONG)(p+2);
KdPrint(("[GetAddrFromProcessId] pspcidtable:0x%x\n",q));
return q;
break;
}
}
return 0;
}
//======================================获取pspcidtable地址CODE2=======================
//利用KPCR取得
ULONG GetAddrFromKpcr()
{
ULONG pspcidtable;
//#define kpcr 0xffdff000
//kpcr+0x37 是KdVersionBlock
//KdVersionBlock+0x80是pspcidtable指针
pspcidtable=*((PULONG)((*(PULONG)(kpcr+0x34)) + (ULONG)(0x80)));
KdPrint(("[GetAddrFromKpcr] pspcidtable:0x%x\n",pspcidtable));
return pspcidtable;
}
|
能力值:
( LV12,RANK:210 )
|
-
-
9 楼
//======================================获取pspcidtable地址CODE1=======================
//利用PsLookupXX函数搜索特征码0x35ff和0xe8获取
ULONG GetAddrFromProcessId()
{
UNICODE_STRING pslookup;
PUCHAR addr; //单字节指针
PUCHAR p; //单字节指针
ULONG q; //四字节
RtlInitUnicodeString(&pslookup,L"PsLookupProcessByProcessId");
addr=(PUCHAR)MmGetSystemRoutineAddress(&pslookup);
KdPrint(("[PsLookupProcessByProcessId] addr:0x%x\n",addr));
for(p=addr;p<addr+PAGE_SIZE;p++)
{
if((*(PUSHORT)p==0x35ff)&&(*(p+6)==0xe8))
{
q=*(PULONG)(p+2);
KdPrint(("[GetAddrFromProcessId] pspcidtable:0x%x\n",q));
return q;
break;
}
}
return 0;
}
//======================================获取pspcidtable地址CODE2=======================
//利用KPCR取得
ULONG GetAddrFromKpcr()
{
ULONG pspcidtable;
//#define kpcr 0xffdff000
//kpcr+0x37 是KdVersionBlock
//KdVersionBlock+0x80是pspcidtable指针
pspcidtable=*((PULONG)((*(PULONG)(kpcr+0x34)) + (ULONG)(0x80)));
KdPrint(("[GetAddrFromKpcr] pspcidtable:0x%x\n",pspcidtable));
return pspcidtable;
}
//====================================获取pspciatable地址CODE3=========================
//KdEnableDebugger->KdInitSystem->KdDebuggerDataBlock->KDDEBUGGER_DATA32->PspCidTable
//======================================================================================
//====================================遍历pspcidtable CODE1=========================
//利用导出的ExEnumHandleTable(ntoskrnl.exe导出函数),可以直接操作,不需要根据GMM自己定位地址
//================================================================================
//====================================自己遍历pspcidtable表=========================
//=============通过当前进程获取进程对象指针(PsGetCurrentProcess函数获取object指针转换成object_header里面记录对象类型)===============
typedef struct _OBJECT_HEADER
{
union
{
struct
{
LONG PointerCount;
LONG HandleCount;
};
LIST_ENTRY Entry;
};
POBJECT_TYPE Type;
UCHAR NameInfoOffset;
UCHAR HandleInfoOffset;
UCHAR QuotaInfoOffset;
UCHAR Flags;
union
{
//POBJECT_CREATE_INFORMATION ObjectCreateInfo;
PVOID QuotaBlockCharged;
};
PSECURITY_DESCRIPTOR SecurityDescriptor;
QUAD Body;
} OBJECT_HEADER, *POBJECT_HEADER;
ULONG GetProcessType()
{
ULONG type;
ULONG objecttoeprocess;
objecttoeprocess=(ULONG)PsGetCurrentProcess();
//object转换成object_header完全可以object-0x18
objecttoeprocess=(ULONG)OBJECT_TO_OBJECT_HEADER(objecttoeprocess);
type=*(PULONG)(objecttoeprocess+TYPE);
KdPrint(("[GetProcessType] type:0x%x\n",type));
return type;
}
//===================================采用单链表记录进程Eprocess地址================================================
void RecordProcess(ULONG address) //address
{
PROCESSINFO *r;
if(head==NULL)
{
head=(PROCESSINFO *)ExAllocatePoolWithTag(NonPagedPool,sizeof(PROCESSINFO),MEM_TAG);//分配头指针
if(head==NULL)
{
KdPrint(("[RecoardProcess] Aloocate error\n"));
}
//分配内存,用完必须释放,否则内存泄漏
head->addr=0x0;
}
if(head->addr==0x0)
{
head->addr=address;
KdPrint(("[RecordProcess] head->addr:0x%x",head->addr));
p=head;
}
else
{
r=(PROCESSINFO *)ExAllocatePoolWithTag(NonPagedPool,sizeof(PROCESSINFO),MEM_TAG);
if(r==NULL)
{
KdPrint(("[RecoardProcess] Aloocate error\n"));
}
p->next=r;
p=r;
r->addr=address;
KdPrint(("[RecoardProcess] r->addr:0x%x\n",r->addr));
r->next=NULL;
}
}
//==================================根据PID找对象指针=====================================================
void GetPointerToObject(ULONG table,ULONG pid)//函数返回的object即指向EPROCESS的指针
{
ULONG object,objectheader;
ULONG NextFreeTableEntry;
ULONG processtype,type;
ULONG flags;
processtype=GetProcessType();//调用函数获取进程类型
if(MmIsAddressValid((PULONG)(table+pid*2)))
{
if(MmIsAddressValid((PULONG)(table+ pid*2 +NEXTFREETABLEENTRY)))
{
NextFreeTableEntry=*(PULONG)(table + pid*2 + NEXTFREETABLEENTRY);
if(NextFreeTableEntry==0)//正常的handle_table_entry中NextFreeTableEntry为0
{
object=*(PULONG)(table+pid*2);
object=((object | 0x80000000)& 0xfffffff8);//转换为对象指针
KdPrint(("[GetPointerToObject] object:0x%x\n",object)); //函数要记录的进程ERROCESS地址
objectheader=(ULONG)OBJECT_TO_OBJECT_HEADER(object);//获取对象头指针
KdPrint(("[GetPointerToObject] objectheader:0x%x\n",objectheader));
if(MmIsAddressValid((PULONG)(objectheader+TYPE)))
{
type=*(PULONG)(objectheader+TYPE);
if(type==processtype)//表明是进程对象
{
flags=*(PULONG)(object+FLAGS);//EPROCESS中Flags偏移量,指明了进程的死活
//KdPrint(("[GetPointerToObject]) flags:0x%x\n",flags)
if((flags&0xc)!=0xc)//死进程的flags最后一位为C
{
RecordProcess(object);
num=num+1; //进程数量
}
}
}
}
}
}
}
//==================================遍历pspcidtable列举进程信息============================================
void ListProcess()
{
ULONG pspcidtable;
ULONG tablecode;
ULONG table1,table2,table3,table4,table5; //2层表正常情况下足够存放进程了,所以3层表就不考虑
ULONG cid;
ULONG NextHandleNeedingPool;
pspcidtable=GetAddrFromKpcr(); //取pspcidtable地址
KdPrint(("[ListProcess]pspcidtable:0x%x\n",pspcidtable));
if(MmIsAddressValid((PULONG)pspcidtable))//地址是否有效
{
tablecode=*(PULONG)(*(PULONG)pspcidtable);//取tablecode地址
NextHandleNeedingPool=*(PULONG)(*(PULONG)pspcidtable+0x038);//NextHandleNeedingPool值
KdPrint(("[ListProcess] tablecode:0x%x\n",tablecode));
KdPrint(("[ListProcess] NextHandleNeedingPool:0x%0x\n",NextHandleNeedingPool)); //PID最大值
if((tablecode & 0x00000003)==0)//1层表存放的就是handle_table的基址
{
table1=tablecode;
KdPrint(("[ListProcess] table1:0x%0x\n",table1));
table2=0;
}
else if((tablecode & 0x00000003)==1)//2层表,一级表存放的是指向2级表的指针
{
tablecode=tablecode&0xfffffffe; //低二位清零
table1=*(PULONG)tablecode; //1个表存放0x800=2048大的PID,不够大
table2=*(PULONG)(tablecode+4);//2个表存放0x1000=4096大的PID,不够大
table3=*(PULONG)(tablecode+8);//3个表存放0x1800=6144大的PID,不够大
table4=*(PULONG)(tablecode+12);//4个表可以存放0x2000=8192大的PID,还不够大啊
table5=*(PULONG)(tablecode+16);//5个表可以存放0x2800=10240的的PID,足够了吧
KdPrint(("[ListProcess] table1:0x%x\n",table1));
KdPrint(("[ListProcess] table2:0x%x\n",table2));
KdPrint(("[ListProcess] table3:0x%x\n",table3));
KdPrint(("[ListProcess] table4:0x%x\n",table4));
KdPrint(("[ListProcess] table5:0x%x\n",table5));
}
//遍历
for(cid=0;cid<NextHandleNeedingPool;cid=cid+4)//要加4
{
if((table1!=0)&&(cid<=0x800))//在第一个表中
{
GetPointerToObject(table1,cid);
}
if((table2!=0)&&(0x800<cid && cid<=0x1000))//在第二个表中
{
cid=(ULONG)(cid-0x800);
//KdPrint(("[ListProcess] cid 0x%x\n",cid));
GetPointerToObject(table2,cid);
cid=(ULONG)(cid+0x800);
//KdPrint(("[ListProcess] cid 0x%x\n",cid));
}
if((table3!=0)&&(0x1000<cid && cid<=0x1800))//在第三个表中
{
cid=(ULONG)(cid-0x1000);
GetPointerToObject(table3,cid);
cid=(ULONG)(cid+0x1000);
}
if((table4!=0)&&(0x1800<cid && cid<=0x2000))//在第四个表中
{
cid=(ULONG)(cid-0x1800);
GetPointerToObject(table4,cid);
cid=(ULONG)(cid+0x1800);
}
if((table5!=0)&&(0x2000<cid && cid<=0x2800))//在第五个表中
{
cid=(ULONG)(cid-0x2000);
GetPointerToObject(table5,cid);
cid=(ULONG)(cid+0x2000);
}
}
}
}
//
//取进程全路径====================================================================
//原理Eprocess->sectionobject(0x138)->Segment(0x014)->ControlAera(0x000)->FilePointer(0x024)->(FileObject->FileName,FileObject->DeviceObject)
void GetProcessPath(ULONG eprocess,CHAR ProcessPath[256])
{
ULONG object;
PFILE_OBJECT FilePointer;
UNICODE_STRING path; //路径
UNICODE_STRING name; //盘符
ANSI_STRING string;
path.Length=0;
path.MaximumLength=256;
path.Buffer=(PWCHAR)ExAllocatePoolWithTag(NonPagedPool,256,MEM_TAG); //必须释放
if(MmIsAddressValid((PULONG)(eprocess+0x138)))//Eprocess->sectionobject(0x138)
{
object=(*(PULONG)(eprocess+0x138));
KdPrint(("[GetProcessFileName] sectionobject :0x%x\n",object));
if(MmIsAddressValid((PULONG)((ULONG)object+0x014)))
{
object=*(PULONG)((ULONG)object+0x014);
KdPrint(("[GetProcessFileName] Segment :0x%x\n",object));
if(MmIsAddressValid((PULONG)((ULONG)object+0x0)))
{
object=*(PULONG)((ULONG_PTR)object+0x0);
KdPrint(("[GetProcessFileName] ControlAera :0x%x\n",object));
if(MmIsAddressValid((PULONG)((ULONG)object+0x024)))
{
object=*(PULONG)((ULONG)object+0x024);
KdPrint(("[GetProcessFileName] FilePointer :0x%x\n",object));
}
else
return ;
}
else
return ;
}
else
return ;
}
else
return ;
FilePointer=(PFILE_OBJECT)object;
//KdPrint(("[GetProcessFileName] FilePointer :%wZ\n",&FilePointer->FileName));
ObReferenceObjectByPointer((PVOID)FilePointer,0,NULL,KernelMode);//引用计数+1,操作对象
RtlVolumeDeviceToDosName(FilePointer->DeviceObject,&name); //获取盘符名
//KdPrint(("[GetProcessFileName] FilePointer :%wZ\n",&name));
RtlCopyUnicodeString(&path,&name);//盘符连接
RtlAppendUnicodeStringToString(&path,&FilePointer->FileName);//路径连接
//KdPrint(("[GetProcessFileName] FilePointer :%wZ\n",&path));
ObDereferenceObject(FilePointer); //关闭对象引用
//需要转换成ANSI_STRING,然后在转换成char输出给ring3
RtlUnicodeStringToAnsiString(&string,&path,TRUE); //释放内存
if(string.Length >= 256 ) //保证以\0结尾
{
memcpy(ProcessPath, string.Buffer, 256);
*(ProcessPath + 255) = 0;
}
else
{
memcpy(ProcessPath, string.Buffer, string.Length);
ProcessPath[string.Length] = 0;
}
ExFreePool(path.Buffer); //释放
RtlFreeAnsiString(&string);//释放
}
//====================================进程信息==========================================================
void GetProcessInformation()//获取进程名,PID,路径
{
ListProcess(); //调用函数
KdPrint(("[DispatchIoctl] num:%d\n",num));//打印出进程数量
for(p=head;MmIsAddressValid(p);p=p->next) //写实现功能,获取进程PID,进程名,图标、全路径
{
//KdPrint(("===============================================\n"));
KdPrint(("[GetProcessInformation] EPROCESS:0x%x\n",p->addr));
//EPROCESS获取进程名
//XP下0x084偏移存放的进程PID
//XP下0x174偏移存放的进程ProcessName
p->pid=*(int *)(p->addr+CID);
KdPrint(("[GetProcessInformation] PID:%d\n",p->pid)); //进程PID
KdPrint(("===============================================\n"));
memcpy(p->name,(PUCHAR)(p->addr+NAME),16);
KdPrint(("[GetProcessInformation] ProcessName:%s\n",p->name)); //进程名
//获取全路径
GetProcessPath(p->addr,p->Path);
KdPrint(("[GetProcessInformation] ProcessPath:%s\n",p->Path)); //进程全路径
}
}
//==================================释放链表函数=========================================================
void FreeList()
{
//释放链表,释放内存
PROCESSINFO *q;
p=head;
q=p->next;
while(q!=NULL)
{
KdPrint(("[DispathIoctl] p->addr:0x%x\n",p->addr));
ExFreePool(p);
p=q;
q=p->next;
n=n+1;
}
KdPrint(("[DispathIoctl] p->addr:0x%x\n",p->addr));
ExFreePool(p);
head=NULL; //不知道ExFreePool释放后head竟然不为NULL.p和head都是全局变量
n=n+1;
KdPrint(("[DispatchIoctl] n:%d\n",n));//打印出释放进程数量
n=0;
}
|
能力值:
( LV12,RANK:210 )
|
-
-
10 楼
pspcidtable未导出结构保存了所有句柄的对象指针,只要获取这些指针地址就可以获取对象相关信息。
利用pspcidtable获取进程信息(进程名、进程PID、进程全路径、进程图标资源)
1.首先pspcidtable未导出需要获取地址 GetAddressPspcidtable()
2.pspcidtable如何获取句柄表的基址 GetTableBase()
3.pspcidtable记录的是对象指针,需要找出标志是进程对象的指针 GetProcessType()
4.如何找出进程对象 GetProcessObject()
5.利用什么结构记录进程对象地址 RecordProcessObject()
6.怎样遍历进程对象 ListProcessObject()
进程的object指向进程的EPROCESS,有了Eprocess就什么都有了。
|