Last updated at Thu, 17 Aug 2017 23:53:42 GMT
A short, mostly-accurate history of character encodings
In the beginning, when you wanted to use a computer to store text, there were not many options - you inherited something from punchcards like EBCDIC or invented something convenient and unique to your system. Computers did not need to talk to each other, so there was not much point in standardizing between vendors. Things were pretty simple.
Then, there came the need for computers and vendors to interoperate and communicate. Thus, ASCII and later some of its cousins like ISCII, VISCII, and YUSCII were born. And now, large islands of computers, teletypes, modems, etc. could communicate with each other. ASCII only defined the lower 7 bits of a character, so other non-english encodings frequently used the upper half to define the extra language-specific characters. Things were a little confusing on the edges, but largely compatible, as long as you stuck to the lower 7-bits.
To deal with alternate languages MS-DOS, OS/2 and 16-bit Windows used a concept called code pages to define how the display characters from different languages. This was somewhat consistent as long as you stuck to Microsoft products (they were not a real standard), and as long as you did not need to look at more than one language at a time. Of course, there was still a limit on the number of characters one can represent with the extra half-byte in ASCII. Various multi-byte 'encodings' like Big5 and Shift JIS were invented to represent eastern languages like Chinese and Japanese, which can have thousands of characters. These were all largely incompatible and still did not work if one wanted to use more than one language. The term 'mojibake' describes some of the hilarious results that can occur when incompatible encodings are interpreted the wrong way. Things started getting really confusing.
Eventually, a new standard called Unicode was born to deal with the mess of these incompatible language encodings. It defined the concept of code points, which are numerical representations of all the characters, ligatures, symbols, and other doodads from the world's languages, along with ways to store and transmit them between computers.
Fast forward to the present: if you're an Android, iOS, Mac, Linux, BSD, or Unix user, you're probably used to Unicode working out of the box. This is thanks to a simple exchange and encoding standard called UTF-8. Being fully compatible with ASCII text, it does not necessarily require a lot of special support from existing programs in order to work. This is especially true in languages like C, where a UTF-8 string looks just like a C string. Sure, concepts like 'uppercase' and 'isalpha' take on new meaning and require thousands of lines of code to get right, but it is generally possible to write a program that ignores the concept of Unicode but still works with it without issues with UTF-8 strings.
Now, let's go back to around the time that the Unicode standard was born. Prior to UTF-8 existing, there was an encoding standard called UCS-2 that used 16-bit words (the WCHAR or wide character of you have done any Windows programming) to represent international characters. This had benefits for efficiency: doing things like finding the number of characters in a UCS-2 string is just as easy as a traditional C string. The designers of Microsoft Windows NT decided to adopt this fledgling standard and designed the whole system to be compatible with UCS-2. Of course, to make it compatible with all the C code in the world that did not understand wide characters and still wanted to use code pages and weird encodings, Windows also was designed with a compatibility layer that supports those too. Why 2 bytes? Well, 4 was seen as too much wasted space, and after all, 65535 characters should be enough for anyone.
As you can probably guess, it did not take long to determine that 16 bits was not actually sufficient to actually encode the world's character sets. Even efforts to merge common Japanese and Chinese characters, called Han Unification, did not yield enough space to represent all of the known characters (to say nothing of Emoji !)
So, the Unicode standard grew, Windows switched to a new UTF-16 encoding which allowed using multiple 16-bit words to represent the extended characters. Unfortunately, UTF-16 that has none of the speed benefits of UCS-2 and none of the compatibility or size benefits of UTF-8. Effectively, internationalization in Windows is based on a mixture of different encoding standards, old, new and transitional, where programs can opt in or out of seeing all characters by explicitly enabling Unicode, or only some by living in an ASCII or code-paged world.
Where does this tie in to the Metasploit Framework? Meterpreter now has Unicode Support on Windows. Why so long in coming? Effectively, Meterpreter was originally built as an ASCII-native C program that uses byte-size characters, taking advantage of the legacy support for ASCII and code pages in Windows. Flipping the switch wholesale to UTF-16 would break a number of things. The Ruby language (which the Metasploit Framework is based on) has gone through some growing pains as well with Unicode, but it is now a stable and first-class citizen.
Thankfully, since UTF-8 support in Ruby 2 is actually very good (some might say over-zealous!), and since Meterpreter was already dealing in C strings, switching to UTF-8 was largely painless. In fact, once the Unicode filters were turned off in the Metasploit Framework, the Posix, PHP and Python meterpreters worked largely unmodified. On Windows, some string gymnastics are applied to convert from UTF-8 on the wire to UTF-16 which Windows is now based on. The Java language's support for Unicode largely mirrors Windows (it uses UTF-16 internally), but there is a Pull Request to add support there as well.
Here are some examples of its usage - it is largely transparent:
meterpreter > ls Listing: e:\metasploit-framework\mytest\test ============================================ Mode Size Type Last modified Name ---- ---- ---- ------------- ---- 40777/rwxrwxrwx 102 dir 2015-03-20 15:43:52 -0500 . 40777/rwxrwxrwx 1564 dir 2015-03-23 09:50:17 -0500 .. 100666/rw-rw-rw- 15 fil 2015-03-17 02:50:27 -0500 プロトタイプ.txt2 meterpreter > mv プロトタイプ.txt2 ʇıoʃdsɐʇǝW.txt meterpreter > ls Listing: e:\metasploit-framework\mytest\test ============================================ Mode Size Type Last modified Name ---- ---- ---- ------------- ---- 40777/rwxrwxrwx 102 dir 2015-03-24 09:42:53 -0500 . 40777/rwxrwxrwx 1564 dir 2015-03-23 09:50:17 -0500 .. 100666/rw-rw-rw- 15 fil 2015-03-17 02:50:27 -0500 ʇıoʃdsɐʇǝW.txt
As a side note, the ls command gained a few new tricks of its own, such as viewing Windows 'Short' names for compatibility with older operating systems and MS-DOS:
meterpreter > ls -x c:\ Listing: c:\ ============ Mode Size Type Last modified Short Name Name ---- ---- ---- ------------- ---------- ---- 40777/rwxrwxrwx 0 dir 2015-02-19 03:31:32 -0600 $SYSRE~1 $SysReset 40777/rwxrwxrwx 0 dir 2013-08-22 03:45:52 -0500 DOCUME~1 Documents and Settings 40555/r-xr-xr-x 0 dir 2015-03-17 04:29:10 -0500 PROGRA~1 Program Files 40555/r-xr-xr-x 0 dir 2015-03-19 10:48:07 -0500 PROGRA~2 Program Files (x86) 40777/rwxrwxrwx 0 dir 2015-03-18 08:21:01 -0500 PROGRA~3 ProgramData 40777/rwxrwxrwx 0 dir 2015-03-24 06:57:11 -0500 SYSTEM~1 System Volume Information 40777/rwxrwxrwx 0 dir 2015-03-17 04:33:11 -0500 METASP~1 metasploit
As well as new sorting options.
meterpreter > ls -h Usage: ls [dir] [-x] [-S] [-t] [-r] -x Show short file names -S Sort by size -t Sort by time modified -r Reverse sort order
There are still other areas of the Meterpreter we are looking at to improve Unicode support, such as registry access and user enumeration, but this is a good first step.
Have fun using Unicode in meterpreter. A big thanks to OJ, HD and @zeroSteiner and @schierlm for contributing fixes, testing time and reviews.