Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / Visual Basic 10

ASM.Net - x86 Emulation

Rate me:
Please Sign up or sign in to vote.
4.90/5 (31 votes)
26 Dec 2011CPOL3 min read 50.5K   775   55   15
ASM.Net a assembly x86 emulator which emulates the language the managed way

Asm.Net is an assembly emulator which is developed in C# which will also help you develop your programs using assembler language in the .NET Framework. You will also be able to run programs using Asm.Net for emulating programs.

Introduction 

ASM.Net is an assembly x86 emulator which can emulate the instructions of assembly. Not all of them yet though. Yet there are a lot of instructions/opcodes which are already working. And may be enough for you to work in it.

ASM.Net is also able to convert all the code you write to a byte array which ASM.Net can read again to get all instructions/variables/APIs. So the code you write in ASM.Net is all portable and can be ran in another computer or processed or whereever you want  without any problems. But this is not all, the byte array ASM.Net is able to generate is encrypted at every section and compressed to keep it away from thieves for your code.

Using the code

Using the code in ASM.Net is easy but you really need to have some knowledge about assembly to understand any of it, e.g., JMP, JNZ, CALL, XOR and so on... With the OpcodeWriter we can write our code, create variables, and do more. At the end when we write everything that the program needs to do we can also debug it using the ASM.Net debugger and see if everything is working like expected.

This is an example code for how to write our MessageBox:

C#
OpcodeWriter writer = new OpcodeWriter();

//Call MessageBox
writer.codeSection.PUSH_VALUE(0); //push a value to stack
//ASM.Net creates a new variable and pushes the address of it to stack
writer.codeSection.PUSH_STRING("Title here");
writer.codeSection.PUSH_STRING("Hello CodeProject!");
writer.codeSection.PUSH_VALUE(0); //push a value to stack
writer.codeSection.CALL(Functions.User32_MessageBoxA); //call our MessageBox
AsmNet asmNet = new AsmNet(writer.Generate(true)); //initialize ASM.Net
Processor Cpu = asmNet.InitializeCPU(); //initialize the CPU to be able to execute our code
Cpu.RunLoop(); //execute the code we wrote

As you can see it's not that much code to show a MessageBox. And as may have noticed ASM.Net only needs one argument which is the byte array I told you about. The OpcodeWriter is able to convert to a byte array using the Generate function. The True you see as argument for the Generate function is to check every JUMP address for errors at runtime. This could prevent errors at runtime before you would share it with others.

But showing a simple MessageBox is not all ASM.Net can do, it can do a lot more. This is a simple TCP server:

C#
OpcodeWriter writer = new OpcodeWriter();

WSAData wsaData = new WSAData();
sockaddr_in sockaddr = new sockaddr_in();
sockaddr_in Clientsockaddr = new sockaddr_in();
VirtualAddress wsaDataAddr = writer.dataSection.CreateVariable(wsaData);
VirtualAddress SockinAddress = writer.dataSection.CreateVariable(sockaddr);
VirtualAddress ClientSockinAddress = writer.dataSection.CreateVariable(Clientsockaddr);
VirtualAddress ArrayAddress = 
  writer.dataSection.CreateVariable(ASCIIEncoding.ASCII.GetBytes(":)"));
//the data we want to send when a client connects

//socket initialization
//set the WSADATA settings
writer.codeSection.MOV_VARIABLE_VALUE(wsaDataAddr, "HighVersion", (ushort)2);
writer.codeSection.MOV_VARIABLE_VALUE(wsaDataAddr, "Version", (ushort)2);

//set the sockaddr_in settings, setting the family IPv4
writer.codeSection.MOV_VARIABLE_VALUE(SockinAddress, 
  "sin_family", (short)ValueCodes.InterNetworkv4);
//setting port, we need to encode it first...
writer.codeSection.PUSH_VALUE(1337); //1337=listen port
writer.codeSection.CALL(Functions.ws2_32_htons);
writer.codeSection.MOV_VARIABLE_REGISTER(SockinAddress, "sin_port", Register.EAX);

writer.codeSection.PUSH_VARIABLE(wsaDataAddr);
writer.codeSection.PUSH_VALUE(36);
writer.codeSection.CALL(Functions.ws2_32_WSAStartup);

//started successfully ?
writer.codeSection.MOV_ECX(0);
writer.codeSection.CMP(CmpRegisterOpcodes.CMP_ECX_EAX);
writer.codeSection.JNE("failed");

//create a socket
writer.codeSection.PUSH_VALUE(ValueCodes.Tcp, (int)0);
writer.codeSection.PUSH_VALUE(ValueCodes.Stream, (int)0);
writer.codeSection.PUSH_VALUE(ValueCodes.InterNetworkv4, (int)0);
writer.codeSection.CALL(Functions.ws2_32_socket);
                
//is socket > 0 ?
writer.codeSection.MOV_ECX((int)ValueCodes.INVALID_SOCKET);
writer.codeSection.CMP(CmpRegisterOpcodes.CMP_ECX_EAX);
writer.codeSection.JE("failed");

//lets move our socket handle to EBX
writer.codeSection.MOV(MovRegisterOpcodes.MOV_EBX_EAX);

//lets bind our socket
writer.codeSection.PUSH_VALUE(Marshal.SizeOf(sockaddr));
writer.codeSection.PUSH_VARIABLE(SockinAddress); //our sockaddr_in
writer.codeSection.PUSH_EBX(); //socket handle
writer.codeSection.CALL(Functions.ws2_32_bind);

//ok lets listen at a port
writer.codeSection.PUSH_VALUE((int)100);
writer.codeSection.PUSH_EBX(); //socket
writer.codeSection.CALL(Functions.ws2_32_listen);


//now a infinite loop for accept our connections but lets setup our console
writer.codeSection.PUSH_VALUE(-11); //STD_OUTPUT_HANDLE
writer.codeSection.CALL(Functions.Kernel32_GetStdHandle);
writer.codeSection.MOV(MovRegisterOpcodes.MOV_EDX_EAX);

writer.codeSection.CreateLabel("loop");
//lets accept connections
writer.codeSection.PUSH_VALUE(Marshal.SizeOf(Clientsockaddr));
writer.codeSection.PUSH_VARIABLE(ClientSockinAddress);
writer.codeSection.PUSH_EBX(); //server socket
writer.codeSection.CALL(Functions.ws2_32_accept);
writer.codeSection.MOV(MovRegisterOpcodes.MOV_EDI_EAX); //set client socket to EDI


writer.codeSection.PUSH_VALUE(0);
writer.codeSection.PUSH_VALUE(0);
writer.codeSection.PUSH_VALUE(20);//char length
writer.codeSection.PUSH_STRING("new client accepted\r\n");
writer.codeSection.PUSH_EDX();
writer.codeSection.CALL(Functions.Kernel32_WriteConsoleA);

//lets send a packet
writer.codeSection.PUSH_VALUE(0);
writer.codeSection.PUSH_VALUE(2);
writer.codeSection.PUSH_VARIABLE(ArrayAddress);
writer.codeSection.PUSH_EDI(); //client socket
writer.codeSection.CALL(Functions.ws2_32_send);

//close our connection with the client...
writer.codeSection.PUSH_EDI();
writer.codeSection.CALL(Functions.ws2_32_closesocket);

writer.codeSection.JMP("loop");

writer.codeSection.PUSH_EBX();
writer.codeSection.CALL(Functions.ws2_32_closesocket);

writer.codeSection.CreateLabel("failed");
writer.codeSection.XOR(XorRegisterOpcodes.XOR_ECX_ECX); 

When you are reading through the code, you will find some JUMPs, Calls etc. But this example is for starting a simple TCP server which accepts connections and sends a simple smiley face "Smile | <img src= " align="top" src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> " to the one who connects. As you can see here in this image it's waiting for a new connection. By the way don't think it's Linux it's just Windows 7 with an Ubuntu theme:

OK, so let's do for example in our command line of Windows "telnet 127.0.0.1 1337":

As you can see when I pressed Enter in the command line it came up with a smiley face:

As you could see the "Smile | <img src= " align="top" src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> " came up and it worked just fine and something to another console was written that a new connection was accepted (look above for the code in the TCP server). And to make debugging a little more easier let's see where our JUMPs are going:

As you can see the red line shows us instantly where the JUMP is going to. If there are any questions I'd love to answer those. But you also should need to dig in the code a little. There are also events etc., that you can use.

Points of Interest 

While making ASM.Net I needed to laugh a little because I can now write portable code instead of using CodeDom which can give errors because a .dll is missing whatsoever and dumps its code to a temp directory that people can access and steal the code from.

And ASM.Net can run anywhere but it still needs the ASM.Net library and don't even need to be dumped to the hard drive if not needed.

History

You can checkout our SVN: 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
QuestionDownload error Pin
Ken M4-Jan-16 21:32
Ken M4-Jan-16 21:32 
QuestionImages are missing Pin
Smitha Nishant22-May-13 8:29
protectorSmitha Nishant22-May-13 8:29 
Generalmeta meta Pin
dave.dolan10-Apr-12 4:32
dave.dolan10-Apr-12 4:32 
GeneralMy vote of 5 Pin
John Underhill5-Jan-12 17:09
John Underhill5-Jan-12 17:09 
QuestionNice, but... Pin
KenBeckett2-Jan-12 11:09
KenBeckett2-Jan-12 11:09 
SuggestionRe: Nice, but... Pin
Philip Liebscher3-Jan-12 15:12
Philip Liebscher3-Jan-12 15:12 
GeneralRe: Nice, but... Pin
Drag0nHunter4-Jan-12 2:28
Drag0nHunter4-Jan-12 2:28 
QuestionCool stuff Pin
HaBiX26-Dec-11 23:27
HaBiX26-Dec-11 23:27 
QuestionVery cool idea. How's the performance? Pin
buddy.james26-Dec-11 18:02
buddy.james26-Dec-11 18:02 
AnswerRe: Very cool idea. How's the performance? Pin
Drag0nHunter27-Dec-11 12:34
Drag0nHunter27-Dec-11 12:34 
GeneralRe: Very cool idea. How's the performance? Pin
Marc Clifton27-Dec-11 16:39
mvaMarc Clifton27-Dec-11 16:39 
QuestionAwesome! Pin
Yonghwi Kwon26-Dec-11 12:37
Yonghwi Kwon26-Dec-11 12:37 
AnswerRe: Awesome! Pin
Drag0nHunter27-Dec-11 12:40
Drag0nHunter27-Dec-11 12:40 
AnswerRe: Awesome! Pin
Derek Viljoen1-Feb-12 2:42
Derek Viljoen1-Feb-12 2:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.