A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #26085  by pboy0922
 Mon Jun 15, 2015 8:18 am
cziter15 wrote:The best way to do this from kernelmode on x86 is to hook NtQueryPerformanceCounter via SSDT.
You can allso call PsGetCurrentProcess inside your hook and tamper values only if call is made in context of a process you need to speed up.

Remember that on x64 kernelmode hooking is not allowed due to Patchguard.
Hi,cziter15,I know the PatchGuard on x64,I use other method to pass it.Now I want to kown how to make systemtime normal~ :D
 #26091  by cziter15
 Tue Jun 16, 2015 1:36 pm
If you hook KeQueryPerformanceCounter, you can determine (by using PsGetCurrentProcess) which process called this function.
If PEPROCESS of your target is equal to value returned by PsGetCurrentProcess, then do your moddifications.
This covers only QueryPerformanceCounter API calls.

If you want to modify time returned by GetTickCount from kernelmode for a specific process, you need to hack pages and remap SharedUserData for specific process, then update values there manually. This will be not easy. You can also try to play with "on access" breakpoint.
 #26106  by pboy0922
 Thu Jun 18, 2015 1:23 am
myid wrote:
pboy0922 wrote:
myid wrote:Talk is cheap. Show me the code.
hi,my Key Code is
[syntax="c"]
#pragma pack(push) //保存对齐状态
#pragma pack(1)//设定为1字节对齐

typedef struct _JmpStub
{
unsigned char jmp;
unsigned long *offset;
}JmpStub,*PJmpStub;

typedef struct _FakeKeUpdateSystemTime
{
unsigned short mul;
unsigned long *speedx;
unsigned short div;
unsigned long *speedbase;
JmpStub jmpStub;
}FakeKeUpdateSystemTime,*PFakeKeUpdateSystemTime;

typedef struct _OriginalFunction
{
unsigned char OriginalHead[12];//存储函数的头几个字节,默认为nop
JmpStub jmpStub;
}OriginalFunction,*POriginalFunction;
#pragma pack(pop)//恢复对齐状态


ULONG g_uSpeedX = 600;
ULONG g_uSpeedBase = 100;

ULONG g_uHookOffsetKeUpdateSystemTime = 0;//内联hook函数KeUpdateSystemTime的字节数
BOOLEAN g_bHookKeUpdateSystemTime = FALSE;
LPVOID g_lpfnKeUpdateSystemTime = NULL;
PFakeKeUpdateSystemTime g_FakeKeUpdateSystemTime = NULL;
POriginalFunction g_OriginalKeUpdateSystemTime = NULL;

ULONG g_uHookOffsetKeQueryPerformanceCounter = 0;//内联hook函数KeQueryPerformanceCounter的字节数
BOOLEAN g_bHookKeQueryPerformanceCounter = FALSE;
//LPVOID g_lpfnKeQueryPerformanceCounter = NULL;
POriginalFunction g_OriginalKeQueryPerformanceCounter = NULL;
LARGE_INTEGER g_liPreOriginalCounter;// 前一个查询到的性能指标数
LARGE_INTEGER g_liPreReturnCounter;// 变化后的性能指标数值


NTSTATUS HookKeyFunctions()
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = HookKeUpdateSystemTime();
if(!NT_SUCCESS(status)) return status;
status = HookKeQueryPerformanceCounter();
return status;
}

NTSTATUS HookKeUpdateSystemTime()
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PFakeKeUpdateSystemTime lpFake = NULL;
POriginalFunction lpOriginal = NULL;
JmpStub jmpStub = {0};
KIRQL Irql;
GetHookOffset();
if(!g_bHookKeUpdateSystemTime)
{
g_lpfnKeUpdateSystemTime = GetFuncAddress(L"KeUpdateSystemTime");
if(!g_lpfnKeUpdateSystemTime) return status;

lpOriginal = (POriginalFunction)ExAllocatePoolWithTag(NonPagedPool,sizeof(OriginalFunction),XSPEED_ORIGINAL_FUNCTION_TAG);
if(!lpOriginal) return status;
RtlFillMemory(lpOriginal,sizeof(OriginalFunction),0x90);
RtlCopyMemory(lpOriginal->OriginalHead,g_lpfnKeUpdateSystemTime,g_uHookOffsetKeUpdateSystemTime);
lpOriginal->jmpStub.jmp = 0xe9;
lpOriginal->jmpStub.offset = (unsigned long *)((unsigned char *)g_lpfnKeUpdateSystemTime + g_uHookOffsetKeUpdateSystemTime - (unsigned char *)(lpOriginal+1));

lpFake = (PFakeKeUpdateSystemTime)ExAllocatePoolWithTag(NonPagedPool,sizeof(FakeKeUpdateSystemTime),XSPEED_FAKE_UPDATE_TAG);
if(!lpFake) return status;
RtlZeroMemory(lpFake,sizeof(FakeKeUpdateSystemTime));
lpFake->mul = 0x25f7; //mul g_uSpeedX
lpFake->speedx = &g_uSpeedX;
lpFake->div = 0x35f7;//div g_uSpeedBase
lpFake->speedbase = &g_uSpeedBase;
lpFake->jmpStub.jmp = 0xe9;//jmp
lpFake->jmpStub.offset = (unsigned long *)((unsigned char *)lpOriginal - (unsigned char *)(lpFake+1));//

jmpStub.jmp = 0xe9;
jmpStub.offset = (unsigned long *)((unsigned char *)lpFake - (unsigned char *)g_lpfnKeUpdateSystemTime - sizeof(JmpStub));//

PageProtectOff();
Irql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory(g_lpfnKeUpdateSystemTime,&jmpStub,sizeof(JmpStub));
KeLowerIrql( Irql );
PageProtectOn();

g_FakeKeUpdateSystemTime = lpFake;
g_OriginalKeUpdateSystemTime = lpOriginal;
g_bHookKeUpdateSystemTime = TRUE;

status = STATUS_SUCCESS;
}

return status;
}

NTSTATUS HookKeQueryPerformanceCounter()
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
POriginalFunction lpOriginal = NULL;
JmpStub jmpStub = {0};
KIRQL Irql = 0;
unsigned char *lpKeQueryPerformanceCounter = (unsigned char *)KeQueryPerformanceCounter;

if(!g_bHookKeQueryPerformanceCounter)
{
// 初始化数值
g_liPreOriginalCounter.QuadPart = 0;
g_liPreReturnCounter.QuadPart = 0;

// 首次查询
g_liPreOriginalCounter = KeQueryPerformanceCounter( NULL );
g_liPreReturnCounter.QuadPart = g_liPreOriginalCounter.QuadPart;

lpOriginal = (POriginalFunction)ExAllocatePoolWithTag(NonPagedPool,sizeof(OriginalFunction),XSPEED_ORIGINAL_FUNCTION_TAG);
if(!lpOriginal) return status;
RtlFillMemory(lpOriginal,sizeof(OriginalFunction),0x90);
RtlCopyMemory(lpOriginal->OriginalHead,KeQueryPerformanceCounter,g_uHookOffsetKeQueryPerformanceCounter);
lpOriginal->jmpStub.jmp = 0xe9;
lpOriginal->jmpStub.offset = (unsigned long *)((unsigned char *)KeQueryPerformanceCounter + g_uHookOffsetKeQueryPerformanceCounter - (unsigned char *)(lpOriginal+1));

jmpStub.jmp = 0xe9;
jmpStub.offset = (unsigned long *)((unsigned char *)FakeKeQueryPerformanceCounter - (unsigned char *)KeQueryPerformanceCounter - sizeof(JmpStub));

PageProtectOff();
Irql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory(KeQueryPerformanceCounter,&jmpStub,sizeof(JmpStub));
KeLowerIrql( Irql );
PageProtectOn();

g_OriginalKeQueryPerformanceCounter = lpOriginal;
g_bHookKeQueryPerformanceCounter = TRUE;
status = STATUS_SUCCESS;
}

return status;
}

[/syntax]
AFAIK, if you make your time speed as 2x faster, you should correct system time every 2 seconds(actually, is 1 second).
hi,I have try this method you said,this method can reach my target to a certain degree,this will product some mistake of time,I think I need a other method to compute the right time,myid, can you give me some suggestion? this is my code :
Code: Select all
	SYSTEMTIME	sysTime = {0};
	__int64		time = 0;
	__int64		iCount = 0;
	GetLocalTime(&sysTime);
	time = SystemTimeToInt64(&sysTime);
	while(1)
	{
		Sleep(990);
		iCount ++;
		if(iCount % 6 == 0)
		{
			time+=10000000;
			Int64ToSystemTime(time,&sysTime);
		}
		SetLocalTime(&sysTime);
		printf("Int64Time 0x%llx:%I64d\r\n",time,time);
	}
 #26107  by pboy0922
 Thu Jun 18, 2015 1:47 am
cziter15 wrote:If you hook KeQueryPerformanceCounter, you can determine (by using PsGetCurrentProcess) which process called this function.
If PEPROCESS of your target is equal to value returned by PsGetCurrentProcess, then do your moddifications.
This covers only QueryPerformanceCounter API calls.

If you want to modify time returned by GetTickCount from kernelmode for a specific process, you need to hack pages and remap SharedUserData for specific process, then update values there manually. This will be not easy. You can also try to play with "on access" breakpoint.
hi,cziter15,the method you said is to filter the target process in the ring0,this is great! I will try it! The other method you said about SharedUserData is amateur for me, and I think this will be diffcult! :D
I have try another method what myid said,and the software in the attachment is also use the method to make system time normal, but the method it computes time with is better than my method,now I think If I get other good method to compute time on the status of speeder,I will solve this problem~ can you give me some suggestion~ :D
 #26110  by cziter15
 Thu Jun 18, 2015 9:13 am
To make things more clear, about SharedUserData.

This is a structure that contains informations about kernel version, tick counts, system time, debugger and more.
(see http://www.nirsoft.net/kernel_struct/vi ... _DATA.html)

KeUpdateSystemTime is called from interrupt dispatcher to dispatch Clock interrupt.
It fills SharedUserData.TickCount field with up-to-date tick count.

SharedUserData is mapped into every process, but this it is owned by kernel.
You can change PTE of a page to "redirect" SharedUserData in a specified process to another region of memory.
This will require you to copy and keep your copy of SharedUserData values up to date.

To hook GetTickCount it will be easier to do this from ring3.