Background and Theory
Code and DLL injection refer to a method for attackers to manipulate programs and processes to execute another program. DLL injection provides a manner for attributing the malicious .dll to running processes. Processes are tasks that are being handled by the operating system. DLL are Dynamic Link Libraries, are shared code that may be executed by a running process. There are two kinds of injection: static and dynamic injection. Static injection occurs prior to program execution. Dynamic injection occurs when processes are loaded into memory.
What is DLL Injection?
Injection via Windows API
Injection via Debugging API
It would not be very interesting to us as an attacker if the moment we start to own the box, it became unusable. As a supplement to an application, we would not want to crash the original application either. This is the case for any injection attempt, but the ThreadContext() functions help us accomplish this easier.
The debugging features can be troublesome when you are actually trying to debug such an application, which as an attacker is a good thing for us. While analyzing, we will have to pick our battles and not set arbitrary breakpoints.
Static DLL Injection
Now we will explore Code Injection. Code Injection can occur in one of two ways, statically (Static Code Injection) or dynamically (Dynamic Code Injection). Static Code Injection occurs prior to execution. A file is altered, by 'injecting' a jump at the beginning of the filespace. This jump directs to arbitrary code written by an attacker into available space of the filespace of a program.
We will now explore static code injection. For this purpose, we will manipulate the Windows game Mineswipper so that before it runs it displays a message saying “I am a trojan.”
First, go to C:\WINDOWS\system32 and make a copy of winmine.exe into a file with a different name just in case (it is always good to have a security backup).
In order to manipulate winmine.exe, we will use OllyDbg, “a 32-bit assembler level analysing debugger for Microsoft Windows. Emphasis on binary code analysis makes it particularly useful in cases where source is unavailable.”
Run OllyDbg.exe from the folder in which you installed OllyDbg. The first time you run OllyDbg you might get a message asking you whether you want to update on the library (.dll) files. Just say no.
Click Open, and open winmine.exe. What you will get in OllyDbg is assembly code of winmine.exe (yes, this is reverse engineering).
On the right part of the screen you will see the Register values. The EIP register is a pointer to the next command that will execute. In this case it should store the Module Entry Point.
The memory space of winmine.exe contains a lot of useful information, but it also contains areas with no useful information whatsoever. These areas are full of noop operations (\x00's). These areas could be modified to add code without corrupting winmine.exe.
In OllyDbg, on the left upper window (right below the menu), scroll down until you find a big group of noops put together where you have enough space to add your code. The place you find is called a 'cave'.
Now in the 'cave' we found we will add a Message Box call.
The function call is:
MsgBoxA(0,”I am a Trojan”,”I am a Trojan”,0)
So this is the ASM code for doing that:
In Machine Code we go to an even lower level...we must allocate space for the “I am a Trojan” string and then push the address of this allocated memory by doing a
Push
We will now add the code. Highlight a bunch (about 20) of NOOPs from the cave. Right click and select Binary->Edit. Now on the Ascii field simply type in “I am a Trojan.”
You will now get some garbage on OllyDbg. Do not worry. Olly needs to reanalyze this code. Press CTRL + A to analyze the code. After this, you should see “I am a Trojan” in some address.
Now below the address where you added your string, double click on one of the “DB 00” fields. You will get an Assemble at window.
Type in:
push 0
and press Assembler. A new Assemble at will appear. Now type in:
push MYADDRESS
where MYADDRESS is the address where your string is located (your answer to Q1.5). In the next address you should type:
push MYADDRESS
again (because you are pushing the same string 2 times, once for the header of the box once for the message in the box). On the next address we type in:
push 0
again. Finally we have to call the actual function call, so on the next address type:
call user32.MessageBoxA
The Assemble window where you can input the instructions.
Now press the '*' key in your numpad, this will take you to the top of the window (the origin). Select the first 6 instructions, highlight them and then press CTRL + C (since we will be modifying some commands, we want to make sure we have a backup). Paste this code into notepad.
Now we will overwrite some code. Double click on the Origin instruction (the first instruction) and type in:
JMP CODEADDRESS
where CODEADDRESS is the address where your code starts.
You will notice that more than one line got edited. The edited lines will be in red. Compare the first few lines with your copy in Notepad and delete the lines that are duplicated from Notepad. The lines that are not duplicated we will need to add again somewhere.
It is important to keep this address because what the program will do is read the EIP register. This points to the line where we added the JMP. The JMP will redirect the PC to the new code. The new code will execute, and then we want to jump back to the address you just wrote down so that normal execution continues as if nothing had happened. However, before we return to normal execution, we have to add the code that we overwrote. So we add this at the end of our code before we jump back to the beginning of the code.
Go to back to the origin. Highlight the origin instruction, and right click with your mouse. Then press Follow. This will take you to the address to which the origin jumps. If you have done everything correctly so far, this should take you to the beginning of your code (The first push 0).
Now we need to add the code that was overwritten (you have it in your notepad, remember?). Add the remaining instructions from notepad at the end (immediately after the Call MessageBox command).
Note: If it says something like 'PUSH winmine.1234567' in notepad, just type in 'push 1234567').
Now at the last line of the new code insert the command
JMP SECONDADDRESS
where SECONDADDRESS is the address of the second line, or the line after the origin.
Now right click and go to Copy to executable -> All Modifications.
On the window that appears select:
Copy All
A new window will appear. Click yes to save modifications. Save as a different name (winminealtered.exe or something like that).
Now press Run (the play button at the top of Olly). The Message Box should have appeared and then Winmine.
We have already seen how static code injection works. The previous example could have been used to load a .dll library rather than displaying a message box. That would have been static dll injection.
Dynamic DLL Injection
Now we will look at a technique more widely used by trojans: dynamic dll injection.
Static injection occurred prior to program execution. Dynamic injection occurs after a program has been executed. After a program has been executed, a process is created in the operating system. When an attacker attempts to load code into the process memory space, then the attacker is using dynamic injection. When .dll libraries are loaded through dynamic injection, the process is known as dynamic dll injection.
So how are .dlls injected dynamically anyway?
Microsoft's Platform SDK provides some API calls to manipulate processes. Let's look at a couple of interesting ones. Their specifics are found in Appendix A. Make sure you read it and understand it before proceeding:
OpenProcess: opens an existing process object
LoadLibrary: maps the specified executable module into the address space of the calling process (yes, a .dll is a module)
VirtualAllocEx: reserves or commits a region of memory within the virtual address space of a specified process
WriteProcessMemory: writes data to an area of memory in a specified process. The entire area to be written to must be accessible, or the operation fails.
CreateRemoteThread: creates a thread that runs in the virtual address space of another process
So by now you should know that processes are not too hard to intrude.
A program, when executed, can dynamically load a dll module into a running process by doing the following:
Open a process using OpenProcess. One of the parameters is the Process ID which you can get from using PE from the previous section. Next, Allocate memory using VirtualAllocEx (one of the parameters of VirtualAllocEx will be the process opened by OpenProcess)
Write something into the memory space we allocated within the process. We will pass in the Process into which we want to write, the address of the memory into which we want to write (we already have all of those), the number of bytes to write, and a pointer to the DLL we want to load.
Now we will create a new thread which will call a function. The address of the function is the address of LoadLibrary and as parameters we pass the address of the memory we allocated...so the process will call the code we injected into the process. We do this using CreateRemoteThread and passing in the addresses.
_________________Code and DLL injection refer to a method for attackers to manipulate programs and processes to execute another program. DLL injection provides a manner for attributing the malicious .dll to running processes. Processes are tasks that are being handled by the operating system. DLL are Dynamic Link Libraries, are shared code that may be executed by a running process. There are two kinds of injection: static and dynamic injection. Static injection occurs prior to program execution. Dynamic injection occurs when processes are loaded into memory.
What is DLL Injection?
- Dynamically Linked Library
- Designed this way on purpose
- – Code reuse
– Modular programming
- Can be loaded into memory
- Can be called from another process
Injection via Windows API
- VirtualAllocEx()
- – Not for Windows 9x/Me
- ReadProcessMemory() and WriteProcessMemory()
- – All Versions of Windows
Injection via Debugging API
- GetThreadContext() and SetThreadContext()
- – Saves backup of registers
– Uses breakpoint to resume original
- Useful for returning control back to the host process gracefully
It would not be very interesting to us as an attacker if the moment we start to own the box, it became unusable. As a supplement to an application, we would not want to crash the original application either. This is the case for any injection attempt, but the ThreadContext() functions help us accomplish this easier.
The debugging features can be troublesome when you are actually trying to debug such an application, which as an attacker is a good thing for us. While analyzing, we will have to pick our battles and not set arbitrary breakpoints.
Static DLL Injection
Now we will explore Code Injection. Code Injection can occur in one of two ways, statically (Static Code Injection) or dynamically (Dynamic Code Injection). Static Code Injection occurs prior to execution. A file is altered, by 'injecting' a jump at the beginning of the filespace. This jump directs to arbitrary code written by an attacker into available space of the filespace of a program.
We will now explore static code injection. For this purpose, we will manipulate the Windows game Mineswipper so that before it runs it displays a message saying “I am a trojan.”
First, go to C:\WINDOWS\system32 and make a copy of winmine.exe into a file with a different name just in case (it is always good to have a security backup).
In order to manipulate winmine.exe, we will use OllyDbg, “a 32-bit assembler level analysing debugger for Microsoft Windows. Emphasis on binary code analysis makes it particularly useful in cases where source is unavailable.”
Run OllyDbg.exe from the folder in which you installed OllyDbg. The first time you run OllyDbg you might get a message asking you whether you want to update on the library (.dll) files. Just say no.
Click Open, and open winmine.exe. What you will get in OllyDbg is assembly code of winmine.exe (yes, this is reverse engineering).
On the right part of the screen you will see the Register values. The EIP register is a pointer to the next command that will execute. In this case it should store the Module Entry Point.
The memory space of winmine.exe contains a lot of useful information, but it also contains areas with no useful information whatsoever. These areas are full of noop operations (\x00's). These areas could be modified to add code without corrupting winmine.exe.
In OllyDbg, on the left upper window (right below the menu), scroll down until you find a big group of noops put together where you have enough space to add your code. The place you find is called a 'cave'.
Now in the 'cave' we found we will add a Message Box call.
The function call is:
MsgBoxA(0,”I am a Trojan”,”I am a Trojan”,0)
So this is the ASM code for doing that:
Push 0
Push “I am a Trojan”
Push “I am a Trojan”
Push 0
Call User32.MessageBoxA
Push “I am a Trojan”
Push “I am a Trojan”
Push 0
Call User32.MessageBoxA
In Machine Code we go to an even lower level...we must allocate space for the “I am a Trojan” string and then push the address of this allocated memory by doing a
Push
We will now add the code. Highlight a bunch (about 20) of NOOPs from the cave. Right click and select Binary->Edit. Now on the Ascii field simply type in “I am a Trojan.”
You will now get some garbage on OllyDbg. Do not worry. Olly needs to reanalyze this code. Press CTRL + A to analyze the code. After this, you should see “I am a Trojan” in some address.
Now below the address where you added your string, double click on one of the “DB 00” fields. You will get an Assemble at window.
Type in:
push 0
and press Assembler. A new Assemble at will appear. Now type in:
push MYADDRESS
where MYADDRESS is the address where your string is located (your answer to Q1.5). In the next address you should type:
push MYADDRESS
again (because you are pushing the same string 2 times, once for the header of the box once for the message in the box). On the next address we type in:
push 0
again. Finally we have to call the actual function call, so on the next address type:
call user32.MessageBoxA
The Assemble window where you can input the instructions.
Now press the '*' key in your numpad, this will take you to the top of the window (the origin). Select the first 6 instructions, highlight them and then press CTRL + C (since we will be modifying some commands, we want to make sure we have a backup). Paste this code into notepad.
Now we will overwrite some code. Double click on the Origin instruction (the first instruction) and type in:
JMP CODEADDRESS
where CODEADDRESS is the address where your code starts.
You will notice that more than one line got edited. The edited lines will be in red. Compare the first few lines with your copy in Notepad and delete the lines that are duplicated from Notepad. The lines that are not duplicated we will need to add again somewhere.
It is important to keep this address because what the program will do is read the EIP register. This points to the line where we added the JMP. The JMP will redirect the PC to the new code. The new code will execute, and then we want to jump back to the address you just wrote down so that normal execution continues as if nothing had happened. However, before we return to normal execution, we have to add the code that we overwrote. So we add this at the end of our code before we jump back to the beginning of the code.
Go to back to the origin. Highlight the origin instruction, and right click with your mouse. Then press Follow. This will take you to the address to which the origin jumps. If you have done everything correctly so far, this should take you to the beginning of your code (The first push 0).
Now we need to add the code that was overwritten (you have it in your notepad, remember?). Add the remaining instructions from notepad at the end (immediately after the Call MessageBox command).
Note: If it says something like 'PUSH winmine.1234567' in notepad, just type in 'push 1234567').
Now at the last line of the new code insert the command
JMP SECONDADDRESS
where SECONDADDRESS is the address of the second line, or the line after the origin.
Now right click and go to Copy to executable -> All Modifications.
On the window that appears select:
Copy All
A new window will appear. Click yes to save modifications. Save as a different name (winminealtered.exe or something like that).
Now press Run (the play button at the top of Olly). The Message Box should have appeared and then Winmine.
We have already seen how static code injection works. The previous example could have been used to load a .dll library rather than displaying a message box. That would have been static dll injection.
Dynamic DLL Injection
Now we will look at a technique more widely used by trojans: dynamic dll injection.
Static injection occurred prior to program execution. Dynamic injection occurs after a program has been executed. After a program has been executed, a process is created in the operating system. When an attacker attempts to load code into the process memory space, then the attacker is using dynamic injection. When .dll libraries are loaded through dynamic injection, the process is known as dynamic dll injection.
So how are .dlls injected dynamically anyway?
Microsoft's Platform SDK provides some API calls to manipulate processes. Let's look at a couple of interesting ones. Their specifics are found in Appendix A. Make sure you read it and understand it before proceeding:
OpenProcess: opens an existing process object
LoadLibrary: maps the specified executable module into the address space of the calling process (yes, a .dll is a module)
VirtualAllocEx: reserves or commits a region of memory within the virtual address space of a specified process
WriteProcessMemory: writes data to an area of memory in a specified process. The entire area to be written to must be accessible, or the operation fails.
CreateRemoteThread: creates a thread that runs in the virtual address space of another process
So by now you should know that processes are not too hard to intrude.
A program, when executed, can dynamically load a dll module into a running process by doing the following:
Open a process using OpenProcess. One of the parameters is the Process ID which you can get from using PE from the previous section. Next, Allocate memory using VirtualAllocEx (one of the parameters of VirtualAllocEx will be the process opened by OpenProcess)
Write something into the memory space we allocated within the process. We will pass in the Process into which we want to write, the address of the memory into which we want to write (we already have all of those), the number of bytes to write, and a pointer to the DLL we want to load.
Now we will create a new thread which will call a function. The address of the function is the address of LoadLibrary and as parameters we pass the address of the memory we allocated...so the process will call the code we injected into the process. We do this using CreateRemoteThread and passing in the addresses.
program DllInject;
uses
Windows;
var
PID, BytesWritten, Process, Thread, ThreadId: dword;
Paramaters: pointer;
DLL: pchar;
function xCreateRemoteThread(hProcess: dword; lpThreadAttributes: Pointer; dwStackSize: dword; lpStartAddress: Pointer; lpParameter: Pointer; dwCreationFlags: dword; lpThreadId: dword): dword; stdcall; external 'RT.dll';
function xVirtualAllocEx(hProcess: dword; lpAddress: Pointer; dwSize: dword; flAllocationType: dword; flProtect: dword): Pointer; stdcall; external 'RT.dll';
function xVirtualFreeEx(hProcess: dword; lpAddress: Pointer; dwSize: dword; dwFreeType: dword): boolean; stdcall; external 'RT.dll';
begin
DLL := 'c:\Inject\Library.dll'; //full path!
PID := 1784; //process id!
Process := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
Paramaters := xVirtualAllocEx(Process, nil, 4096, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(Process, Paramaters, Pointer(DLL), 4096, BytesWritten);
Thread := xCreateRemoteThread(Process, nil, 0, GetProcAddress(GetModuleHandle('KERNEL32.DLL'), 'LoadLibraryA'), Paramaters, 0, ThreadId);
WaitForSingleObject(Thread, INFINITE);
xVirtualFreeEx(Process, Paramaters, 0, MEM_RELEASE);
CloseHandle(Thread);
CloseHandle(Process);
end.
uses
Windows;
var
PID, BytesWritten, Process, Thread, ThreadId: dword;
Paramaters: pointer;
DLL: pchar;
function xCreateRemoteThread(hProcess: dword; lpThreadAttributes: Pointer; dwStackSize: dword; lpStartAddress: Pointer; lpParameter: Pointer; dwCreationFlags: dword; lpThreadId: dword): dword; stdcall; external 'RT.dll';
function xVirtualAllocEx(hProcess: dword; lpAddress: Pointer; dwSize: dword; flAllocationType: dword; flProtect: dword): Pointer; stdcall; external 'RT.dll';
function xVirtualFreeEx(hProcess: dword; lpAddress: Pointer; dwSize: dword; dwFreeType: dword): boolean; stdcall; external 'RT.dll';
begin
DLL := 'c:\Inject\Library.dll'; //full path!
PID := 1784; //process id!
Process := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
Paramaters := xVirtualAllocEx(Process, nil, 4096, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(Process, Paramaters, Pointer(DLL), 4096, BytesWritten);
Thread := xCreateRemoteThread(Process, nil, 0, GetProcAddress(GetModuleHandle('KERNEL32.DLL'), 'LoadLibraryA'), Paramaters, 0, ThreadId);
WaitForSingleObject(Thread, INFINITE);
xVirtualFreeEx(Process, Paramaters, 0, MEM_RELEASE);
CloseHandle(Thread);
CloseHandle(Process);
end.
0 comentarios:
Publicar un comentario