首页
社区
课程
招聘
[原创]围绕SwapContext的一点代码
发表于: 2012-3-4 12:50 12124

[原创]围绕SwapContext的一点代码

2012-3-4 12:50
12124
这次的胡乱探究原因在于看到了一个08年的帖子:
标 题:HOOK SwapContext 枚举隐藏进程(学习笔记4)
作 者:bzhkl
时 间:2008-12-11 12:01
链 接:http://bbs.pediy.com/showthread.php?t=78464
都是很老的东西了,可惜很菜才开始关注

这篇帖子讲了HOOK SwapContext 枚举隐藏进程的方法
下面是我虚拟机的WIN XP SP3  调试的代码:
/*********************************************************************
nt!KiSwapContext:
8054696c 83ec10          sub     esp,10h
8054696f 895c240c        mov     dword ptr [esp+0Ch],ebx
80546973 89742408        mov     dword ptr [esp+8],esi
80546977 897c2404        mov     dword ptr [esp+4],edi
8054697b 892c24          mov     dword ptr [esp],ebp
8054697e 648b1d1c000000  mov     ebx,dword ptr fs:[1Ch]
80546985 8bf1            mov     esi,ecx                       //自己修改处
80546987 8bbb24010000    mov     edi,dword ptr [ebx+124h]
8054698d 89b324010000    mov     dword ptr [ebx+124h],esi
80546993 8a4f58          mov     cl,byte ptr [edi+58h]
80546996 e8f5000000      call    nt!SwapContext (80546a90)     //作者修改处
8054699b 8b2c24          mov     ebp,dword ptr [esp];
8054699e 8b7c2404        mov     edi,dword ptr [esp+4]
805469a2 8b742408        mov     esi,dword ptr [esp+8]
805469a6 8b5c240c        mov     ebx,dword ptr [esp+0Ch]
805469aa 83c410          add     esp,10h
805469ad c3              ret
805469ae 8bff            mov     edi,edi
/**********************************************************************/
作者就是在 0x80546996 地址处修改的CALL  XXXXX  的  XXXXX  使之先调用自己的FAKE函数,在Fake函数里得到换进换出的线程信息,进而得到进程信息。

所以想要试试能不能绕过这种方法,顺便可以增进inline hook以及堆栈平衡的理解
于是,第一次尝试:        作者是通过4号线程(本机)的KernelStack参数得到的SwapContext 的返回地址进而修改,于是打算直接绕过这里的CALL,由于测试中修改5个字节的JMP老是不对,后面指令碎屑填充nop会被认为成这样:jmp  9090:XXXXXXXX,基础有限不知为啥??于是改为了绝对跳转,在这修改
        80546985 8bf1            mov     esi,ecx
        Fake函数:
__declspec (naked) VOID Fake_Function()
{
	_asm
	{
		mov     esi,ecx                  //2 Byte
	    mov     edi,dword ptr [ebx+124h] //6 Byte
        mov     dword ptr [ebx+124h],esi //6  Byte
        mov     cl,byte ptr [edi+58h]    //3  Byte
	}
	_asm
	{
	     _emit 0x90     //CALL SwapContext
         _emit 0x90
         _emit 0x90
         _emit 0x90
		_emit 0x90
		_emit 0x90     //JMP  BACK
         _emit 0x90
         _emit 0x90
         _emit 0x90
         _emit 0x90  
         _emit 0x90  
         _emit 0x90  
	}
}
又想到如果有人在SawpContext开头做了HOOK,不就不行了么,于是又绕过了SawpContext的开头,流程:KiSwapContext--(JMP)---Fake_Function---(CALL)--Fake_SwapContext----(JMP TO)----SwapContext+14处,继续执行---(函数返回)---Fake_Function---(JMP)---KiSwapContext
_declspec (naked) VOID Fake_SwapContext()
{
	_asm//14   BYTE
	{
		or      cl,cl
		mov     byte ptr es:[esi+2Dh],2
		pushfd
		lea     ecx,[ebx+540h]
	}
	_asm
	{
		_emit 0x90  //jmp          Fake_SwapContext----SwapContext+0x14
        _emit 0x90
        _emit 0x90
        _emit 0x90
        _emit 0x90  
        _emit 0x90  
        _emit 0x90  
	}
}
结果,居然不行。。。
照样可以得到进程信息,当时就郁闷了,结果用WINDBG查看,发现  自己的  CALL   SwapContext 被   替换成了他的,也就是说  他得到的  KernelStack  是我的CALL  Fake_SwapContext的返回地址。。。。KernelStack被修改了。。。。
        基础太烂,都不知道KernelStack这个内核栈调用的开始是干嘛的,于是又在  CALL  Fake_SwapContext  前面加了一个CALL (一个空函数) 不行。。。。改为  在后面加一个CALL  还是不行。。。检测程序都能正确定位CALL    SwapContext   地址然后替换。。。。
        查看SwapContext的具体实现发现,有这么一处:
        80546ae0 fa              cli
        80546ae1 896728          mov     dword ptr [edi+28h],esp
        这里将栈指针存储到了线程的KernelStack,怪不得老能被他正确定位。。
于是,第二次尝试        在原有第一次基础上,在SwapContext函数的
       
80546ada 8a4e2c          mov     cl,byte ptr [esi+2Ch]    //这里跳走
	80546add 884b50          mov     byte ptr [ebx+50h],cl
	80546ae0 fa              cli
	80546ae1 896728          mov     dword ptr [edi+28h],esp
	80546ae4 8b4618          mov     eax,dword ptr [esi+18h]   //跳回这里
	80546ae7 8b4e1c          mov     ecx,dword ptr [esi+1Ch]
        跳转到自己的函数,修改esp的值,当时想就只是修改了线程的KernelStack的值,之后又恢复了esp的值,继续执行SwapContext后边的指令( mov     eax,dword ptr [esi+18h]   //跳回这里),应该可以吧...结果蓝的不成样子,完全不知道什么错误
        还发现
        u  KiSwapContext
        得到的结果完全不同于原始的,第一条指令就不同。。我就纳闷了,KiSwapContext函数怎么会被改掉????

于是,第三次尝试还是第一次这个流程
    流程:KiSwapContext--(JMP)---Fake_Function---(CALL)--Fake_SwapContext----(JMP TO)----SwapContext+14处,继续执行---(函数返回)---Fake_Function---(JMP)---KiSwapContext
    但是会在Fake_Function开始即判断接下来自己的   CALL  Fake_SwapContext有没有被修改,如果没有,继续走这个流程,如果被修改了,则跳回
80546996 e8f5000000      call    nt!SwapContext (80546a90)   
继续执行,不走这个流程了,心想这样应该可以了吧。。。。
经过测试成功!修改如下:

#include <ntddk.h>
#define LOCKEDCODE code_seg()
ULONG RealSwapContextOffset;
ULONG FixAddr;//函数KiSwapContext修改处的地址
ULONG SwapContextAddr;
ULONG NextAddr;//KiSwapContext  CALL 处的下一条指令地址
ULONG GoBackAddr;
ULONG CallAddr;//CALL Fake_Swap处的地址
extern KIRQL Irql;
extern ULONG g_uCr0;

ULONG NewCallOffset;//CALL新的偏移

PETHREAD Thread;

VOID Fake_SwapContext();
VOID Fake_Stack();

BYTE Call_To_SwapContext[5]={0xE8,0,0,0,0};
BYTE Jmp_To_KiSwapContext[7] = {0xEA, 0, 0, 0, 0, 0x08, 0x00 };
BYTE Jmp_To_SwapContext[7] = {0xEA, 0, 0, 0, 0, 0x08, 0x00 };
BYTE JNZ_JMP[6]={0x0f,0x85, 0, 0, 0, 0};BYTE EditCode[8]={0xEA,0,0,0,0,0x08,0x00,0x90};
BYTE EditCode1[1]={0x90};

NTKERNELAPI
NTSTATUS
PsLookupThreadByThreadId (
    IN PVOID        UniqueThreadId,
    OUT PETHREAD    *Thread
);
ULONG GetRealSwapContextOffset()
{
	ULONG status;

	ULONG Off;	status=PsLookupThreadByThreadId((PVOID)8, &(PETHREAD)Thread);//win xp sp3
	DbgPrint("Thread地址:%x\n",Thread);
	if(NT_SUCCESS(status))
	{
		if(MmIsAddressValid(Thread))
		{
			NextAddr = *(PULONG)((ULONG)Thread+0x028 );
		}
		if(MmIsAddressValid(NextAddr+8))
		{
			_asm
			{
				push eax
				mov eax,NextAddr
				add eax,8
				mov eax,[eax]
				mov NextAddr,eax;
				pop eax
			}
			DbgPrint("NextAddr:%x\n",NextAddr);
		}
	}
	else
	{
		return 0;
	}

	_asm
	{
		mov eax,NextAddr
	    sub eax,5
		mov FixAddr,eax
		mov edx,[eax+1]
		push edx
		pop Off
	}
	DbgPrint("FixAddr:%x\n",FixAddr);
	DbgPrint("偏移:%x\n",Off);
	SwapContextAddr=Off+NextAddr;
	DbgPrint("SwapContext地址:%x\n",SwapContextAddr);
	return Off;
}

__declspec (naked) VOID Fake_Function()
{
	_asm
	{
		mov     esi,ecx                  //2 BYTE
	    mov     edi,dword ptr [ebx+124h] //6 BYTE
        mov     dword ptr [ebx+124h],esi //6 BYTE
        mov     cl,byte ptr [edi+58h]    //3 BYTE
	}//17 BYTE   对抗内核栈Patch
	_asm
	{
		push ebx                         //1 BYTE
		mov ebx,Fake_Function            //5 BYTE
		add ebx,55                       //3 BYTE
		mov ebx,[ebx]                    //2 BYTE
		mov CallAddr,ebx                 //6 BYTE
		pop ebx                          //1 BYTE
	}//38
	_asm
	{
		push eax                              //1 BYTE
		mov eax,CallAddr                      //5 BYTE
		cmp eax,NewCallOffset                 //6 BYTE
		pop eax                               //1 BYTE
	}//48
	_asm
	{
		_emit 0x90  // 这个填充 jnz   //跳转到?KiSwapContext
        _emit 0x90
        _emit 0x90
        _emit 0x90
        _emit 0x90  //绝对地址
        _emit 0x90  
	}//54
	_asm
	{
		_emit 0x90  // 这个填充CALL   e8 xxxxxxxx 
        _emit 0x90
        _emit 0x90
        _emit 0x90
		_emit 0x90
		_emit 0x90  // 这个填充 jmp   //跳转到  KiSwapContext
        _emit 0x90
        _emit 0x90
        _emit 0x90
        _emit 0x90  //绝对地址
        _emit 0x90  
        _emit 0x90
	}
}

VOID StartHookKiSwapContext()
{
	ULONG uAttr;
	ULONG uTemp,uTemp1;
	ULONG NewCallOffset1;
	RealSwapContextOffset=GetRealSwapContextOffset();
	uTemp=(ULONG*)((BYTE*)Fake_Function+54);
	NewCallOffset=(ULONG)Fake_SwapContext-uTemp-5;//Fake_Function----Fake_SwapContext

	NewCallOffset1=(ULONG*)((BYTE*)SwapContextAddr+14);
	

	DbgPrint("uTemp:%x\n",uTemp);
	DbgPrint("NewCallOffset1:%x\n",NewCallOffset1);
	DbgPrint("NewCallOffset:%x\n",NewCallOffset);
	_asm
    {
        push eax;
        mov eax, cr0;
        mov uAttr, eax;
        and eax, 0FFFEFFFFh;
        mov cr0, eax;
        pop eax;
        cli
    }

    g_uCr0 = uAttr;

    //提升IRQL中断级
    //Irql=KeRaiseIrqlToDpcLevel();
	*(ULONG*)(EditCode+1)=(ULONG*)Fake_Function;// KiSwapContext----Fake_Function
	*((ULONG*)(Call_To_SwapContext + 1)) =NewCallOffset;//填充CALL指令
	*((ULONG*)(Jmp_To_KiSwapContext + 1)) = (ULONG)((BYTE*)FixAddr+5);//填充JMP
	*((ULONG*)(Jmp_To_SwapContext + 1)) = NewCallOffset1;

	CallAddr=*(ULONG*)((BYTE*)Fake_Function+55);   //CALL   Fake_SwapContext    *(PULONG)CallAddr==(PULONG)Fake_SwapContext
	
	*((ULONG*)(JNZ_JMP + 2)) = (ULONG)((BYTE*)FixAddr-1)-(ULONG)(((BYTE*)Fake_Function+48))-5;

		RtlCopyMemory((BYTE*)Fake_Function+ 48,JNZ_JMP,6);

		RtlCopyMemory((BYTE*)Fake_Function+ 54,Call_To_SwapContext,5); //Fake_Function----Fake_SwapContext     
	
		RtlCopyMemory((BYTE*)Fake_SwapContext+14,Jmp_To_SwapContext,7);//Fake_SwapContext----SwapContext+0x14
	
		RtlCopyMemory((BYTE*)Fake_Function+ 59,Jmp_To_KiSwapContext,7);//Fake_Function----KiSwapContext  

		RtlCopyMemory((BYTE*)FixAddr-17,EditCode,8);//KiSwapContext----Fake_Function

	//_asm int 3

    //KeLowerIrql(Irql);
    _asm
    {
        sti
        push eax;
        mov eax, g_uCr0;
        mov cr0, eax;
        pop eax;
    }
}
_declspec (naked) VOID Fake_SwapContext()
{
	_asm//14   BYTE
	{
		or      cl,cl
		mov     byte ptr es:[esi+2Dh],2
		pushfd
		lea     ecx,[ebx+540h]
	}
	_asm
	{
	    _emit 0x90  // 这个填充 jmp          Fake_SwapContext----SwapContext+0x14
        _emit 0x90
        _emit 0x90
        _emit 0x90
        _emit 0x90  
        _emit 0x90  
        _emit 0x90  
	}
}

调用StartHookKiSwapContext即可,如图:


[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (4)
雪    币: 107
活跃值: (326)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
膜拜啊~~~~~~~~~~~~~~~

很牛啊................
2012-3-4 13:00
0
雪    币: 207
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
每次任务切换的时候,建立的新KernelStack中都会保存调用该次任务切换SwapContext的返回地址!所以第二次尝试失败
膜拜楼主
2012-3-4 20:50
0
雪    币: 862
活跃值: (329)
能力值: ( LV9,RANK:165 )
在线值:
发帖
回帖
粉丝
4
底子没打好,有时候

RtlCopyMemory((BYTE*)Fake_Function+ 48,JNZ_JMP,6);

会导致蓝屏,呃。。。。为什么呢?
2012-3-10 20:10
0
雪    币: 862
活跃值: (329)
能力值: ( LV9,RANK:165 )
在线值:
发帖
回帖
粉丝
5
有没有人解答一下上面的问题?已经WPOFF()了,有时候会蓝屏,说是地址写错误。。
2012-3-28 22:15
0
游客
登录 | 注册 方可回帖
返回
//