vista/7 以后的系统都存在session隔离,
CreateRemoteThread的流程
vista下 Kernel32!CreateRemoteThread
win7下 Kernel32!CreateRemoteThread->..一些中转..->KernelBase->CreateRemoteThreadEx
在vista/7上,kernel32/或者KernelBase里存在一个全局变量BaseRunningInServerProcess
在进程创建的时候就被初始化了,表示本进程是否是一个服务进程,服务的session是0
由CreateRemoteThread流程的最尾端的函数处理了这个标志,发现不是从服务进程发起的调用,就跳转了,
由于这个判断是在应用层而且是本进程做的,所以很没有意义,可以直接修改.
一般的修改办法是修改判断跳转,其实在某些条件下比如win7/32/64可以更简单的方式绕过.
原理是一样的,只是vista下没有导出这个全局变量,不好定位,只想到用特征码搜索的方式.
但在win7下就方便多了,由于kernelBase!KernelBaseGlobalData 导出,所以直接调用这个函数就可以获得这个全局结构体的地址,\
虽然这个结构体没有文档,但是加上一个偏移就是我们要的标志.
kernelBase!KernelBaseGlobalData的函数原形
typedef void* (__stdcall *LPFN_KernelBaseGetGlobalData)(void);
win7 32下 BaseRunningInServerProcess 位于 KERNELBASE!KernelBaseGlobalData+0x30
win7 64 下, BaseRunningInServerProcess 位于 KERNELBASE!KernelBaseGlobalData+0x5c
原理说完了,贴段可以自适应的代码.
代码:
//创建远程线程,处理session隔离
//xSpy 2012.07.26
//xp ~ 7 /x86 ~ x64 自适应
typedef void* (__stdcall *LPFN_KernelBaseGetGlobalData)(void);
HANDLE LibCreateRemoteThread
(
__in HANDLE hProcess,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId
)
{
OSVERSIONINFOEX stOSVersionInfoEx={0};
LPFN_CreateRemoteThreadEx pCreateRemoteThreadEx=NULL;
LPFN_KernelBaseGetGlobalData pKernelBaseGetGlobalData=NULL;
UCHAR* pCreateRemoteThread=NULL;
UCHAR* pGlobalData=NULL;
UCHAR* pMisc=NULL;
HMODULE hKernelBase=NULL;
HMODULE hKernel32=NULL;
HANDLE hNewThread=NULL;
ULONG ulIndex=0;
WORD wCode=0;
do
{
stOSVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (!GetVersionEx((OSVERSIONINFO*)&stOSVersionInfoEx))
{
break;
}
//vista以前的系统不存在这个问题
if ( (stOSVersionInfoEx.dwMajorVersion < 6) || (NtCurrentProcess() == hProcess) )
{
hNewThread = CreateRemoteThread(hProcess,NULL,0,lpStartAddress,lpParameter,dwCreationFlags,lpThreadId);
break;
}
if ( (stOSVersionInfoEx.dwMajorVersion == 6) && (0 == stOSVersionInfoEx.dwMinorVersion) )
{
//vista
hKernel32 = LoadLibraryA("Kernel32.dll");
pCreateRemoteThread = (UCHAR*)GetProcAddress(hKernel32,"CreateRemoteThread");
for (ulIndex=0; ulIndex < 0x300;ulIndex += 1)
{
wCode = *((USHORT*)(pCreateRemoteThread + ulIndex));
#ifdef _WIN64
if (0x3D80 == wCode )
{
pMisc = (*((ULONG*)(pCreateRemoteThread + ulIndex + 2))) + (pCreateRemoteThread + ulIndex + 7);
break;
}
#else
if (0x1D38 == wCode )
{
pMisc = (UCHAR*)(*((ULONG*)(pCreateRemoteThread + ulIndex + 2)));
break;
}
#endif
}
}
else if ( (stOSVersionInfoEx.dwMajorVersion == 6) && (1 == stOSVersionInfoEx.dwMinorVersion) )
{
//win7
hKernelBase = LoadLibraryW(L"KernelBase.dll");
if (NULL == hKernelBase)
{
break;
}
pKernelBaseGetGlobalData = (LPFN_KernelBaseGetGlobalData)GetProcAddress(hKernelBase,"KernelBaseGetGlobalData");
if (NULL == pKernelBaseGetGlobalData)
{
break;
}
pGlobalData = (UCHAR*)pKernelBaseGetGlobalData();
if (NULL == pGlobalData)
{
break;
}
#ifdef _WIN64
pMisc = pGlobalData + 0x5C;
#else
pMisc = pGlobalData + 0x30;
#endif
}
else
{
//手上的win8 Build 8250 没有session 隔离
}
//////////////////////////////////////////////////////////////////////////
if (NULL == pMisc)
{
break;
}
//Patch
*pMisc = 1;
//xx
hNewThread = CreateRemoteThread(hProcess,NULL,0,lpStartAddress,lpParameter,dwCreationFlags,lpThreadId);
//UnPatch
*pMisc = 0;
} while (FALSE);
if (NULL != hKernelBase)
{
FreeLibrary(hKernelBase);
hKernelBase = NULL;
}
return hNewThread;
}
在vista 32/64 win7 32/64 下测试通过.
手上的win8 Build 8250 因为是测试版的原因,没有session 隔离,可以任意注入.
但是session判断语句仍然存在,直接跳走了,估计正式版会放开.