Hiding malware in Windows – The basics of code injection

In2an3_PpG

Level 18
Thread author
Verified
Top Poster
Content Creator
Well-known
Nov 15, 2016
867
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

The ending i got a kick out of.

"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."

Hiding malware in Windows – The basics of code injection
 

Moonhorse

Level 37
Verified
Top Poster
Content Creator
Well-known
May 29, 2018
2,605
If you have malware hiding in windows, that means youre infected right? If it already has happened, i dont think many of average joes have common sense / basics to do anything in that case

And how common even is that hiding malware, when using proper antivirus software, its more like adware that will hide for long time
 
E

Eddie Morra

A better method would be to not create or manually hijack an existing thread in the targeted process at all.

I can tell you right now that most vendors who have behavioural protections are going to be relying on USER-MODE hooking for routines like NtCreateThreadEx (NTDLL), NtQueueApcThread (NTDLL), NtQueueApcThreadEx (NTDLL), etc.

1. NtAllocateVirtualMemory to allocate memory for your shell-code.
2. NtWriteVirtualMemory to write your shellcode to the allocated memory.
3. Now patch a routine under the virtual memory of the target process which you KNOW is going to be called sooner or later to redirect to the shell-code you allocated.
3. After your shell-code is executed, return back to the original address where you redirected from after cleaning up and removing the patch on the innocent routine.

This way, you get your shell-code in the target process, but it automatically hijacks a thread for you and redirects to your injected code. No need to create a new thread remotely, hijack an existing thread by changing the context or queuing an APC (if the thread is alertable), etc.

If you use direct system calls for those NtAllocateVirtualMemory and NtWriteVirtualMemory calls, then everything becomes more complicated, because on traditional systems... AV solutions aren't going to be able to do anything about it. The best they are going to be able to do without hardware-assisted virtualization is user-mode hooking for those routines (most AVs with behavioural protections are patching both of those routines btw), but a direct system call will bypass those hooks. And thus without hardware-assisted virtualization, since traditional systems are on Windows x64, they aren't going to be able to intercept those virtual memory operations from kernel-level to even know about what you did.

However, if you're doing things like creating a remote thread, that can be caught from a kernel-level via a documented and officially supported technique... kernel-mode callbacks!

One more note, it may be obvious if you are opening a handle to a process like explorer.exe, because such can be identified from a kernel-level as well. Most AVs are using ObRegisterCallbacks for self-protection purposes, but it can be used to watch out for which processes are trying to access other processes (for whatever reason).

Obviously I am not condoning malware development, but good people view this place who clearly have an interest in these topics, so if I don't share these tips... who will?
 
E

Eddie Morra

One more note, it may be obvious if you are opening a handle to a process like explorer.exe, because such can be identified from a kernel-level as well. Most AVs are using ObRegisterCallbacks for self-protection purposes, but it can be used to watch out for which processes are trying to access other processes (for whatever reason).
In fact, I recommend all AV vendors to do the following.

1. Use ObRegisterCallbacks to monitor for process handle/duplication attempts on sensitive processes like explorer.exe.
2. Remove the access rights for flags like PROCESS_VM_OPERATION. Only grant access for things like query information.
3. Now it makes everything tougher because it is going to be a pain trying to RCE into the process without sufficient rights to do things like virtual memory operations, thread creation, etc.

Same applies to the process's threads to block the ability for hijacking it via APC and what-not.
 

In2an3_PpG

Level 18
Thread author
Verified
Top Poster
Content Creator
Well-known
Nov 15, 2016
867
In fact, I recommend all AV vendors to do the following.

1. Use ObRegisterCallbacks to monitor for process handle/duplication attempts on sensitive processes like explorer.exe.
2. Remove the access rights for flags like PROCESS_VM_OPERATION. Only grant access for things like query information.
3. Now it makes everything tougher because it is going to be a pain trying to RCE into the process without sufficient rights to do things like virtual memory operations, thread creation, etc.

Same applies to the process's threads to block the ability for hijacking it via APC and what-not.

Now for typical users, wouldn't default deny or SUA be key in preventing this type?
 
E

Eddie Morra

Now for typical users, wouldn't default deny or SUA be key in preventing this type?
If you're using SRP like AppGuard then you have features like memory protection which can be useful but SUA won't be handy for things like preventing RCE into processes like explorer.exe because explorer.exe is standard rights. :)

SUA will only really help in terms of preventing code injection if the launcher isn't allowed to run as admin (e.g. you don't go through the UAC prompt so it either refuses to run or runs as standard rights) but wants to target an elevated process. In that case, the process handle acquisition will be refused by the Windows kernel due to integrity level of the caller and the target.

The way it works is you cannot open a handle to a process at a higher integrity as you. You'd have to somehow escalate your privileges officially (e.g. through UAC procedure) or with an privilege escalation exploit.
 

In2an3_PpG

Level 18
Thread author
Verified
Top Poster
Content Creator
Well-known
Nov 15, 2016
867
If you're using SRP like AppGuard then you have features like memory protection which can be useful but SUA won't be handy for things like preventing RCE into processes like explorer.exe because explorer.exe is standard rights. :)

SUA will only really help in terms of preventing code injection if the launcher isn't allowed to run as admin (e.g. you don't go through the UAC prompt so it either refuses to run or runs as standard rights) but wants to target an elevated process. In that case, the process handle acquisition will be refused by the Windows kernel due to integrity level of the caller and the target.

The way it works is you cannot open a handle to a process at a higher integrity as you. You'd have to somehow escalate your privileges officially (e.g. through UAC procedure) or with an privilege escalation exploit.

Thanks.

Figured id ask the question in case anyone else was thinking the same thing.
 
E

Eddie Morra

Note, since Windows 8.1 you can optionally opt-in to make lsass.exe a protected process. This is a really good idea by the way because lsass.exe has been attacked before for credential theft.

Configuring Additional LSA Protection

I recommend to everyone to do this but only if you understand how it works to prevent potential problems.

It should be mentioned that to attack lsass.exe normally, you'd need to be running with elevation anyway... and thus if you had administrator rights, you could always forcefully disable the protection via registry modification, but it is unlikely this would happen in the real world anyway. I've never seen a malware sample actually do this in the wild yet.
 

In2an3_PpG

Level 18
Thread author
Verified
Top Poster
Content Creator
Well-known
Nov 15, 2016
867
Note, since Windows 8.1 you can optionally opt-in to make lsass.exe a protected process. This is a really good idea by the way because lsass.exe has been attacked before for credential theft.

Configuring Additional LSA Protection

I recommend to everyone to do this but only if you understand how it works to prevent potential problems.

It should be mentioned that to attack lsass.exe normally, you'd need to be running with elevation anyway... and thus if you had administrator rights, you could always forcefully disable the protection via registry modification, but it is unlikely this would happen in the real world anyway. I've never seen a malware sample actually do this in the wild yet.

ASR rule "Block credential stealing from the Windows local security authority subsystem (lsass.exe)" would work for this?
 
E

Eddie Morra

ASR rule "Block credential stealing from the Windows local security authority subsystem (lsass.exe)" would work for this?
I haven't looked into that ASR rule yet but I definitely will when I have time to. I have a lot more rules to look into...

I was under the impression that ASR only covers scripts (e.g. VBScript, PowerShell (?), Office VBA Macro's). If this is the case, then that ASR rule won't work against Win32-based malware trying to attack lsass.exe for credential theft. I've only ever tested ASR with Office VBA Macro's though, so I do not know the true extent of how far it covers.

If you opt-in for the additional Windows 8.1+ lsass.exe protection, you will definitely be covered across everything from Win32-based malware to scripts, etc. though. :)

One thing to note is that with the normal opt-in lsass.exe protection, you aren't going to be notified if an attack is blocked, because the attack will never reach a stage where it is definite that lsass.exe was being attacked for credential theft - so even if you are infected and the credential theft attack is blocked, you may stay unaware of the infection. However, if the attacker falls under the scope of ASR and the ASR rule you mentioned intervenes, you can be notified about the attack attempt by Windows Defender (I've seen Windows Defender notify during testing of my own POCs for the prevention of code injection attacks).
 
E

Eddie Morra

I will have to learn more about the extra protection for lsass.exe. Thanks for the info.
Me and you both hahahaha

I know the opt-in registry setting once enabled and the changes are made after the reboot will cause the Windows kernel to protect lsass.exe. Essentially, it will be set as a protected process (either normal protected process or protected process light) and this is tracked in opaque kernel-mode structures.

When you try to open a handle to lsass.exe, eventually you'll reach undocumented and non-exported kernel routines which continue this handle opening operation. They'll see lsass.exe is identified as a protected process and block the operation, passing back an error code that lets the original caller know that the access was denied.

The ASR rule though? No idea how it protects lsass.exe yet. When I look into that rule soon, I can try and debunk how it works... I guess trying is better than nothing. I doubt there will be any official or public documentation which explains the internals of it, so you pretty much have to go down reverse-engineer galore.
 

shmu26

Level 85
Verified
Honorary Member
Top Poster
Content Creator
Well-known
Jul 3, 2015
8,153
If you have malware hiding in windows, that means youre infected right?
If malware is running, then yes, technically you are infected, but it still might not be gameover, because it is possible that the malware is not succeeding to do what it wants. The final stages might be stopped by firewall, behavior blocker, or AV detection of the payload if and when it is spawned. Or maybe it just doesn't run properly on your version of Windows, like with other software.
 
E

Eddie Morra

I've seen it many times with malware where the payload on conclusion doesn't perform as coded correctly and just does nothing. If this happens it can pot luck. :p
It's pretty common with old samples which rely on a Command and Control (C&C) server because those servers get taken down quite quickly and then the sample won't work properly. Even if the malware author doesn't take down the C&C, if the malware spreads far enough, you can bet legal action will be taken by authorities to try and take it down by force (or illegally without permission by cyber-vigilantes).

Then there's the case of certain features being disabled on the machine, or limitations depending on the environment. For example, a sample might be dependent on a feature which isn't installed on the machine or is disabled, and may not have a backup plan (such as force-enabling or installing the required feature). An example of this would be where a malicious campaign is pushed out to an organisation which relies on a macro in an office document, but macro's are disabled on the environment and cannot be enabled by the business customer, only the administration team. Another example would be a newer version of the .NET Framework being used, or the VC++ run-time not being statically linked, while different versions are available on the environment.

This is why it can be stressful when testers are using old samples and are testing the product you helped build in a team for several years, only for it to look bad because while it looks like the samples got through, not all of the samples being used were actually doing anything malicious on the environment. This is why it is pretty crucial for testing to be performed with samples which have been checked within a reasonable time-frame of the test, preferably the test being carried out by someone who is equipped to handle such analysis. Albeit such is rare unless it is a professional testing organisation, which is completely understandable in my humble opinion.

I know this is a bit off-topic, but it just seemed like the perfect opportunity to note that.
 
L

Local Host

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
Pretty much what I been saying for years, I did that myself years ago for testing purposes, the same method wouldn't work on Windows due to all the Security APIs built into the System.
On Windows it requires more time and effort, while on Linux it takes 5 min (yet the majorly of people still believe Linux is safer than Windows).
 

Andy Ful

From Hard_Configurator Tools
Verified
Honorary Member
Top Poster
Developer
Well-known
Dec 23, 2014
8,119
...
The ASR rule though? No idea how it protects lsass.exe yet. When I look into that rule soon, I can try and debunk how it works... I guess trying is better than nothing. I doubt there will be any official or public documentation which explains the internals of it, so you pretty much have to go down reverse-engineer galore.
Most people interested in ASR, would appreciate if you could reverse-engineer the rule:
"Block executable files from running unless they meet a prevalence, age, or trusted list criteria".
No one knows if or how it can be configured.:notworthy:
 

About us

  • MalwareTips is a community-driven platform providing the latest information and resources on malware and cyber threats. Our team of experienced professionals and passionate volunteers work to keep the internet safe and secure. We provide accurate, up-to-date information and strive to build a strong and supportive community dedicated to cybersecurity.

User Menu

Follow us

Follow us on Facebook or Twitter to know first about the latest cybersecurity incidents and malware threats.

Top