Hiding malware in Windows – The basics of code injection

By PRDeving

There are hundreds of teams working professionally trying to break into any single digital device to compromise sensitive data leaving no fingerprint.Malware industry is bigger than you might think, more than 4,000 ransom-ware attacks have occurred every day since the beginning of 2016 and much more general system vulnerations.

I remember viruses in Win95 where you got a freeze screen, a broken OS or a BSOD; that’s not the case anymore, today access violations leave no trace and usually patches the vulnerabilities behind them so no other malware can take control of the system.
A guy i met once told me that “the best AV you could ever have is a harmless virus”, I don’t fully agree, but that sentence hides a bit of truth.

But, how can this malicious code run freely in a host machine without the user noticing?

If you have been following this site you have probably read this article where I explain how a key-logger can be written for Linux with almost no effort, and maybe this other where i explain briefly how a malicious program can be camouflaged for windows. Well, those where dirty cheap examples on how this can be achieved, hooking to system buffers, hiding the process or changing the icon, but in real world it’s not that straight forward.

The first thing an attacker has to be sure is that the user can not notice a weird process running in his machine, in the examples above you can use the task manager and kill the process, can you imagine how useful would a ransom-ware be if that can be done? exactly, no attacker wants that.

Desktop screenshot (18).png

Wouldn’t you kill this immediately?

There are ways to hide this but that’s not the idea, any single process running in a machine can be spotted, might be easy, might be hard, but they will eventually be. In order to do this, code has to be run without starting a process, here’s where code injections comes to play.

When you run a binary the content is copied to the RAM and the OS starts an execution thread on the memory block that holds the entry point of it, then, the instruction is executed and that “pointer” moves to the next address, doing this until the instruction set ends and the process is killed. (i know, it’s not that simple)

A process can ask for a new thread, this means that a new sub-process will be created executing instructions in any address we want, without interfering with the main process execution. By doing this we could have different blocks inside the same RAM region (the part that belongs to the process) being run at the same time.

If we manage to inject our malicious code into a legit process memory and then run a thread in that same process pointing to our bad-code entry code, the exploit that we wanted to run would be running inside the legit process in a thread. This is exactly what code injection is.

And you might be wondering, are threads listed? well, yes, threads are listed but you can’t know exactly what a thread is doing unless you get a dump of the memory and find what is it executing, so it’s “safe” enough.

Cool right? How do we do this then?

Well, we will need to allocate our malicious code into the legit process memory and then start a thread to the entry point. But windows memory regions are protected, what means that usually only the same process can interact with it. There are exceptions, bypasses and other ways to do it but let’s stick to the theory to make it easier.

Obviously we can’t do anything directly on the process itself but windows architecture has a concept called HANDLE. Handles are permissions identifiers that the OS keeps in a table and grants access to processes, any application can ask for those permissions and, if given, the owner of the handle can perform some actions inside the final process, like reading or writing memory, running threads or listing the modules.

With this in mind, what we need is:

1- get a handle to a legit application or OS service 2- allocate some memory for our exploit 3 – write the malicious code into that buffer

4- launch a thread in the victim process to the malicious code’s entry point

Let’s code a little.

HANDLE handle, snapshot;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32); snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Process32First(snapshot, &pe32);
do { if (strcmp(pe32.szExeFile, "explorer.exe") == 0) { handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); if (handle != NULL) break; }
} while(Process32Next(snapshot, &pe32));
CloseHandle(snapshot);

Great, with this code we are listing the processes running in the machine and seeing if the process is “explorer.exe” in this case, and as you can see, using OpenProcess we are retrieving the handle to the “explorer.exe” process and saving it in a variable. If everything goes as planed we will have access to the process with ALL_ACCESS permissions granted.
From now on we can act in explorer.exe memory region.

Let’s define an injectable.

typedef struct { int data;
} ARGUMENTS, *PARGUMENTS; DWORD WINAPI Injectable(PVOID p) { ARGUMENTS *args = (ARGUMENTS*)p; // this is the code that will be run by the thread
}
DWORD WINAPI InjectableEnd() { return 0; }

Here we are doing three important things, first, we are defining a way to pass information to the injected code from the launcher (remember that the launcher is a program itself, the launcher is the infected binary that the victim executed, most attacks starts this way).

After that, we have the block of injected code, anything in that block will be copied into the explorer.exe process memory region, meaning that for this code the global context will not be the launcher anymore, it will run in the explorer.exe one. It will have full access to memory inside it.

the last thing is the end of the block, it’s there so we can calculate the size of the injectable code easily.

Now we create a buffer into the target process memory and inject the arguments and the code there.

ARGUMENTS args;
args.data = 101; PVOID arguments_address = VirtualAllocEx(handle, NULL, sizeof(ARGUMENTS), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(handle,(PVOID)arguments_address, &args, sizeof(ARGUMENTS), NULL); PVOID code_address = VirtualAllocEx(handle, NULL, (DWORD)InjectableEnd - (DWORD)Injectable, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(handle,(PVOID)code_address, Injectable, (DWORD)InjectableEnd - (DWORD)Injectable, NULL);

As you can imagine (you can also just check MSDN) VirtualAllocEx is the extended function for VirtualAlloc that allows us to allocate memory in the target process identified by a handler.
In this case we are allocating a chunk with size ‘sizeof(ARGUMENTS)’ for the arguments struct and a chunk with size &injectableEnd – &injectable for the code, this is a subtraction of the addresses of each function, which gives the size in bytes.

After allocation, then we write the code and the struct directly to the buffer we just created. With this we have the exploit and the arguments already in the explorer.exe memory (in this case) and code_address and arguments_address are the location of the entry points.

So, having the code and the entry point we just have to start a thread to run the code.

HANDLE hThread;
hThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)code_address, (DWORD)arguments_address, 0, NULL);

After this line the target process will have a running thread executing the code we injected, that code could be a simple “hello world” MessageBox or a complex game cheat.

So, what do we have now? We have a legit OS process running our code, we have opened a handle to this process and started a thread, which can be detected, but there are some “stealth” ways to do it that we might discuss in further articles.
Now the user can’t identify easily if random code is running on the machine and, if the attacker manage to inject the code in a OS protected process like csrss.exe even AVs or ACs will struggle to identify it.

The important question is, can we be protected against this? well, most non-blacklisted applications can open handles without AV being notified so is not that easy, as always the conclusion is, don’t execute any binary on your machine coming from any random source, always check integrity for the programs you download, watch if random threads runs on processes that usually doesn’t use them, don’t give admin permissions to random applications, etc.

common sense.

As investigator I’d say that this kind of techniques opens doors beyond the simple hacking, you have potential access to the whole system, please, don’t be “that guy”, investigation, publications and proofs of concept are as fun as actual vulneration and doesn’t hurt anyone.

Hope it helps 😉