DUPLICATEHANDLE函数可以实现将同步内核对象被拷贝并且将原内核对象关闭, 从而达到可以自由控制内核对象的目的, 这可以实现使得只能单一启用的进程成为多启用的进程的目的。
实现方法:
只要用钩子WH_GETMESSAGE挂钩并实现如下代码即可
LRESULT HookProc(int code, WPARAM wParam, LPARAM lParam)
{
STARTUPINFO startUpInfo;
GetStartupInfo(&startUpInfo);
char str[MAX_PATH];
LPSTR lpStr = startUpInfo.lpTitle;
if (!g_bFounded)
{
if (lpStr != NULL)
{
_splitpath(lpStr,NULL, NULL, str, NULL);
DWORD iStrLen = MAX_PATH;
DWORD dwRegType = 0;
RegQueryValueExA(HKEY_CURRENT_USER, "PeHook//HookedProcessName", NULL, &dwRegType, (LPBYTE)g_strAppName, &iStrLen);
if (strcmp(str,/*"wmplayer"*/g_strAppName) == 0)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
DWORD rl;
BOOL Result = FALSE;
//打开进程令牌环
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
//获得进程本地唯一ID
if (LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &tp.Privileges[0].Luid) )
{
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//调整权限
Result = AdjustTokenPrivileges(hToken, false, &tp, sizeof(tp), NULL, &rl);
}
if (Result)
{
char buffer[200];
HANDLE hSrcProc = GetCurrentProcess();
DWORD dwError = GetLastError();
HANDLE hSrcHandle = (HANDLE)0x38;
HANDLE hCurProc= GetCurrentProcess();
HANDLE hTargetHandle = 0;
BOOL bRet = DuplicateHandle(hSrcProc, hSrcHandle, hCurProc, &hTargetHandle, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_CLOSE_SOURCE);
if (bRet)
{
CloseHandle(hTargetHandle);
g_bFounded = TRUE;
}
}
}
return CallNextHookEx(glhHook,code,wParam,lParam);
}
/
资料:
简单算法遍历PspCidTable句柄表
ULONG GetPspCidTable()
{
ULONG PspCidTable=0;
ULONG FuncAddr=NULL;
UNICODE_STRING FuncName={0};
RtlInitUnicodeString(&FuncName,L"PsLookupProcessByProcessId");
FuncAddr=(ULONG)MmGetSystemRoutineAddress(&FuncName);
for (;;FuncAddr++)
{
if ((0x35ff==(*(PUSHORT)FuncAddr)) && (0xe8==(*(PUCHAR)(FuncAddr+6))))
{
PspCidTable=*(PULONG)(FuncAddr+2);
break;
}
}
return PspCidTable;
}
#define OBJECT_BODY_TO_TYPE 0x10
//从3级表开始遍历
ULONG BrowseTableL3(ULONG TableAddr)
{
ULONG Object=0;
ULONG ItemCount=511;
do
{
TableAddr+=8;
Object=*(PULONG)TableAddr;
Object&=0xfffffff8;
if (Object==0)
{
continue;
}
if ((*PsProcessType)==(*(PULONG)(Object-OBJECT_BODY_TO_TYPE)))
{
KdPrint(("%s",PsGetProcessImageFileName((PEPROCESS)Object)));
}
} while (--ItemCount>0);
return 0;
}
//从二级表开始遍历
ULONG BrowseTableL2(ULONG TableAddr)
{
do
{
BrowseTableL3(*(PULONG)TableAddr);
TableAddr+=4;
} while ((*(PULONG)TableAddr)!=0);
return 0;
}
//从1级表开始遍历
ULONG BrowseTableL1(ULONG TableAddr)
{
do
{
BrowseTableL2(*(PULONG)TableAddr);
TableAddr+=4;
} while ((*(PULONG)TableAddr)!=0);
return 0;
}
VOID RefreshProcessByPspCidTable()
{
ULONG PspCidTable=0;
ULONG HandleTable=0;
ULONG TableCode=0;
ULONG flag=0;
PspCidTable=GetPspCidTable();
HandleTable=*(PULONG)PspCidTable;
TableCode=*(PULONG)HandleTable;
flag=TableCode&3;
TableCode&=0xfffffffc;
switch (flag)
{
case 0:
BrowseTableL3(TableCode);
break;
case 1:
BrowseTableL2(TableCode);
break;
case 2:
BrowseTableL1(TableCode);
break;
}
}
劫持内核句柄
为什么需要 句柄?
句柄只是对对像的引用,当我们进入 内核时,获取 对象显然是很简单,但为什么还需要 句柄
呢? 应该说我们需要 句柄做为参数调用API. 因为直接操作 对象,可能会涉及到许多的未导
出的东西,如果自己实现的话是不大现实,而调用标准的 内核API又很多时候依赖于 句柄,但
又很多时候因为种种原因,我们得不到 句柄,这里我们将探讨下怎样获取一个有用的 句柄.
任何问题欢迎指正: http://hi.baidu.com/sysnap/blog
1 句柄的创建
ObpCreateHandle 这个函数会判断AccessMode,如果是用户态的,那PVOID ObjectTable;
ObjectTable = PsGetCurrentProcess()->ObjectTable
如果是在驱动中打开 句柄,则ObjectTable = ObpKernelHandleTable;
ObpKernelHandleTable是一个指向HANDLE_TABLE的指针,在系统中并没有导出
接着便会ATTACH 到SYSTEM进程..
调用ObpIncrementHandleCount增添Object引用数,这个地方我们慢点再考虑有没必要做一
些工作.
调用ExCreateHandle 创建 句柄,ExCreateHandle中调用
分配一个HANDLE_TABLE_ENTRY和一个 句柄,这个 句柄的值的产生是在
///ExpAllocateHandleTableEntry中完成的.
ExpAllocateHandleTableEntry 是 句柄分配的核心,也涉及到了相关的数据结构.但这里我
们可以不用管它.
我们只需要知道ObpCreateHandle会给我们返回一和 句柄,并且会在ObjectTable新添加一
个HANDLE_TABLE_ENTRY
lkd> dt _HANDLE_TABLE_ENTRY
nt!_HANDLE_TABLE_ENTRY
+0x000 Object : Ptr32 Void
HANDLE_TABLE_ENTRY 就包含有我们的Object了.
2 句柄到OBJECT的映射
这里涉及到了 句柄表的格式,关于 句柄表,网上已经有相关的文章讨论了,这里将不再说
typedef struct _EXHANDLE
{
union
{
struct
{
ULONG TagBits : 02;
ULONG Index : 30;
};
HANDLE GenericHandleOverlay;
};
} EXHANDLE, *PEXHANDLE;
可以看出,一个类型为HANDLE的,其实分为俩部分,TagBits和Index,具体干什么,请看下面
的函数
PHANDLE_TABLE_ENTRY
LookupHandleTableEntry(
IN PXP_HANDLE_TABLE HandleTable,
IN EXHANDLE Handle
)
{
ULONG i, j, k;
PHANDLE_TABLE_ENTRY Entry = NULL;
ULONG TableCode = HandleTable->TableCode& ~TABLE_LEVEL_MASK;
i = (Handle.Index >> 17) &0x1FF;
j = (Handle.Index >> 9) &0x1FF;
k = (Handle.Index) &0x1FF;
switch (HandleTable->TableCode &TABLE_LEVEL_MASK)
{
case 0 :
Entry = &((PHANDLE_TABLE_ENTRY)TableCode)[k];
break;
case 1 :
if (((PVOID *)TableCode)[j])
{
Entry = &((PHANDLE_TABLE_ENTRY *)TableCode)[j][k];
}
break;
case 2 :
if (((PVOID *)TableCode)[i])
if (((PVOID **)TableCode)[i][j])
{
Entry = &((PHANDLE_TABLE_ENTRY **)TableCode)[i][j][k];
}
break;
}
return Entry;
}
这样的话我们就可以根据HANDLE..获取到HANDLE_TABLE_ENTRY从而得到Object
上面的函数只适合XP和2003
3 句柄的权限
如果我们想修改某个 句柄的权限,可以通过HANDLE_TABLE_ENTRY::GrantedAccess
4ObOpenObjectByPointer
ObReferenceObjectByPointer添加计数
ObpCreateHandle 句柄的创建
5怎样伪造.
比如我们想伪造一个XX.EXE进程的 句柄.
1首先我们需要确定 ObpKernelHandleTable
DWORD dwObpKernelHandleTable = 0;
PVOID lpPsSystemObject = (PVOID)PsGetCurrentProcess();
dwObpKernelHandleTable = *(DWORD*)((DWORD)lpPsSystemObject +
gdwObjectTableOffset);
2 ObOpenObjectByPointer 打开explorer.exe (这里随便找个没保护的进程就可以)
获取 句柄 hProcess
3 用某些办法获取XX.EXE 的进程 对象EPROCESS
4 用hProcess为参数调用,LookupHandleTableEntry得到一个指向PHANDLE_TABLE_ENTRY的
指针pEntry
这个时候pEntry->Object应该就是 explorer.exe的EPRCESS
5 修改pEntry->Object = XX.EXE的EPROCESS
这样我们就完成了HANDLE和OBJECT的劫持..
这都是主要步骤,其中有些细节问题需要注意,比如增加 对象的 句柄数啊等等.
6 有效果吗?
我们知道大部分 内核API都会以一个HANDLE做为参数的,其内部基本都是调用了
ObReferenceObjectByHandle来定位 对象,我们分析下ObReferenceObjectByHandle就知道
我们的劫持是否有用了.
很显然ObReferenceObjectByHandle是根据 HANDLE_TABLE来把 句柄转化为 对象,过程简单
为 handle--->> pEntry->Object
所以劫持是成功的.