Last week I was asked a simple question on the Discord server for my Tree of Savior server emulator, Melia, in regards to reviving a feature that had been removed from the game, which lead me down a path that would remedy all of those problems for this particular project.
Did you see the packet to open player shops by any chance?
Well, I hadn’t, but I was kind of curious, so I dug around the client files a little and I found the UI for personal shops. So far, so good. If the UI is still there we could always open it, and be it via client modification. However, Tree of Savior has some interesting design aspects to it. Their UI system runs mostly on Lua and there are a few packets that call into the Lua environment, like ZC_ADDON_MSG, which the server can use to trigger events. That means you can control the UI to a certain degree, as long as events were set up by the scripts.
addon:RegisterMsg('DIALOG_CLOSE', 'DO_SOMETHING_ON_CLOSE');
Unfortunately there were no events for the personal shops, so the UI couldn’t be opened that way. But I remembered another packet. For some reason some Lua functions appear to be called directly by the server, with a packet that contains a single line of Lua code, like this:
CALL_SOME_FUNCTION()
So I tried to call the function for opening the personal shop UI, and it worked. I just sent the function call and the window opened. “Neat,” I thought, but the UI didn’t fully work. It didn’t react to my actions, so more work, and client modifications, would be required after all. I wanted to know what exactly was wrong though, so I debugged the script. As it turns out the problem was simple. The UI elements had changed slightly after this feature had been removed, and one of the personal shop functions just failed to get a reference to one of the window’s elements. We’d only have to fix that one function and the UI would be working again.
At this point I started wondering about something. Note the parentheses in the snippet above. This packet, ZC_EXEC_CLIENT_SCP, appeared to not just call a function by its name. It’s actual Lua code, a function call. If we could run arbitrary Lua code on the client via this packet… could we replace existing functions? See, Lua is a scripting language, and a flexible one at that. You can easily redefine functions just by defining them again.
function Foo()
print("foo")
end
function Foo()
print("bar")
end
Foo(); -- prints "bar"
And every piece of Lua code that gets executed becomes part of the environment. If I were to instruct Lua to run another piece of code that contained a new Foo function, that function would become the one that’s called going forth. A quick test confirmed my hopes. I fixed the function that was failing to find the UI element and sent it to the client, and it worked. Suddenly the UI did its job again.
“Neat” wasn’t the right word for this anymore. You don’t usually modify a client’s behavior from the server to such a degree. If the entire UI is runing on Lua, and we’re able to freely modify it in any way we see fit, that would open up a lot of possibilities. It was slowly dawning on me how powerful this ability is. Care for another example?
After the UI was working again, I tried to actually create a personal shop, but nothing happened when I clicked the button. Looking at the client’s scripts again, this was the end of the rope. At that point, when you click the button, the scripts call into the client. There was nothing more that I could do. I launched a debugger and found where the internal function was failing, and I was able to skip a check to make it work and have the client send the shop creation packet. Though this would not only require a modification of client data, but the client itself. Very unfortunate. However, ToS’s Lua scripts can do a few more things than just handle the UI. They can also send chat messages for example.
ui.Chat("/createshop ...")
Well, the UI, and subsequently the script, knows everything there is to know about the shop… the name, the items, the prices you specified… putting that into a string is not an issue. Sending that string via chat message, in the form of a command, is not an issue. And the server can read and interpret that command and act on it. Just like that, you open up a custom communication path, and you’re able to tell the server to create a shop.
Just being able to modify the UI is very useful. Being able to fix logic errors and customize behavior is amazing, but with the server instructing the client to run arbitrary Lua code and the client sending chat messages, you have two-way communication, with which you can do pretty much anything. That is mindblowing. What else could I do with this…
One of my bigger gripes about ToS is that shops are client-sided. The client simply has a database with the shops and the items and all the server does is tell the client which shop to open by name. A look at the shop UI scripts quickly showed that it didn’t have to be that way. The script requests a list of shop items from the client, with a call like this:
session.GetShopItemList()
But even though that’s a client function, it’s still in the Lua environment, so you can replace it.
function session.GetShopItemList()
return MyCustomShopItems
end
And the code you can have the client run isn’t limited to functions, you could also define global arrays, like a list of items that a shop should have. With these pieces falling into place I was able to implement dynamic NPC shops in Melia. Not by modifying the client data, not even by modifying the data from the server via packet, which is technically possible as well (with limitations), but by simply overwriting two client-side Lua functions and adding just a little bit of code to Melia.
To me this discovery immediately made ToS ten times more interesting to work on. The UI is always a very limiting factor when working on server emulators. You typically have to live with what you get. Some games even hardcode their UIs and don’t even use any kind of markup, so that even if you’re okay with client modifications, it would be difficult to remove a button for some feature you’re not using on your server for example, or to add some kind of new element. And even if you can do that, then the behavior is usually defined in the client. By having access to the UI, the behavior, and a way to communicate with the server back and forth, there are very few limitations left.
It’s amazing how one simple packet can change your whole outlook on a project. Exciting times.
]]>A few months ago, I got acquainted with the developer of an indie (M)MORPG, and being a server developer, the first thought that popped into my head was to write a server for their game, just for fun. Luckily they hadn’t protected their game at all yet, so I whipped together a simple packet logger, got the information I needed, and a few hours later I had a basic server going. It was a fun afternoon, and I probably would’ve stopped there, but while looking through the packets, I noticed some rather glaring security issues and potential exploits. That’s when I decided to have some more fun and explore the game and what I could do with it.
Spawning items, flying around, killing entire maps full of monsters in seconds; it’s always interesting to see how far you can go with a simple packet editor. But after I’d had my fun, I reported the issues I had found to the developer. They fixed most everything I had found rather quickly, and then they figured they would give protecting their game a try, by encrypting their packets.
As I talked with this developer, I realized they had no idea how I did what I did. They figured if the packets are encrypted, I can’t read them anymore, I can’t modify them anymore, and I wouldn’t be able to send my own packets, because I don’t know how to encrypt them. But since I simply read the client’s code, I did know the encryption. After they had published that update it took me just a few minutes to open up the client, reverse engineer their custom encryption algorithm, and then I was back up and running.
This was their first encryption algorithm ever and it was fairly simple. It was basically a Vigenère cipher, with a password that was a) stored in plain text inside the client, and b) could be inferred from the packets, because the 0 bytes in the packets made it obvious by how much the byte had been shifted. This way I immediately knew what they had done and understanding and reverse engineering their code became even easier.
After this failed attempt they figured their custom algorithm had just been too simple, and they rewrote the encryption. This time it wasn’t immediately clear to me what the encryption was from the packets, but once I had opened up the client I was greeted by references to AES. That made it simple once again. Download an AES library, plug it in, look up how the keys are generated and shared in the client, and I was done once more.
They made an effort, but in the end they failed to keep me out because they weren’t familiar with my methods. They even ported AES to their engine because they figured that’s an industry standard, it must be secure, but this all stemmed from the incorrect assumption that you need to protect your packet data.
The thing is, you can never, ever, prevent users from hacking your client. Assume that they can literally read your code, that they know everything you’re doing on the client side. Well, how are you supposed to protect anything from hackers then? The trick is to annoy them. Annoy them to the point where they don’t want to work on your game anymore. Until they figure it’s not worth it.
An encryption is actually a good first step, because that keeps out everybody who is not able to reverse engineer your code. Without an encryption they could just read the raw packets and that’s that. Getting through the encryption requires at least some effort.
However, it is my believe that you shouldn’t use a well established encryption algorithm. AES is most definitely difficult to crack, but don’t forget that I’m not trying to crack the packets. I just read the code, see that it’s AES, and then I basically know how to en- and decrypt everything. Even modifying the algorithm slightly is relatively pointless because I can just compare your code to the original and find the differences when I notice that I’m not getting the correct results.
A custom algorithm on the other hand forces me to actually understand and reverse engineer it. And even though I said that you should assume users can literally read your code, that’s still an actual hurdle. And the more creative you get, the more time and effort it takes to reverse it. I realize this sounds kind of counter intuitive, but the crude, custom algorithm the developer had written was actually a better idea than using AES. They would’ve just have to tweak it a little.
If you have a popular game on your hands though, someone will eventually break your encryption or find a way around it. It’s just a matter of time. They’ll figure it out, they’ll start doing things you don’t want them to, and that’s that. Well, unless you simply change the encryption. For you it’s fairly simple to make some quick changes or even swap out the entire encryption, but every time you do, someone has to go back in there and reverse everything once again. They might do this for fun a few times, but nobody will do it consistently without getting annoyed by it. “Another week, another update, another new encryption… Ugh, here we go again.” And it doesn’t have to stop at the encryption.
Another underutilized method of annoying hackers is to simply randomize a few things on a regular basis. For example, opcodes. Opcodes are used to identify packets, and a hacker might be using packet 0xEA28 to, say, kill NPCs. For some reason you might be unable to change/fix this behavior, but if you randomized your opcodes regularly, hackers would need to look them back up every time. “Another week, another update, 496 random opcodes… Ugh, here we go again.” And while hackers might only be interested in a handful of specific packets that they can use for their purposes, server developers actually need all packets, and by randomizing your opcodes you essentially stop them in their tracks. Very, very few people will ever go to the trouble of updating hundreds of opcodes on a regular basis.
Or how about something really nasty, randomizing the fields inside the packets, so even if a hacker has the encryption and the opcodes, they have to rewrite the entire reading and writing of every packet every week? There’s so many little things you can do, with very little effort, that could even be automated, that would annoy me personally so much that I would probably drop any project related to your game in two weeks or less.
Essentially, you should never think about how to encrypt your packets even more, but about how to slow down and annoy reverse engineers. The keyword is obfuscation. Remember that most of them are doing it for fun, and if it stops being fun, they’ll stop doing it. I might have fun developing a server emulator or a packet editor, but I definitely don’t want to get the new opcodes every week, or reverse engineer another encryption algorithm. No, thank you.
I find it fascinating that, to my knowledge, there are very few examples of companies employing such strategies, despite of how simple they would be to implement. Only two explanations come to my mind. A) They don’t care. It’s possible, but since companies usually do at least try to get rid of hackers, I doubt that. Or B) Just like the game developer I talked to, they just don’t know better. They see their encryption as their barrier, and once it’s breached they either lie down and give up, or, at most, put up a new wall. Very few ever come up with the idea of putting an ever changing labyrinth in front of their castle. Then again, I suppose I’m kind of thankful for that
When I started looking into using PHP from C#, I found a few code snippets that were enough to run a simple script, maybe even handle some GET parameters, but that was about it. Examples for POST were difficult to come by, and I don’t believe I found anything on file uploads.
Why was I even looking into this though? When I write an application that needs a web server, like a server emulator for a game that provides some services over HTTP, I want to include a simple web server with the application. Just so it’s there, and the whole package can be used as is, without setting up more additional software. And ideally I want an upgrade path to a more capable web server, like Apache or NGIX, in case a user wants to integrate everything into their existing eco-system, and for that, I want to be able to use PHP.
The web server part of the equation is actually fairly simple, as there are several viable options out there, such as NHttp or EmbedIO. I won’t go into too much detail about that here, as these packages are fairly self-explanatory and not difficult to use at all. Personally I’m currently using EmbedIO, which can be easily integrated into any .NET application and works perfectly fine for my purposes. You can be up and running in minutes if all you want is serve static content, but the interesting part is the dynamic part, like with PHP scripts.
One way web servers often times handle dynamic content is via CGI, which in this context stands for “Common Gateway Interface”. It’s a standard for passing information from a web server to an application or script, which can then do something with that information and return data, such as HTML code, to send back to the browser. In the real world this might look something like this.
You make a request to a file called “foobar.php”, the web server recognizes that this is a PHP file, it passes that request to a “php-cgi.exe”, PHP runs the script with the information from the web server, such as the GET variables, and then writes the result to the standard output, which the web server reads and sends back to the browser.
This sounds pretty easy, and it is, you just have to pass the right information.
Let’s get straight to it, this is an example of what a request handler that calls PHP might look like for EmbedIO, though it should be simple to adjust it for other libraries.
protected override async Task OnRequestAsync(IHttpContext context)
{
var fileInfo = fileSystemProvider.MapUrlPath(context.RequestedPath, context);
// Skip if file isn't a PHP file
if (!fileInfo.IsFile || Path.GetExtension(fileInfo.Path) != ".php")
return;
// Get query string from URL
var index = context.Request.RawUrl.IndexOf("?");
var queryString = index == -1 ? "" : context.Request.RawUrl.Substring(index + 1);
// Read body for POST requests
byte[] requestBody;
using (var ms = new MemoryStream())
{
context.Request.InputStream.CopyTo(ms);
requestBody = ms.ToArray();
}
// Get paths for PHP
var documentRootPath = "C:/MyWebServer/";
var scriptFilePath = Path.GetFullPath(fileInfo.Path);
var scriptFileName = Path.GetFileName(fileInfo.Path);
var scriptFolderPath = Path.GetDirectoryName(fileInfo.Path);
var tempPath = Path.GetTempPath();
// Execute PHP
using (var process = new Process())
{
process.StartInfo.FileName = "php-cgi.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.EnvironmentVariables.Clear();
process.StartInfo.EnvironmentVariables.Add("GATEWAY_INTERFACE", "CGI/1.1");
process.StartInfo.EnvironmentVariables.Add("SERVER_PROTOCOL", "HTTP/1.1");
process.StartInfo.EnvironmentVariables.Add("REDIRECT_STATUS", "200");
process.StartInfo.EnvironmentVariables.Add("DOCUMENT_ROOT", documentRootPath);
process.StartInfo.EnvironmentVariables.Add("SCRIPT_NAME", scriptFileName);
process.StartInfo.EnvironmentVariables.Add("SCRIPT_FILENAME", scriptFilePath);
process.StartInfo.EnvironmentVariables.Add("QUERY_STRING", queryString);
process.StartInfo.EnvironmentVariables.Add("CONTENT_LENGTH", requestBody.Length.ToString());
process.StartInfo.EnvironmentVariables.Add("CONTENT_TYPE", context.Request.ContentType);
process.StartInfo.EnvironmentVariables.Add("REQUEST_METHOD", context.Request.HttpMethod);
process.StartInfo.EnvironmentVariables.Add("USER_AGENT", context.Request.UserAgent);
process.StartInfo.EnvironmentVariables.Add("SERVER_ADDR", context.LocalEndPoint.Address.ToString());
process.StartInfo.EnvironmentVariables.Add("REMOTE_ADDR", context.Request.RemoteEndPoint.Address.ToString());
process.StartInfo.EnvironmentVariables.Add("REMOTE_PORT", context.Request.RemoteEndPoint.Port.ToString());
process.StartInfo.EnvironmentVariables.Add("REFERER", context.Request.UrlReferrer?.ToString() ?? "");
process.StartInfo.EnvironmentVariables.Add("REQUEST_URI", context.RequestedPath);
process.StartInfo.EnvironmentVariables.Add("HTTP_COOKIE", context.Request.Headers["Cookie"]);
process.StartInfo.EnvironmentVariables.Add("HTTP_ACCEPT", context.Request.Headers["Accept"]);
process.StartInfo.EnvironmentVariables.Add("HTTP_ACCEPT_CHARSET", context.Request.Headers["Accept-Charset"]);
process.StartInfo.EnvironmentVariables.Add("HTTP_ACCEPT_ENCODING", context.Request.Headers["Accept-Encoding"]);
process.StartInfo.EnvironmentVariables.Add("HTTP_ACCEPT_LANGUAGE", context.Request.Headers["Accept-Language"]);
process.StartInfo.EnvironmentVariables.Add("TMPDIR", tempPath);
process.StartInfo.EnvironmentVariables.Add("TEMP", tempPath);
process.Start();
// Write request body to standard input, for POST data
using (var sw = process.StandardInput)
sw.BaseStream.Write(requestBody, 0, requestBody.Length);
// Write headers and content to response stream
var headersEnd = false;
using (var sr = process.StandardOutput)
using (var output = context.OpenResponseText())
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (!headersEnd)
{
if (line == "")
{
headersEnd = true;
continue;
}
// The first few lines are the headers, with a
// key and a value. Catch those, to write them
// into our response headers.
index = line.IndexOf(':');
var name = line.Substring(0, index);
var value = line.Substring(index + 2);
context.Response.Headers[name] = value;
}
else
{
// Write non-header lines into the output as is.
output.WriteLine(line);
}
}
}
}
// Set context to handled, so no more modules get the request
context.SetHandled();
await Task.CompletedTask;
}
The important part is to get all the information PHP needs to properly handle the request and to read back the response it gives. In this example we create a new process, set it to our “php-cgi.exe”, which comes with PHP, and set the respective variables to not actually show the PHP interpretor and redirect the in- and output, so we can use it. Some of the arguments you might recognize if you’ve used PHP before, such as SCRIPT_FILENAME or REQUEST_URI.
Just by passing QUERY_STRING, you are able to handle GET requests, because PHP will parse that string for variables. For correct handling of POST requests, you need to read the data sent to the web server, pass the correct CONTENT_LENGTH and CONTENT_TYPE, and write the entire body of the request you read from the web server to the standard input of the process, so PHP can read it.
Finally, all you do is read the result from the process, to be found in the standard output, and use it in your web server’s response. Parse the response for headers, to integrate them into your web server’s response handling, and write the remaining lines into the response as is. The seperator between the headers and the content is a simple, empty line. That’s it.
GET requests are relatively simple with this snippet, POST requests are as well, as long as you properly read and write the data to and from the standard input and output. To support uploading files however, you need to make sure that PHP has access to a temp folder, and that it knows where it is. It does not default to the system’s default temp directory. To fix this, you can either set the option upload_tmp_dir in the php.ini, or pass the two arguments TMPDIR and TEMP to php-cgi, which are the two environment variables in use for different operating systems.
While this code works great for simple applications, I want to mention that you shouldn’t use this in a high-traffic production evironment. CGI is actually rarely used nowadays, because starting an instance of php-cgi for every request isn’t very efficient. Instead, most web servers either use plugins or FastCGI, the latter of which is a system to keep instances running in the background, and just pass the information to them, to generate a response. But if you have such a requirement, you should probably use a more well established web server anyway.
Well, I hope this will be helpful to someone out there. I wish I had found a complete example of this anywhere, that would’ve saved me some time^^
]]>But just like C and other languages protect you from simple mistakes, like not unwinding the stack after calling a cdecl-convention function or addressing variables on the stack incorrectly, you can easily help yourself write cleaner, more robust code with the help of macros.
NASM, the assembler I decided to use, has a pretty powerful preprocessor, that is similar to C’s preprocessor in many ways. You have your defines, you can create macro “functions”, and with a bit of fiddling around, you can turn something like this:
_start:
push nameQuery
call Write
push inputLen
push input
call ReadLine
push eax
push input
push inputGiven
call WriteLineF
add esp,8
pop eax
cmp eax, 0
jne .sayHello
push noName
call WriteLine
jmp .end
.sayHello:
push input
push hello
call WriteLineF
add esp, 8
.end:
xor eax, eax
ret
into the visually much more appealing
sproc _start
invoke Write, nameQuery
invoke ReadLine, input, inputLen
cinvoke WriteLineF, inputGiven, input
if eax,'==',0
invoke WriteLine, noName
else
cinvoke WriteLineF, hello, input
endif
xor eax, eax
endp
I’m always amazed how simple it is to “redefine” a language this way, and aside from being easier on the eyes, it can also help with making the code less error prone.
The first macro I tackled was proc (for procedure), which is what most Assembly coders seem to name their function macros. Technically, setting up a simple function isn’t all that difficult or error prone, since it’s essentially just a label that you jump to, but it can become very useful in combination with other macros, like ones for defining arguments or local variables, as the proc macro can help with preparing everything, and the endp macro with clean up.
For demonstration purposes, let’s take a simple function that multiplies two values with each other and also uses a local variable.
; int multiply(int val1, int val2)
multiply:
push ebp
mov ebp, esp
sub esp, 4 ; make space on stack for temp variable
mov eax, [ebp+8] ; val1
mul dword [ebp+12] ; multiply by val2
mov dword [ebp-4], eax ; save eax in temp
mov eax, 1234 ; use eax for something else
mov eax, [ebp-4] ; write temp back to eax for return
leave
ret 8
A rather pointless function, but it will work for demonstrating the proc pattern that I came up with. First though, a quick word about what’s going on here.
The function starts with preserving ebp on the stack and setting it to esp, while calling leave before ret, which basically reverses this, restoring ebp. This is a common pattern to make addressing arguments and local variables easier, because ebp is typically a preserved register, that you can expect not to change when calling other functions. Additionally, you don’t have to factor in potential pushes and pops in your code, which would make addressing local variables using [esp+X] rather difficult.
With the preserved ebp value sitting at [ebp+0], and the return address at [ebp+4], the first argument can be found at [ebp+8]. In the other direction, since we push local variables after setting ebp, you can find the first local variable at [ebp-4].
| ebp-4 | temp |
| ebp+0/esp | Preserved ebp value |
| ebp+4 | Return address |
| ebp+8 | val1 |
| ebp+12 | val2 |
Arguments and locals are then accessed with [ebp+-X], and at the end ret is called with the size of the arguments (2 integers = 8 byte) to unwind the stack and effectively remove them from it. And by resetting esp to ebp using leave at the end you remove the local variables automatically. The function is very simple, but one can imagine how easy it is to make mistakes in just these few lines.
If you add or remove an argument you have to remember to also update the return, otherwise your application will likely crash, due to the state the stack is in. If you forget leave, your function will not restore ebp and your locals will not be removed, also most likely causing a crash. And if you get the offset to one of your arguments or locals wrong, in the best case you will get wrong behavior, and in the worst, you guessed it, a crash.
The first step for me then was to define the proc macro, as a wrapper around my function body.
%imacro proc 1
%ifctx proc
%error "proc can't be nested"
%endif
%1:
push ebp
mov ebp, esp
%push proc
%endmacro
%imacro endp 0
%ifnctx proc
%error "unexpected endp without proc"
%endif
leave
ret
%pop proc
%endmacro
These are multi-line macros, with which you can do a lot. You define them with a name and a number of arguments, which you can then use inside them. The one parameter proc gets is the name of the function. It then ensures that you’re not using the macro multiple times without ending the previous one, sets up a label that can be used with call, and sets up ebp for use with arguments and locals.
By combining the instructions proc foobar and endp, you quickly get a simple wrapper for a function.
proc foobar
; code
endp
; evolves to~
foobar:
push ebp
mov ebp, esp
; code
leave
ret
A good start, but just hiding how ebp is modified and making ret static, without stack unwind, is probably not such a good idea, unless the function doesn’t have any arguments. With the addition of an arg macro and some changes to endp, however, everything comes together.
%imacro arg 2
%ifnctx proc
%error "arg must be contained in a proc"
%endif
%ifndef %$argsOffset
%assign %$argsOffset 8
%assign %$returnSize 0
%endif
%xdefine .%1 ebp+%$argsOffset
%assign %$argsOffset %$argsOffset+%2
%assign %$returnSize %$returnSize+%2
%endmacro
%imacro endp 0
%ifnctx proc
%error "unexpected endp without proc"
%endif
leave
%ifdef %$returnSize
ret %$returnSize
%else
ret
%endif
%undef %$argsOffset
%undef %$returnSize
%pop proc
%endmacro
The arg macro essentially does just one thing, it sets up a local define that you can use to address your arguments with by name. That’s the only effect it has on your code, though it doesn’t even produce any code, it all happens on the preprocessor. It also saves the total size of the arguments in a context-scope variable though, which can then be used from endp to unwind the stack correctly. What it needs for these two things are a name for the variable and its size, because you naturally don’t have to only push simple 4 byte integers to the stack, it could also be arrays, structs, etc.
Setting up local variables is almost the same, just with a different offset for ebp, so I’ll not paste that here, but at the end of this post I’ll include all the macros I wrote, including comments, so you can take a closer look at them.
With these changes in place, the function already becomes quite a bit more structured.
proc multiply
arg val1, 4
arg val2, 4
local temp1, 4
sub esp, 4
mov eax, [.val1]
mul dword [.val2]
mov dword [.temp1], eax
mov eax, 1234
mov eax, [.temp1]
endp
However, the local variables still need to be set up, and with everything else hidden behind macros, that lone sub esp seems out of place. No big deal, we can get rid of that as well. One option would be to change esp inside the local macro, but with a lot of local variables, that would be a lot of unnecessary subs, one per local, and naturally we don’t want our code to become less efficient by use of macros. Instead, let’s add a convention to place arguments and locals in a kind of “header” part of the function, and initialize the “body” of the function with another macro, beginp.
(beginp… I serioulsy didn’t notice that until just now.)
All this macro will do is modify esp for the total size of the locals we declared.
%imacro beginp 0
%ifdef %$localsSize
sub esp, %$localsSize
%endif
%endmacro
I also added a few alternatives for arg and local, so you don’t always have to define the size in bytes, and that gives us our final multiply function. No “magic numbers” to access the stack, no risk of using the wrong register, automatic clean up with implicit leaves and returns at the end.
proc multiply
argd val1
argd val2
locald temp1
beginp
mov eax, [.val1]
mul dword [.val2]
mov dword [.temp1], eax
mov eax, 1234
mov eax, [.temp1]
endp
It’s funny how sometimes you learn what certain design decisions of other people were about, that didn’t make sense to you at the time. For example, when I tried to write my first 2D top-down game I was determined to not rely on tile movement, because I thought free pixel movement would be so much better, and easier, because you can just move the character pixel by pixel as long as the move key is pressed. That worked great, until I realized that in a tile-based map design you have to be able to walk through narrow paths, and with strict pixel-based movement it’s rather difficult to get around corners without much additional work… and that’s how I decided that tile-movement was definitely superior!
What does that have to do with Assembly though? Well, I felt pretty good about this pattern I had come up with, but somehow… it felt familiar. This header section… and how arguments and locals are defined before the actual code of the function… That’s when it dawned on me, that I had accidentally re-invented Pascal/Delphi functions. There, this function might look something like this:
function multiply(val1: Integer, val2: Integer): Integer
temp1: Integer;
begin
val1 := val1 * val2;
temp1 := val1;
val1 := 1234;
Result := temp1
end;
Quite similar, isn’t it? A few years ago I briefly had to work with Delphi and I absolutely hated this design. What was that “header section” about? Coming from other languages it seemed so weird. That’s definitely not how I would design a language! Well, if you approach it from the other direction, you have to consider that the compiler has to turn that code into Assembly and/or machine code somehow, and since those compilers are written by humans, the code they produce will be similar to what a human might write. I’m guessing that the design the Pascal developers created stemmed from a similar structure as the one I came up with, something that could be easily converted to Assembly.
I ended up refining these macros some more, adding a proc alternative that doesn’t modify ebp, for functions that don’t need it, adding support for variadic functions (variable number of arguments with no ret value), adding invoke macros to make calling functions easier, and even adding if/elseif/else macros, to make branching easier, eventually arriving at the code example at the start of this post.
sproc _start
invoke Write, nameQuery
invoke ReadLine, input, inputLen
cinvoke WriteLineF, inputGiven, input
if eax,'==',0
invoke WriteLine, noName
else
cinvoke WriteLineF, hello, input
endif
endp
I like where this is going, and I could see myself actually developing applications this way, but as I went along, I realized more and more how silly it would be to actually do it as long as I have other options, like C. Even if you took the C library away, it already has all the function, struct, loop, and variable patterns you could want, which you’re just “patching in” when you’re using Assembly. Why would you do that?
For me personally, a web, desktop, and server developer, there’s little to no practical reason. Having started to work with Assembly, I have a natural urge to do more with it, and familiarizing myself more with it will come in handy when analyzing and debugging programs, but for that it might even be better to write raw Assembly, tedious as it might be, since that’s also what I’m seeing in debuggers. Writing actual applications in my “redefined” Assembly though? I don’t know.
That being said, Assembly does have a certain mystic about it, and it would be kind of fun to have an actual application written in it. I might just do it
In case you’re curious about my full util.inc file, with all my current macros and more detailed comments, you can find it here:
For further reference, I recommend reading the official documention for the NASM preprocessor, which contains all information necessary to write these and other macros. It can be found at the following URL.
]]>While I know the basics of how Assembly works, and even though I’ve read some Assembly code over the years while debugging or analyzing applications, I’ve never written an actual program in any Assembly language. That obviously had to change.
How hard could it be, right?
As it turns out, getting started really isn’t all that bad. You only need two things: An assembler, to turn your code into machine code, and a linker, which turns it into an executable. This part isn’t so different from other languages, like C.
The first question then is which assembler and linker to use. While pretty much any linker will do, as they should generally be able to work with whatever the assemblers generate, which assembler to use borders on the question of what programming language to use. They have different styles and come with different built-in functions. Some focus on Windows development, others are more general-purpose.
The two big names you see a lot are MASM (Microsoft Assembler) and NASM (Netwide Assembler). Since I usually focus on Windows development, I first looked at MASM, but the Hello Worlds I found really weren’t what I was expecting.
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
output db "Hello World!", 0ah, 0h;
.code
start:
invoke GetStdHandle, STD_OUTPUT_HANDLE
invoke WriteConsole, eax, addr output, sizeof output, ebx, NULL
invoke ExitProcess, 0
end start
If you have only ever seen Assembly code in debuggers this sight might be a little surprising, no pushes, no moves… this is no Assembly! It seems closer to C if anything, with its includes and normal looking function calls. The reason for this is that between what I knew as Assembly, and more high level languages, there is some overlap, because Assembly programmers obviously want some comfort as well.
The invoke instruction in this code isn’t actually an instruction. Instead, it might get transformed into the push/mov/call pattern I had expected to see.
push NULL
push ebx
push sizeof output
push addr output
push eax
call WriteConsole
Especially if you come from higher level languages you can see why one might prefer invoke over this. Not only do you have to type more this way, the default Windows calling convention for functions (stdcall) also requires you to push arguments in reverse order, from right to left, which invoke handles automatically.
Similarly, sizeof is a “magical” construct as well, that tells us the length of a string in this example. But it’s just there for convenience. Usually you would have to figure out the length of that string yourself.
MASM has quite a few such creature comforts, and while I’m sure those are great to have when you’re developing seriously, I didn’t want to be tempted by convenient functions like that at the start. I also looked at GoAsm, another assembler with focus on Windows, but it too had quite a few such features, and it’s less popular, meaning you’d find less support for it.
Outside of Windows there’s a few other options, but the most popular choice appears to be NASM. Not only is it less focused on Windows, it also doesn’t come with too many features out of the box, aside from what you might expect from Assembly. Since the syntax was also to my liking, I decided to go with that one.
While looking into GoAsm, however, I tried the linker GoLink from the same author, and I quite liked how simple to use it was, so I went with it. As previously mentioned, switching to a different linker down the line should be fairly simple.
So my tools of choice ended up being NASM and GoLink, both of which can easily be downloaded for free.
Alright, time for a proper Hello World!
global _start
section .data
hello: db 'Hello, World!',10
helloLen: equ $-hello
section .text
_start:
; write hello world
mov eax, 4
mov ebx, 1
mov ecx, hello
mov edx, helloLen
int 80h
; exit with code 0
mov eax,1
mov ebx,0
int 80h
Now that’s more like it! Look at those moves and interrupts! And how the string length is manually calculated! I love it! There’s just one little problem. While you see examples like this a lot, this code won’t run under Windows. In the Linux world, you can use interrupts to call functions in the kernel, such as “function” 0x80, which essentially prints a string. In DOS you could use such interrupts as well, but not on Windows. Microsoft wants you to use the Windows API for such purposes, and that might look something like this.
global _start
extern GetStdHandle
extern WriteConsoleA
section .data
hello: db 'Hello, World!',10
helloLen: equ $-hello
section .text
_start:
; get standard output handle
push -11
call GetStdHandle
; write hello world
push 0
push 0
push helloLen
push hello
push eax
call WriteConsoleA
; return 0
xor eax, eax
ret
Paste this code into a text file, compile and link it with the following commands, and you’re good to go.
> nasm -f win32 hello_world.asm
> golink /console /entry _start hello_world.obj kernel32.dll
Of note are, for one, the argument to nasm, -f win32, which tells it that we want 32-bit Windows code generated, then the entry argument to golink, telling it the label where the program starts (_start in this case), and finally the argument “kernel32.dll” at the end, which we add to tell the linker to search for external functions (or rather symbols) in that DLL. This is what allows us to call GetStdHandle and WriteConsoleA, which aren’t part of the code, but which we told the Assembler to make addressable, because they exist in some external file.
So far, so good! This isn’t difficult at all! Well…
Naturally, you can’t do a lot with a program that does nothing but output some text. The next step for me is usually to extend my Hello Worlds, to make them ask my name and greet me directly. Read the input and print the formatted message Hello, [input]!
What's your name?
: exec
Hello, exec!
And this took more than copying and understanding a Hello World example. See, as far as I can tell, there’s no viable, build-in function to print a formatted string in the Windows API, nor is there a function to read a line from the input that works exactly as I was expecting.
WriteConsoleA writes a string, but doesn’t support formatting. ReadConsoleA reads a line, but it includes the line-break (\r\n) in the result, which you use to end the data input. Now, there’s a very simple solution to these problems: use printf and getline. These are part of the C library, which you can easily link to, and they’re the functions I might use for this Hello World test in other languages. But if I had wanted to use C functions, I could write C. What’s next, link to .NET and use Console.WriteLine? No, that doesn’t seem right. I won’t learn anything by going the easy route.
First things first, printing a formatted string. Without using the C library, you seem to have exactly two options here. One is writing your own formatting function, but I didn’t feel quite ready for that by this point. The other is called wsprintfA and is part of user32.dll.
Unfortunately, wsprintfA isn’t exactly ideal, because:
They recommend some alternatives, but these are only implemented via header files, so they presumably forgot about all the Assembly programmers out there. These alternatives internally use vsnprintf, which is a C library function… Because, naturally, Microsoft’s C++-based DLLs link against the C library. In lack of a better alternative, I decided to use wsprintfA for the moment, but to implement a better solution later on.
So! Prepare a buffer, write a formatted string to it, and print everything!
hello: db 'Hello, %s!',0
helloLen: equ $-hello
input: db 'exec',0
buffer: times 1024 db 0
; ...
push input
push hello
push buffer
call wsprintfA ; format string
add esp, 12 ; clean up stack
mov ebx, eax ; save length of the formatted string
push -11
call GetStdHandle
push 0
push 0
push ebx ; string length
push buffer ; buffer
push eax ; handle
call WriteConsoleA
The buffer is a simple byte array. We call wsprintfA, save the return value, which is the length of the formatted string, and pass everything to WriteConsoleA as before. Just include “user32.dll” in the linking process now, and you’ll get a formatted message.
> hello_world.exe
Hello, exec!
One thing of note in this code, is that after calling wsprintfA, the arguments must be removed from the stack manually, as the function doesn’t clean up after itself. That’s because it’s a variadic function, that takes a variable amount of parameters, and it would be difficult for the callee to determine how many parameters it’s supposed to remove from the stack, so the caller needs to take care of that. And the simplest way to remove 3 arguments from the stack, is to move the stack pointer by 3*4 bytes (3 arguments, times 32-bit per argument on the stack). This is also how the “cdecl” calling convention, which C uses, works, so you always have to clean up after calling functions in the C library. But the 32-bit Windows API uses stdcall, where the function takes care of that.
Now only reading a line is left!
As I’ve mentioned above, there is a function to read a line from the input, but the result includes the line break. Also, at this point I was still just calling the right functions to get things done, but I did want to get my hands dirty, so instead of relying on ReadConsole I decided to give ReadConsoleInput a try. Hoh boy…
I just wanted to read from the input, character by character, and write those into a buffer, but ReadConsoleInput doesn’t simply read characters, it’s basically an event for everything that happens to the console, from mouse input to focusing the console window. It’s actually much more powerful than a simple getchar, and with that, I certainly had an oppurtunity to get my hands dirty, because not only do you have to read characters in a loop to fill up a buffer, you also have to work with the structs that function returns, pick out the events you’re interested in, output the characters you take from the input, etc. This was the first actual code I wrote that didn’t just call some functions, and it took me a while to get it right.
From misaligning my struct definitions, to access violations due to passing the wrong registers or address offsets, to logic errors because I failed to write basic if checks, I had everything. It was kinda funny to start up a debugger and see my code 1:1 in there though
After a day of fiddling around with ReadConsoleInput, and also testing PeekConsoleInput at some point, I ended up with a simple routine that could do what I wanted, though it wasn’t perfect. It didn’t support backspace yet, nor navigating with the arrow keys, all of which are things you have to implement yourself if you were to go this route, which I hadn’t thought about.
My final, though unfinished and unoptimized code, looked something like this.
struc INPUT_RECORD
.EventType resw 1
alignb 4
; union
.Event:
.KeyEvent:
.MouseEvent:
.WindowBufferSizeEvent:
.MenuEvent:
.FocusEvent:
resb KEY_EVENT_RECORD.sizeof
; Include an empty label at the end of the struct,
; which's offset will be equal to the struct's size.
.sizeof resb 0
endstruc
struc KEY_EVENT_RECORD
.bKeyDown resb 1
alignb 4
.wRepeatCount resw 1
.wVirtualKeyCode resw 1
.wVirtualScanCode resw 1
; union
.uChar:
.UnicodeChar:
.AsciiChar:
resw 1
.dwControlKeyState resd 1
.sizeof resb 0
endstruc
; Buffer for ReadConsoleInput
inputRecord resb INPUT_RECORD.sizeof
; ...
ReadLine:
push ebp
mov ebp, esp
mov edi, [esp+4] ; buffer
mov ecx, [esp+8] ; size
.read:
; TODO: Don't call GetStdHandle over and over.
push STD_INPUT_HANDLE
call GetStdHandle
; Read one character from stdin
push readLineEventsRead
push 1
push inputRecord
push eax
call ReadConsoleInput
; Cancel if read console failed
cmp eax, 0
je .done
; Read next character if event was not a key press
cmp word [inputRecord+INPUT_RECORD.EventType], KEY_EVENT
jne .read
cmp byte [inputRecord+INPUT_RECORD.KeyEvent+KEY_EVENT_RECORD.bKeyDown], 1
jne .read
; Cancel if the character was a new line
mov dl, byte [inputRecord+INPUT_RECORD.KeyEvent+KEY_EVENT_RECORD.AsciiChar]
cmp dl, `\r`
je .done
cmp dl, `\n`
je .done
; Don't accept any more characters if buffer is full
cmp dword [ebp+4], 0
je .read
; Write character to buffer and advance it by 1, where the next
; character or a terminator can be placed.
mov byte [edi], dl
inc edi
; Decrement size to keep track of how much space we have left in
; the buffer.
dec dword [ebp+4]
; Write character to stdout
push 1
push inputRecord+INPUT_RECORD.KeyEvent+KEY_EVENT_RECORD.AsciiChar
call WriteN
jmp .read
.done:
mov byte [edi], 0
pop ebp
ret 8
And after I finally had something that kind of did what I wanted… I quickly switched to using ReadConsole instead and just trimmed the new line at the end
ReadLine:
mov ebx, [esp+4] ; buffer
mov ecx, [esp+8] ; size
push STD_INPUT_HANDLE
call GetStdHandle
push 0
push readLineEventsRead
push ecx
push ebx
push eax
call ReadConsole
; Get length of string
mov eax, [readLineEventsRead]
sub eax, 2
; Trim new line by terminating string after the read characters,
; where the line break would start.
add ebx, eax
mov byte [ebx], 0
ret 8
It’s fun to try to reinvent the wheel every once in a while, and it’s always a good lerning experience, but of course you should use whatever gets you to your goal the fastest, and implementing your own input reader is definitely not part of that if you can get around it.
But after finishing my readers and writers, I was done with my extended Hello World program.
extern Write
extern WriteLine
extern WriteLineF
extern ReadLine
section .data
nameQuery db `What's your name?\r\n: `,0
hello db 'Hello, %s!',0
inputGiven db `(Input given: %s)`,0
noName db 'Fine, keep your secrets.',0
section .bss
inputLen equ 64
input resb inputLen
section .text
_start:
push nameQuery
call Write
push inputLen
push input
call ReadLine
push eax
push input
push inputGiven
call WriteLineF
add esp,8
pop eax
cmp eax, 0
jne .sayHello
push noName
call WriteLine
jmp .end
.sayHello:
push input
push hello
call WriteLineF
add esp, 8
.end:
xor eax, eax
ret
> hello_world.exe
What's your name?
:
(Input given: )
Fine, keep your secrets.
> hello_world.exe
What's your name?
: exec
(Input given: exec)
Hello, exec!
From start to finish, it took me an entire weekend to read up on Assembler, decide on a toolchain, write print and read functions, get everything to a point where I can compile my code with a simple command, run it, and not get any crashes. Though I once more realized how small a layer C is on top of Assembly.
If you allow it yourself, Assembly isn’t so different from slightly less low level languages, and given a few convenience functions and clever macros, you can actually end up with something that isn’t so far off from base C. You can easily simulate constructs like ifs and loops, make accessing arguments and variables easier, even replace calling constructs, like with invoke, to arrive at something that looks closer to languages like C in some ways.
Assembly takes more time and effort, and making mistakes is much easier (if not guaranteed) for sure, but no matter how you approach it, using the C library, writing everything from scratch, or taking a mixed approach, at some point you will have a base library that you can use to write programs with relative ease, because you end up just using the right functions and macros for the job at hand.
Just like you might not want to use C for a project because you can be more productive in a higher level language, you probably shouldn’t use Assembly either if your goal is to just get things done and you have other options, not to mention that in most cases compilers can produce better Assembly code than you anyway… But I would recommend the experience to people who want to see the real low level, or who’re interested in reverse engineering, because learning a few things about Assembly, common patterns, and why some things are the way they are is eye opening. And it is kinda fun
If I were to continue writing in Assembly, I would definitely start adding macroinstructions like invoke to my code, but I would write them myself, so I still know exactly what they do, and how. Wouldn’t want to lose that learning experience.
Next stop: a server emulator in Assembly
It took them two days to find more people, but on Friday me and a hand full of others eventually received huge Excel tables to translate, with a note that they were to be sent back on Monday, 3 days later. The first problem we encountered was a total lack of information. I was only in direct contact with one other translator, who I knew coincidentally, but apparently nobody on the “team” was actively playing MapleStory, and even if we had been, this was a whole new quest line with new items, locations, NPCs, etc.
We had no idea how to translate certain words and it was difficult to understand some dialogues without knowing the story. We also had no way to discuss how to translate things so they’d be consistent throughout the whole patch, which resulted in certain things being called X in one quest and Y in the next, because multiple people were working on one language. There wasn’t even an explanation on how the markup worked, like when an item is displayed within a dialog, or when the text is colored. There were codes for this in the text that we were to translate. The only reason me and the other translator knew what we were doing here was that I knew all the codes because I had worked on MapleStory private servers before! To top it all off it was the weekend, so the person in charge wasn’t available for questions. Our only contact was the Mabinogi forum moderator who had recruited us, who didn’t have any information or contact to the other translators.
At the end of the weekend I had translated about 120€ worth of lines, a little more than I had to, the other translator about 60€. Each of us had specific ranges that we were to translate (e.g. rows 1000~2000), but we were told we didn’t have to get it done and we could also do more if we wanted to, presumably for consistency. Monday came and they wanted the translations to proof read and fix them up before the patch on Wednesday, and presumably also to fill in holes if somebody hadn’t translated their whole section, however that worked.
On Tuesday I received a checked version of my Excel file with notes about a few mistakes. I wasn’t supposed to fix them, I guess they just wanted to show me what I had done wrong. A few spelling mistakes, but nothing serious. Then it was Wednesday, the day of the update. Excited I downloaded the patch from the FTP server and turned on my data unpacker that I still had lying around from my pserver days. Since MapleStory stores a lot of information on the client side, including NPC information and (quest) dialogues, I could easily find and read my work. Surprisingly, what I found included all of my mistakes. Why they had someone proof read and correct everything when they didn’t plan to use that improved version is beyond me, but it was done. Happy about seeing all of my work having been used, I closed everything and waited for the promised contact about the payment, being amused by the other translator who actually reluctantly went and played the game for the first time, just to see his translations
One week later I hadn’t heard anything yet, neither had the other translator or the moderator. Carefully we started to ask when we would get paid, the person in charge only responding “be patient” via Skype, and inquires via eMail being ignored completely. Even calling wouldn’t get you anywhere, because nobody felt responsible for you.
We didn’t have any contract or anything (mistake on our part), so we couldn’t really do anything but wait and hope. About a year went by, during which I wrote them every other day. The person in charge had stopped responding to my messages months ago, the other translator had long given up. I didn’t expect anything at this point either, but I still continued, just for the heck of it. 14 months after we had finished the translation I received an answer to one of my inquires.
Nexon: “Translation? What? Huh? Uhu… okay, I guess we can give you some NX.” (NX = Nexon’s cash point currency)
Me: “NX… I’m only playing Mabinogi, which is going offline very soon, I don’t have any use for NX, we were promised something else…”
Nexon: “Sorry about that, blah blah blah, the only thing I can offer you is that money in NX, but if you don’t want that…” (“that money” was the sum I had given them in every mail for one year, didn’t sound like they actually had any information about the translation work that had taken place.)
Me: “Okay then, give me the NX. Not like I have a choice…”
Together with the compensation for all the money I spent on Mabinogi EU, that we got when the game was closed, minus some NX that I spent on Vindictus over the years when I was really bored, a large part of that “money” is still there to this day.

animal_do_something(animal). In comes Go, the programming language by Google.
Go is a modern C, and that’s the way you have to approach it. You don’t have classes, polymorphism, enumerators, or exceptions, you get what you’d expect from a language which’s design began with “we’ll start from C”. This is good for everybody who likes C, but might be bad for people who are too used to higher level languages. You can’t use Go like you use C# or Java for the most parts. Take classes for example.
I always thought of classes as glorified structs with some functions, but I gotta admit that coding without any kind of polymorphism is hard in the beginning. It takes a moment to get used to the idea that you don’t create a base class Animal, from which you inherit in Dog, from which you inherit in GermanShepherd, overwriting methods and Properties along the way. If all you have are structs, how do you solve that?
In Go you can embed structs in other structs. Doing so allows you to directly use anything that struct has access to. This way you can “inherit” fields and methods. Methods work similar to extension methods in C#, allowing you to specify functions that take any type as their first parameter, making it “this”.
type Animal struct {
Name string
}
type Dog struct {
Animal
}
func (animal Animal) MakeSound() {
fmt.Println("Moo")
}
func (dog Dog) MakeSound() {
fmt.Println("Woof")
}
func main() {
dog := Dog{Animal{"James"}}
fmt.Println(dog.Name) // James
dog.MakeSound() // Woof
dog.Animal.MakeSound() // Moo
}
While it’s not possible to cast a Dog to an Animal, you can pass Dog.Animal around to functions. However, by doing that so, you lose your “overridden” methods, because you then have a type Animal, not Dog. This problem is solved with interfaces.
A struct satisfies an interface automatically if it provides the correct methods. Aside from that, they work pretty much like you’d expect. This is a nice addition to the way C handled it.
type SoundMaker interface {
MakeSound()
}
func MakeSound(sm SoundMaker) {
sm.MakeSound()
}
...
MakeSound(dog) // Woof
While this looks weird at first, you can really do anything with this design, you don’t need classes and all their features. While there are cases where classes might’ve been a little more straight forward, generally you just have to think a bit different in order to reach your goal.
One thing that’s still strange to me are the packages. In Go you’re supposed to create a “workspace” in which you work, where you put all your Go projects. This is to allow the go tool to automatically download and compile dependencies only once. And it can even do that automatically, based on the imports in your project.
> go get github.com/someuser/somerepo
import "github.com/someuser/somerepo"
This is a cool feature, the versioning problems aside, that you can’t really solve if your goal is to allow downloading packages from anywhere.
But if you have a big project, that might have “sub-packages”, and you store your project in your workspace at “GOPATH/github.com/exectails/project”, the packages going down to “project/subfolder/anotherfolder”, you have to import them using the path “github.com/exectails/project/subfolder/anotherfolder” anywhere you need that package, Go looking for the files from the root, your GOPATH. While Go does support relative paths, it’s not idiomatic to use them apparently.
That’s probably just another thing you have to unlearn though. Packages aren’t namespaces and I suspect you’re supposed to design your projects much more modular, without too many sub-packages. The long imports would still be a little annoying, but at least they’d make sense.
That’s the two things I struggled with, looking into Go the past few days.
All in all I really like it. It’s very clean, it’s “non-magic” (it doesn’t hide much), and it’s fun to work with. It extends ideas from C very cleverly, changing just the right amount. For example, you don’t have enums, but you have consts!
const (
First = iota // 0
Second // 1
Third // 2
)
iota is a counter for that const scope, incrementing by one for each value. You only have to define the first one, the others automatically use the same. So far so normal, but let’s say you wanted to make a bitmask, where you don’t increment each value by 1, but shift it by X bit.
const (
First = 1 << iota // 1
Second // 2
Third // 4
)
This really blew my mind when I discovered it. It doesn’t change the world or anything, but it’s just so clever. That’s Go to me.
]]>