⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 29a-7.024

📁 从29A上收集的病毒源码
💻 024
📖 第 1 页 / 共 4 页
字号:
		OUT PHANDLE KeyHandle,
		IN ACCESS_MASK DesiredAccess,
		IN POBJECT_ATTRIBUTES ObjectAttributes
	);

	KeyHandle is a handle of superordinate key. We will use the value from 
NtEnumerateKey for it. DesiredAccess are access rights. KEY_ENUMERATE_SUB_KEYS 
is the right value for it. ObjectAttributes describes subkey which we want to 
open (including its name).

	#define KEY_ENUMERATE_SUB_KEYS 8

	If the result of NtOpenKey is 0 opening was successful which mean this 
key from our list exists. Opened key should be closed via NtClose.

	NTSTATUS NtClose(
		IN HANDLE Handle
	);

	
	For each call of NtEnumareteKey we will count the shift as a number of 
keys from our list which exist in the given part of registry. Then we will add 
this shift to Index argument and finally call the original NtEnumerateKey.
	For getting name of the key specified by Index we will use the value 
KeyBasicInformation as a KeyInformationClass.

	#define KeyBasicInformation 0

	NtEnumerateKey returns this structure in KeyInformation:

	typedef struct _KEY_BASIC_INFORMATION {
		LARGE_INTEGER LastWriteTime;
		ULONG TitleIndex;
		ULONG NameLength;
		WCHAR Name[1];            
	} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;

	Only thing we need here is Name and its length NameLength.
	If there is no entry for shifted Index we will return error 
STATUS_EA_LIST_INCONSISTENT.

	#define STATUS_EA_LIST_INCONSISTENT 0x80000014


=====[ 5.2 NtEnumerateValueKey ]================================================

	Registry values are not alphabetically sorted. Luckily the number 
of values in one key is quite small, so we can use recall method to get 
the shift. API for getting info about one value is called NtEnumerateValueKey.

	NTSTATUS NtEnumerateValueKey(
		IN HANDLE KeyHandle,
		IN ULONG Index,
		IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
		OUT PVOID KeyValueInformation,
		IN ULONG KeyValueInformationLength,
		OUT PULONG ResultLength
	);

	KeyHandle is again a handle of superordinate key. Index is an index 
to the list of values in given key. KeyValueInformationClass describes a type 
of information which will be stored into KeyValueInformation buffer which 
is long KeyValueInformationLength bytes. Number of written bytes is returned 
in ResultLength.
	Again we have to count the shift but according to the number of values 
in one key we can recall this function for all indexes from 0 to Index. 
The name of the value can be get when KeyValueInformationClass is set to 
KeyValueBasicInformation.
	
	#define KeyValueBasicInformation 0


	Then we will get following structure in KeyValueInformation buffer: 

	typedef struct _KEY_VALUE_BASIC_INFORMATION {
		ULONG TitleIndex;
		ULONG Type;
		ULONG NameLength;
		WCHAR Name[1];
	} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;

	Again we are interested only in Name and NameLength.

	
	If there is no entry for shifted Index we will return error 
STATUS_NO_MORE_ENTRIES.

	#define STATUS_NO_MORE_ENTRIES 0x8000001A



=====[ 6. System services and drivers ]=========================================

	System services and drivers are enumerated by four independent API
functions. Their connections is different in each Windows version. That's why 
we have to hook all four functions. 

	BOOL EnumServicesStatusA(
		SC_HANDLE hSCManager,
		DWORD dwServiceType,
		DWORD dwServiceState,
		LPENUM_SERVICE_STATUS lpServices,
		DWORD cbBufSize,
		LPDWORD pcbBytesNeeded,
		LPDWORD lpServicesReturned,
		LPDWORD lpResumeHandle
	);

	BOOL EnumServiceGroupW(
		SC_HANDLE hSCManager,
		DWORD dwServiceType,
		DWORD dwServiceState,
		LPBYTE lpServices,
		DWORD cbBufSize,
		LPDWORD pcbBytesNeeded,
		LPDWORD lpServicesReturned,
		LPDWORD lpResumeHandle,
		DWORD dwUnknown
	);

	BOOL EnumServicesStatusExA(
		SC_HANDLE hSCManager,
		SC_ENUM_TYPE InfoLevel,
		DWORD dwServiceType,
		DWORD dwServiceState,
		LPBYTE lpServices,
		DWORD cbBufSize,
		LPDWORD pcbBytesNeeded,
		LPDWORD lpServicesReturned,
		LPDWORD lpResumeHandle,
		LPCTSTR pszGroupName
	);

	BOOL EnumServicesStatusExW(
		SC_HANDLE hSCManager,
		SC_ENUM_TYPE InfoLevel,
		DWORD dwServiceType,
		DWORD dwServiceState,
		LPBYTE lpServices,
		DWORD cbBufSize,
		LPDWORD pcbBytesNeeded,
		LPDWORD lpServicesReturned,
		LPDWORD lpResumeHandle,
		LPCTSTR pszGroupName
	);


	The most important here is lpServices which points on the buffer where 
the list of services would be stored. And also lpServicesReturned pointing on 
the number of records in result is important. Structure of data in the output 
buffer depends on the type of function. For functions EnumServicesStatusA 
and EnumServicesGroupW is returned structure 

	typedef struct _ENUM_SERVICE_STATUS {
		LPTSTR lpServiceName;
		LPTSTR lpDisplayName;
		SERVICE_STATUS ServiceStatus;
	} ENUM_SERVICE_STATUS, *LPENUM_SERVICE_STATUS;

	typedef struct _SERVICE_STATUS {
		DWORD dwServiceType;
		DWORD dwCurrentState;
		DWORD dwControlsAccepted;
		DWORD dwWin32ExitCode;
		DWORD dwServiceSpecificExitCode;
		DWORD dwCheckPoint;
		DWORD dwWaitHint;
	} SERVICE_STATUS, *LPSERVICE_STATUS;

for EnumServicesStatusExA a EnumServicesStatusExW it it

	typedef struct _ENUM_SERVICE_STATUS_PROCESS {
		LPTSTR lpServiceName;
		LPTSTR lpDisplayName;
		SERVICE_STATUS_PROCESS ServiceStatusProcess;
	} ENUM_SERVICE_STATUS_PROCESS, *LPENUM_SERVICE_STATUS_PROCESS;

	typedef struct _SERVICE_STATUS_PROCESS {
		DWORD dwServiceType;
		DWORD dwCurrentState;
		DWORD dwControlsAccepted;
		DWORD dwWin32ExitCode;
		DWORD dwServiceSpecificExitCode;
		DWORD dwCheckPoint;
		DWORD dwWaitHint;
		DWORD dwProcessId;
		DWORD dwServiceFlags;
	} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;


	We are interested only in lpServiceName which is the name of system 
service. Records have static size, so if we want to hide one we will move all 
following records by its size. Here we have to differentiate between the size 
of SERVICE_STATUS and SERVICE_STATUS_PROCESS.



=====[ 7. Hooking and spreading ]===============================================

	To get the desiderative efect we have to hook all running processes 
and also all processes which would be created later. New processes should be 
hooked before running their first instruction of their own code otherwise 
they would be able to see our hidden objects in the time before they would be 
hooked.
	

=====[ 7.1 Rights ]=============================================================

	At first it is good to know that we need at least administrators rights
to get access to all running processes. The best possibility is to run our 
process as system service which run on user SYSTEM. To install the service we 
also need special rights. 
	Also getting SeDebugPrivilege is very useful. This can be done using 
API OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges.

	BOOL OpenProcessToken(
		HANDLE ProcessHandle,
		DWORD DesiredAccess,
		PHANDLE TokenHandle
	);

	BOOL LookupPrivilegeValue(
		LPCTSTR lpSystemName,
		LPCTSTR lpName,
		PLUID lpLuid
	);

	BOOL AdjustTokenPrivileges(
		HANDLE TokenHandle,
		BOOL DisableAllPrivileges,
		PTOKEN_PRIVILEGES NewState,
		DWORD BufferLength,
		PTOKEN_PRIVILEGES PreviousState,
		PDWORD ReturnLength
	);


	Neglecting errors the code can look like this:

	#define SE_PRIVILEGE_ENABLED	0x0002
	#define TOKEN_QUERY		0x0008
	#define TOKEN_ADJUST_PRIVILEGES	0x0020

	HANDLE hToken;
	LUID DebugNameValue;
	TOKEN_PRIVILEGES Privileges;
	DWORD dwRet;

	OpenProcessToken(GetCurrentProcess(),
			 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,hToken);
	LookupPrivilegeValue(NULL,"SeDebugPrivilege",&DebugNameValue);
	Privileges.PrivilegeCount=1;
	Privileges.Privileges[0].Luid=DebugNameValue;
	Privileges.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
	AdjustTokenPrivileges(hToken,FALSE,&Privileges,sizeof(Privileges),
			      NULL,&dwRet);
	CloseHandle(hToken);


=====[ 7.2 Global hook ]========================================================

	Enumeration of processes is done by already metioned API function 
NtQuerySystemInformation. There are few native processes in the system, so we 
will use the method of rewriting first instructions of the function to hook 
them. For each running process we will do the same. We will allocate a part 
of memory in target process where we will write our new code for functions 
we want to hook. Then we will change the first five bytes of these functions 
with jmp instruction. This jump will redirect the execution to our code. 
So the jmp instruction will be executed immediately when the hooked function is 
called. We have to save first instructions of each function which is rewritten. 
We need them to call original code of the hooked function. Saving instructions 
is described in chapter 3.2.3 in the document "Hooking Windows API".
	At first we have to open target process via NtOpenProcess and get 
the handle. This will fail if we don't have enough rights. 

	NTSTATUS NtOpenProcess(
		OUT PHANDLE ProcessHandle,
		IN ACCESS_MASK DesiredAccess,
		IN POBJECT_ATTRIBUTES ObjectAttributes,
		IN PCLIENT_ID ClientId OPTIONAL
	);

	ProcessHandle is a pointer on a handle where the result will be stored. 
DesiredAccess should be set on PROCESS_ALL_ACCESS. We will set PID of target 
process to UniqueProcess part of ClientId structure, UniqueThread should be 0.
Open handle can be always closed via NtClose.

	#define PROCESS_ALL_ACCESS 0x001F0FFF

	Now we are going to allocate the part of memory for our code. This can 
be done using NtAllocateVirtualMemory.

	NTSTATUS NtAllocateVirtualMemory(
		IN HANDLE ProcessHandle,
		IN OUT PVOID BaseAddress,
		IN ULONG ZeroBits,
		IN OUT PULONG AllocationSize,
		IN ULONG AllocationType,
		IN ULONG Protect
	);

	ProcessHandle is the one from NtOpenProcess. BaseAddress is a pointer 
on a pointer on the beginning where we want to allocate. Here will be stored 
the address of the allocated memory. Input value can be NULL. AllocationSize 
is a pointer on number of bytes we want to allocate. And again it is also used 
as output value for the real number of allocated bytes. It is good to set 
AllocationType to MEM_TOP_DOWN in addition to MEM_COMMIT because the memory 
would be allocated on the highest possible address near DLLs.

	#define MEM_COMMIT	0x00001000
	#define MEM_TOP_DOWN	0x00100000	


	Then we can write our code there using NtWriteVirtualMemory.

	NTSTATUS NtWriteVirtualMemory(
		IN HANDLE ProcessHandle,
		IN PVOID BaseAddress,
		IN PVOID Buffer,
		IN ULONG BufferLength,
		OUT PULONG ReturnLength OPTIONAL
	);

	BaseAddress will be that address returned by NtAllocateVirtualMemory. 
Buffer points on bytes we want to write, BufferLength is number of bytes we 
want to write.

	Now we have to hook single functions. Only library which is loaded to 
all processes is ntdll.dll. So we have to check if function we want to hook is 
imported to the process if it is not from ntdll.dll. But the memory where would 
this function (from another DLL) be could be allocated, so rewriting bytes on 
its address could easily cause error in target process. This is why we have to 
check whether library (where function we want to hook is) is loaded to target 
process.
        We need to get PEB (Process Environment Block) of target process via 
NtQueryInformationProcess.

	NTSTATUS NtQueryInformationProcess(
		IN HANDLE ProcessHandle,
		IN PROCESSINFOCLASS ProcessInformationClass,
		OUT PVOID ProcessInformation,
		IN ULONG ProcessInformationLength,
		OUT PULONG ReturnLength OPTIONAL
	);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -