//当我们用远程线程注入DLL到另一个进程空间中时,我们就会经常用到OpenProcess()函数。现在假设我们的进程调用OpenProcess()根据要打开的另一个进程Id号,它是怎么得到那一个进程句柄的。然后根据 这个句柄操作那个进程的。OpenProcess里面会调用 NtOpenProcess()系统调用,其它的很多细节没有分析,我只分析了如何得到句柄。看代码如下(都是操作系统的代码,我只是分析一下,因为技术有限)
NTSTATUS
NtOpenProcess (
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId OPTIONAL
)
{ HANDLE Handle;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PEPROCESS Process;
PETHREAD Thread;
CLIENT_ID CapturedCid;
BOOLEAN ObjectNamePresent;
BOOLEAN ClientIdPresent;
ACCESS_STATE AccessState;
AUX_ACCESS_DATA AuxData;
BOOLEAN DebugPrivilege;
PAGED_CODE();
//
// Make sure that only one of either ClientId or ObjectName is
// present.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {//如果这个调用是从OpenProcess引起的就进来,就要检查看内核函数看能不能访问用户空间的地址
//
// Since we need to look at the ObjectName field, probe
// ObjectAttributes and capture object name present indicator.
//
try {
ProbeForWriteHandle(ProcessHandle);
ProbeForRead(ObjectAttributes,
sizeof(OBJECT_ATTRIBUTES),
sizeof(ULONG));
ObjectNamePresent = (BOOLEAN)ARGUMENT_PRESENT(ObjectAttributes->ObjectName);
if (ARGUMENT_PRESENT(ClientId)) {
ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
CapturedCid = *ClientId;
ClientIdPresent = TRUE;
}
else {
ClientIdPresent = FALSE;
}
}
except(EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
}
else {
//如果直接从内核下调用的,直接访问就是了
ObjectNamePresent = (BOOLEAN)ARGUMENT_PRESENT(ObjectAttributes->ObjectName);
if (ARGUMENT_PRESENT(ClientId)) {
CapturedCid = *ClientId;
ClientIdPresent = TRUE;
}
else {
ClientIdPresent = FALSE;
}
}
if ( ObjectNamePresent && ClientIdPresent ) {
return STATUS_INVALID_PARAMETER_MIX;//如果进程对象名和进程ID号都存在的话,返回参数错误
}
//
// Create an AccessState here, because the caller may have
// DebugPrivilege, which requires us to make special adjustments
// to his desired access mask. We do this by modifying the
// internal fields in the AccessState to achieve the effect
// we desire.
//
Status = SeCreateAccessState(
&AccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Check here to see if the caller has SeDebugPrivilege. If
// he does, we will allow him any access he wants to the process.
// We do this by clearing the DesiredAccess in the AccessState
// and recording what we want him to have in the PreviouslyGrantedAccess
// field.
//
// Note that this routine performs auditing as appropriate.
//
if (NtGlobalFlag & ***_IGNORE_DEBUG_PRIV) {
DebugPrivilege = TRUE;
} else
DebugPrivilege = SeSinglePrivilegeCheck(
SeDebugPrivilege,
PreviousMode
);
if (DebugPrivilege) {
//如果这个进程有SeDebugPrivilege权限就会进入这里进一步设置
if ( AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED ) {
AccessState.PreviouslyGrantedAccess |= PROCESS_ALL_ACCESS;
} else {
AccessState.PreviouslyGrantedAccess |= ( AccessState.RemainingDesiredAccess );
}
AccessState.RemainingDesiredAccess = 0;//清除这个权限,意味着给了这个进程对要操作的进程对象的所有权限
}
if ( ObjectNamePresent ) {
//
// Open handle to the process object with the specified desired access,
// set process handle value, and return service completion status.
//
Status = ObOpenObjectByName(
ObjectAttributes,
PsProcessType,
PreviousMode,
&AccessState,
0,
NULL,
&Handle
); 这里应该也会在本进程的句柄表中加入一项,并返回这个HANDLE_TABLE_ENTRY数组的下标(进程句柄),这个HANDLE_TABLE_ENTRY结构里的object指向被打开的那个进程对象头的地址 SeDeleteAccessState( &AccessState );
if ( NT_SUCCESS(Status) ) {
try {
*ProcessHandle = Handle;
}
except(EXCEPTION_EXECUTE_HANDLER) {
return Status;
}
}
return Status;
}
if ( ClientIdPresent ) {
Thread = NULL;
if (CapturedCid.UniqueThread) {
Status = PsLookupProcessThreadByCid(
&CapturedCid,
&Process,
&Thread
);
if ( !NT_SUCCESS(Status) ) {
SeDeleteAccessState( &AccessState );
return Status;
}
}
else {
//注意这里的进程ID其实是PspCidTable句柄表中数组的下标(它是全局的,整个系统才一个这样的表)线程ID也是一样的
Status = PsLookupProcessByProcessId(
CapturedCid.UniqueProcess,
&Process
);//根据进程ID从PspCidTable表中得到进程对象(注意这里是对象体不是对象头)的地址
if ( !NT_SUCCESS(Status) ) {
SeDeleteAccessState( &AccessState );
return Status;
}
}
//
// OpenObjectByAddress
//
Status = ObOpenObjectByPointer(
Process,
ObjectAttributes->Attributes,
&AccessState,
0,
PsProcessType,
PreviousMode,
&Handle
);//这里应该也会在本进程的句柄表中加入一项,并返回这个HANDLE_TABLE_ENTRY数组的下标(进程句柄),这个HANDLE_TABLE_ENTRY结构里的object指向被打开的那个进程对象头的地址。 SeDeleteAccessState( &AccessState );
if ( Thread ) {
ObDereferenceObject(Thread);
}
ObDereferenceObject(Process);
if ( NT_SUCCESS(Status) ) {
try {
*ProcessHandle = Handle;
}
except(EXCEPTION_EXECUTE_HANDLER) {
return Status;
}
}
return Status;
}
return STATUS_INVALID_PARAMETER_MIX;
}
NTSTATUS
ObOpenObjectByPointer(
IN PVOID Object,
IN ULONG HandleAttributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PHANDLE Handle
)
{
NTSTATUS Status;
HANDLE NewHandle;
POBJECT_HEADER ObjectHeader;
ACCESS_STATE LocalAccessState;
PACCESS_STATE AccessState = NULL;
AUX_ACCESS_DATA AuxData;
PAGED_CODE();
ObpValidateIrql( "ObOpenObjectByPointer" );
Status = ObReferenceObjectByPointer( Object,
0,
ObjectType,
AccessMode
);//这个函数会增加这个对象的引用计数,防止对象管理器删除这个对象,如下ObjectHeader->PointerCount += 1;
if (NT_SUCCESS( Status )) {
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
if (!ARGUMENT_PRESENT( PassedAccessState )) {
Status = SeCreateAccessState( &LocalAccessState,
&AuxData,
DesiredAccess,
&ObjectHeader->Type->TypeInfo.GenericMapping
);
if (!NT_SUCCESS( Status )) {
ObDereferenceObject( Object );
return(Status);
}
AccessState = &LocalAccessState;
} else {
AccessState = PassedAccessState;
}
if (ObjectHeader->Type->TypeInfo.InvalidAttributes & HandleAttributes) {
if (AccessState == &LocalAccessState) {
SeDeleteAccessState( AccessState );
}
ObDereferenceObject( Object );
return( STATUS_INVALID_PARAMETER );
}
Status = ObpCreateHandle( ObOpenHandle,
Object,
ObjectType,
AccessState,
0,
HandleAttributes,
FALSE,
AccessMode,
(PVOID *)NULL,
&NewHandle
);
if (!NT_SUCCESS( Status )) {
ObDereferenceObject( Object );
}
}
if (NT_SUCCESS( Status )) {
*Handle = NewHandle;
}
else {
*Handle = NULL;
}
if (AccessState == &LocalAccessState) {
SeDeleteAccessState( AccessState );
}
return( Status );
}
NTSTATUS
ObpCreateHandle(
IN OB_OPEN_REASON OpenReason,
IN PVOID Object,
IN POBJECT_TYPE ExpectedObjectType OPTIONAL,
IN PACCESS_STATE AccessState,
IN ULONG ObjectPointerBias OPTIONAL,
IN ULONG Attributes,
IN BOOLEAN DirectoryLocked,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *ReferencedNewObject OPTIONAL,
OUT PHANDLE Handle
)
{
NTSTATUS Status;
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
PVOID ObjectTable;
OBJECT_TABLE_ENTRY ObjectTableEntry;
HANDLE NewHandle;
ACCESS_MASK DesiredAccess;
ACCESS_MASK GrantedAccess;
ULONG BiasCount;
PAGED_CODE();
ObpValidateIrql( "ObpCreateHandle" );
DesiredAccess = AccessState->RemainingDesiredAccess |
AccessState->PreviouslyGrantedAccess;
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
ObjectType = ObjectHeader->Type;
if (ARGUMENT_PRESENT( ExpectedObjectType ) &&
ObjectType != ExpectedObjectType
) {
if (DirectoryLocked) {
ObpLeaveRootDirectoryMutex();
}
return( STATUS_OBJECT_TYPE_MISMATCH );
}
ObjectTableEntry.ObjectHeader = ObjectHeader;//初始化handle_table_entry结构,它用来插入到句柄表中的一项
//#define ObpGetObjectTable() (PsGetCurrentProcess()->ObjectTable)
ObjectTable = ObpGetObjectTable();//得到当前进程的句柄表(也就是调用OpenProcess的进程)
//
// ObpIncrementHandleCount will perform access checking on the
// object being opened as appropriate.
//
Status = ObpIncrementHandleCount( OpenReason,
PsGetCurrentProcess(),
Object,
ObjectType,
AccessState,
AccessMode,
Attributes
);
if (AccessState->GenerateOnClose) {
Attributes |= OBJ_AUDIT_OBJECT_CLOSE;
}
ObjectTableEntry.Attributes |= (Attributes & OBJ_HANDLE_ATTRIBUTES);
DesiredAccess = AccessState->RemainingDesiredAccess |
AccessState->PreviouslyGrantedAccess;
GrantedAccess = DesiredAccess &
(ObjectType->TypeInfo.ValidAccessMask |
ACCESS_SYSTEM_SECURITY );
if (DirectoryLocked) {
ObpLeaveRootDirectoryMutex();
}
if (!NT_SUCCESS( Status )) {
return( Status );
}
if (ARGUMENT_PRESENT( ObjectPointerBias )) {
BiasCount = ObjectPointerBias;
while (BiasCount--) {
ObpIncrPointerCount( ObjectHeader );
}
}
#if i386 && !FPO
if (NtGlobalFlag & ***_KERNEL_STACK_TRACE_DB) {
ObjectTableEntry.GrantedAccessIndex = ObpComputeGrantedAccessIndex( GrantedAccess );
ObjectTableEntry.CreatorBackTraceIndex = RtlLogStackBackTrace();
}
else
#endif // i386 && !FPO
ObjectTableEntry.GrantedAccess = GrantedAccess;//初始化这个句柄的权限
NewHandle = ExCreateHandle( ObjectTable, (PHANDLE_ENTRY)&ObjectTableEntry );//它会在本进程的句柄表中加入一项ObjectTableEntry,然后返回这个新的句柄(HANDLE_TABLE_ENTRY数组的下标)这里就是在调用OpenProcess()的进程的句柄表中加入了一项得到了句柄。以后根据这个句柄就能调用很多函数了,比如WriteProcessMemory()需要以进程句柄作为参数的函数。
if (NewHandle == NULL) {
if (ARGUMENT_PRESENT( ObjectPointerBias )) {
BiasCount = ObjectPointerBias;
while (BiasCount--) {
ObpDecrPointerCount( ObjectHeader );
}
}
ObpDecrementHandleCount( PsGetCurrentProcess(),
ObjectHeader,
ObjectType,
GrantedAccess
);
return( STATUS_INSUFFICIENT_RESOURCES );
}
*Handle = MAKE_OBJECT_HANDLE( NewHandle );
//
// If requested, generate audit messages to indicate that a new handle
// has been allocated.
//
// This is the final security operation in the creation/opening of the
// object.
//
if ( AccessState->GenerateAudit ) {
SeAuditHandleCreation(
AccessState,
*Handle
);
}
if (OpenReason == ObCreateHandle) {
PAUX_ACCESS_DATA AuxData = AccessState->AuxData;
if ( ( AuxData->PrivilegesUsed != NULL) && (AuxData->PrivilegesUsed->PrivilegeCount > 0) ) {
SePrivilegeObjectAuditAlarm(
*Handle,
&AccessState->SubjectSecurityContext,
GrantedAccess,
AuxData->PrivilegesUsed,
TRUE,
KeGetPreviousMode()
);
}
}
if (ARGUMENT_PRESENT( ObjectPointerBias ) &&
ARGUMENT_PRESENT( ReferencedNewObject )
) {
*ReferencedNewObject = Object;
}
return( STATUS_SUCCESS );
}