Analysis of a vac module
FileSize: 58,880 Bytes
.text hash: 0x59DC680
Number of scans: 4
TimeDateStamp: 0x564B49A8h Tue Nov 17 15:37:12 2015
This is a vac module that was created a week and a half ago.
So far it seems to be an updated version of a module that I reversed months ago.
For anyone interested, I run lionsneck.com and use my knowledge of vac to keep my builds undetected.
This is the analysis of the first scan.
To begin, the function 0x1000361C is called passing the current object, the target process id, and a third paramter.
The scan begins by opening a handle to the target process with OpenProcess using the parameters PROCESS_QUERY_INFORMATION and PROCESS_VM_READ.
If it fails to open a handle it calls GetLastError and returns error code 61.
The scan then stores the process id into the current object and calls GetProcessTimes to get and then store the creation timestamp.
Next it gets a handle to Kernel32.dll and gets the proc address of IsWow64Process.
If successful, it calls IsWow64Process on the current process using GetCurrentProcess.
It checks if the current process is 32 bit and if the target process is 64 bit.
If the target process is 64 bit, it does the following actions using the undocumented 64 bit APIs:
Once past that, it calls NtQueryInformationProcess on the target process querying ProcessBasicInformation.
Then it stores the parent process id.
It checks if the PebBaseAddress is NULL.
If NULL, it queries NtQueryInformationProcess with the option ProcessImageFileName.
It then copies the image file name using widetochar_s and returns code 66.
If the PebBaseAddress is not NULL.
It uses ReadProcessMemory to read the PROCESS_EXECUTABLE_BLOCK structure from the target process.
Using the PEB it then uses ReadProcessMemory to read the ProcessParameters from the target process as well.
Using ReadProcessMemory it copies the Working Directory, Executable Path, and the Command Line from the target process.
After it finishes copying all the strings above, it reads the LoaderData using the PEB.
An interesting part is how they read the modules.
The undocumented version of the PEB_LDR_DATA contains three LIST_ENTRY structures while the MSDN version of the structure only contains one.
MSDN
Undocumented:
The developers of this module are indirectly accessing the InLoadOrderModuleList by passing a negative index to the InMemoryOrderLinks array.
You can tell they are using the MSDN version of the structure since they pass 72 bytes to ReadProcessMemory rather than the actual size
which is 80 bytes.
The InLoadOrderModuleList pointers happen to exist right above InMemoryOrderModuleList in memory, but InInitializationOrderModuleList does not exist after InMemoryOrderModuleList.
Continuing on.
They read up to a maxmimum of 1,000 modules in the format.
It copies FullDllName, DllBase, and even assigns the target process id into what seems like the string buffer.
It uses HeapAlloc and HeapReAlloc to increase the size of the VAC_MODULE_ENTRY array with every loop.
Once it reaches 1,000 modules, hits a null Flink, or the Flink points to the first entry.
It stops copying the modules and the scan is finished.
I hope this was helpful to anyone else that is reversing VAC.
FileSize: 58,880 Bytes
.text hash: 0x59DC680
Number of scans: 4
TimeDateStamp: 0x564B49A8h Tue Nov 17 15:37:12 2015
This is a vac module that was created a week and a half ago.
So far it seems to be an updated version of a module that I reversed months ago.
For anyone interested, I run lionsneck.com and use my knowledge of vac to keep my builds undetected.
This is the analysis of the first scan.
To begin, the function 0x1000361C is called passing the current object, the target process id, and a third paramter.
The scan begins by opening a handle to the target process with OpenProcess using the parameters PROCESS_QUERY_INFORMATION and PROCESS_VM_READ.
If it fails to open a handle it calls GetLastError and returns error code 61.
The scan then stores the process id into the current object and calls GetProcessTimes to get and then store the creation timestamp.
Next it gets a handle to Kernel32.dll and gets the proc address of IsWow64Process.
If successful, it calls IsWow64Process on the current process using GetCurrentProcess.
It checks if the current process is 32 bit and if the target process is 64 bit.
If the target process is 64 bit, it does the following actions using the undocumented 64 bit APIs:
Code
NtWow64ReadVirtualMemory64
NtWow64QueryInformationProcess64Once past that, it calls NtQueryInformationProcess on the target process querying ProcessBasicInformation.
Then it stores the parent process id.
It checks if the PebBaseAddress is NULL.
If NULL, it queries NtQueryInformationProcess with the option ProcessImageFileName.
It then copies the image file name using widetochar_s and returns code 66.
If the PebBaseAddress is not NULL.
It uses ReadProcessMemory to read the PROCESS_EXECUTABLE_BLOCK structure from the target process.
Using the PEB it then uses ReadProcessMemory to read the ProcessParameters from the target process as well.
Using ReadProcessMemory it copies the Working Directory, Executable Path, and the Command Line from the target process.
After it finishes copying all the strings above, it reads the LoaderData using the PEB.
An interesting part is how they read the modules.
The undocumented version of the PEB_LDR_DATA contains three LIST_ENTRY structures while the MSDN version of the structure only contains one.
MSDN
Code
typedef struct _PEB_LDR_DATA {
BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;Undocumented:
Code
typedef struct _PEB_LDR_DATA_EXTENDED{
DWORD dwLength;
DWORD dwInitialized;
DWORD SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA_EXTENDED;The developers of this module are indirectly accessing the InLoadOrderModuleList by passing a negative index to the InMemoryOrderLinks array.
Code
&LdrDataTableEntry.InMemoryOrderLinks.Flink[-1]You can tell they are using the MSDN version of the structure since they pass 72 bytes to ReadProcessMemory rather than the actual size
which is 80 bytes.
The InLoadOrderModuleList pointers happen to exist right above InMemoryOrderModuleList in memory, but InInitializationOrderModuleList does not exist after InMemoryOrderModuleList.
Continuing on.
They read up to a maxmimum of 1,000 modules in the format.
Code
// (sizeof=0x224)
typedef struct _VAC_MODULE_ENTRY {
char field_0[20],
DWORD dwDllBase,
DWORD dwSomeSize,
DWORD dwDllBase2,
char szImageName[256],
char szFullDllName[260]
} VAC_MODULE_ENTRY;It copies FullDllName, DllBase, and even assigns the target process id into what seems like the string buffer.
It uses HeapAlloc and HeapReAlloc to increase the size of the VAC_MODULE_ENTRY array with every loop.
Once it reaches 1,000 modules, hits a null Flink, or the Flink points to the first entry.
It stops copying the modules and the scan is finished.
I hope this was helpful to anyone else that is reversing VAC.


