Skype Reverse Engineering : The (long) journey ;)..

June 29, 2012

Here is a resumé, of my Skype reverse engineering experience, released with the code of some tools i’ve developed throughout this project, plus at the end of this article, some papers containing additional notes about the project.

Make sure you read Skype Reverse Engineering : Genesis before reading this post, to get the whole context..

First of all, let’s note that reverse engineering of the Skype protocol from network traffic is legal in some countries and that reverse engineering of the Skype protocol by inspecting/disassembling binaries is prohibited by the terms and conditions of Skype’s license agreement. However there are legal precedents when the reverse-engineering is aimed at interoperability of file formats and protocols. In addition, many countries specifically permit a program to be copied for the purposes of reverse engineering.

Skype: The Fortress

First of all, the Skype network is quite different from the other existing networks. The main difference is located in its architecture. Rather than operating on a client-server model as classics VoIP clients, the Skype client is based on a peer to peer model. Basically, it means that there is no centralized structure on which every Skype client relies on when it comes up to communications between users. Each Skype client can act as client and server at the same time. In a peer to peer (P2P) network, each client is designed by the term “node”.

Server Based Network

Server Based Network

P2P Network

P2P Network

Skype (almost like every P2P network) has its particular P2P architecture. It had to be adapted to the network uses. For example, unlike the P2P Kazaa network, designed for file sharing, the Skype network had to be optimized to transfer data in real time, where Kazaa network transfers data stored on nodes. In addition, Skype network still includes centralized networks entities, because unlike Kazaa network, Skype protocol had to implement user secured authentication, dynamic contacts lists management and ensure privacy.

The Skype user directory is entirely decentralized and distributed among the nodes in the network, which means the network can scale very easily to large sizes without a complex and costly centralized infrastructure.

Skype also routes communications through other Skype peers on the network to ease the crossing of NAT and firewalls. This, however, puts an extra burden on those who connect to the Internet directly, as their computers and network bandwidth may be used to route the communications of other users.

The anti-reverse-engineering securities

As previously evoked, the Skype protocol implemented in the Skype client, is heavily protected, obfuscated and designed to counter reverse engineering attempts. In order to properly study the protocol, my first aim was to clean out the binary from every protection, thus I’ll be able to observe the protocol working from the inner. Numerous attempts to study and/or reverse engineer the protocol to allow unofficial clients to use Skype have been undertaken, but none had ever been further enough to give a complete understanding of that curious protocol, and lead to achievement of unofficial implementation. Here is a repository of articles on Skype analysis:

The most complete of theses studies had been led by two researchers of the EADS-CCR laboratory, namely Phillipe Biondi and Fabrice Desclaux. In their published study “Silver Needle in the Skype” (cf Skype Reverse Engineering : Genesis), they brought up to daylight the mains protections implemented in the Skype binary, revealing the complexity of the study task, however, not developing the means they used to bypass them and go further in their analysis. On that basis, I had then to find a way to bypass these protections by myself.

As the target is a closed-source program, I have to deal with the binary code interpreted into x86 architecture assembly code. The main tool I used in my study is the well known “OllyDbg” (v1.10 ) with his “Phant0m” plugin. This tool allows me to load the client binary, to get of view on its assembly code (with logical hints added after OllyDbg analysis), to execute assembly instructions one by one, to set break points on the assembly code execution. I also used the well known “IDA” (v5.00). On the protocol analysis level, the main tool I used is called “OSpy” (v1.9.0.0). This one, allow me to set personalized hooks on specifics points in the binary during its execution, in order to gather important data as parameters passed to networks functions and many more.

Armed mainly with those tools, globally, protections I have encountered were:

  • The sensible code in the binary is encrypted with a hard-coded key, in order to avoid static analysis of the code. A procedure at the beginning of the Skype Client (SC) binary execution take in charge the decryption of the sensible and encrypted part of the code, after what, the decrypting routine pass the hand to the freshly decrypted code, and erase another part of the code. The latter action is performed in order to prevent the decrypted code to be saved from the running binary image in the computer’s memory. I had then to design a tool that extract exactly, parts of the code encrypted from the running image of the SC binary in computer’s memory, once decrypted by the procedure in charge of that, that also overwrite encrypted code with the decrypted one in the SC binary stored on the disk, and to patch the decrypting procedure to prevent this one of trying to decrypt already decrypted code and perform the code erasing. The quoted procedure also modify the Imports Table which contains references to every imported functions used by the binary. The tool I’ve designed also manage to fetch modifications applied on the Imports Table and report them into my new SC binary image on the computer disk.
SC Binary Behavior

SC Binary Behavior

/* Skype Sucker */
/* Author : Ouanilo MEDEGAN */

HANDLE hProcess = 0;
char *Buffer;
char *BigBuffer;
long Size;
FILE *result;

result = fopen("Skype.exe", "rb");
fseek(result, 0, SEEK_END);
Size = ftell(result);
fseek(result, 0, SEEK_SET);
BigBuffer = malloc(Size);
fread(BigBuffer, Size, 1, result);

Buffer = malloc(0x49F000);
ZeroMemory(Buffer, 0x49F000);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
hProcess = OpenProcess(PROCESS_VM_READ,FALSE, 0xF0C);
ReadProcessMemory(hProcess, (LPVOID)0x01FB0000, Buffer, 0x49F000,NULL);
result = fopen("Skype.exe", "wb");
memcpy(BigBuffer + 3429792, Buffer, 0x49F000);
fwrite(BigBuffer, Size, 1, result);

return 0;


  • The SC Binary also executes some procedures in order to detect running debuggers and stop execution upon positive detection. In my case no need to patch because OllyDbg was (strangely) not in the list of spotted debuggers. Indeed it just spot one debugger, the notorious but old “SoftIce”, and the detection is performed by various tricks using encrypted strings.
OllyDbg View

OllyDbg View

  • Now by far the most efficient protection implemented in the binary. Indeed, the code contains about 300 polymorphic  integrity agents. Their function: executed randomly, they are in charge to check various part of the code and verify by various ways and criteria the integrity of the checked part of code. They then prevents from patching the sensible parts of the code, and even prevents from setting breakpoints on the code execution, as far as software breakpoints inserted by debugger change the code of the running image in the computer’s memory. The main difficulty bypassing those agents is the fact that they are all different one from another, by the size, by the way of checking and also by the checked criteria which are not trivial (for example the result of a check can be used to determine the next execution’s path of the code, or could be the address of another agent). However, after deep analysis, it had been spotted that they all got the same scheme in their design, sort of a fingerprint. They are composed of :
    • An initialization address
    • A loop
    • A lookup
    • A test/computation

    MOV R32, CONST ;Pattern #1
    ANY 8
    MOV R32, R32
    ANY 8
    MOV R32, [R32+CONST]
    XOR R32, CONST ;Pattern #2
    ANY 8
    ANY 8
    ANY 8
    MOV R32, [R32+CONST]
    XOR R32, R32 ;Pattern #3
    ANY 8
    MOV R32, [R32+CONST]
    ANY 8
    DEC R32
    XOR R32, R32 ;Pattern #4
    ANY 8
    MOV R32, CONST
    ANY 8
    DEC R32


    Aware of that, I have been able, using the pattern research engine of OllyDbg, to spot almost the totality of the agents. Once I’ve got a spot on them all, I tuned the code of an x86 emulator, name “x86emu” plugged into IDA, in order to make him compute the results waited by every spotted agent at the end of their execution. I then designed an OllyDbg plug-in (using its Plug-in Development Kit) which patch the SC binary integrity agents, making them directly set the expected values before reaching their final test. I’ve then been able to patch the SC binary code as I wanted, set breakpoints on every parts of the code, and get a faster SC binary as hundreds of controls weren’t any more performed.

    Checksum Agent View

    Checksum Agent View Exemple#1

    Checksum Agent View

    Checksum Agent View Exemple#2

    Tuned x86 emulator window after execution on spotted agents

    Tuned x86 emulator window after execution on spotted agents

    /* Skype Checksum Agents Patcher Plugin */
    /* Author : Ouanilo MEDEGAN */
    #include "plugin.h"
    HINSTANCE hinst; // DLL instance
    HWND hwmain; // Handle of main OllyDbg window
    typedef struct patchs_s
     unsigned int wtp;
     unsigned int end;
     char *code;
    } patchs;
    int checks[] = {0x74ffdb,
    patchs ptab[] = {
    {0x74fff9, 0x750009, "MOV ECX, 0c445513e"},
    {0x75423e, 0x754251, "MOV EDI, 07a51760"},
    {0x7566ed, 0x7566fd, "MOV ECX, 07f26c1cd"},
    {0x75834e, 0x75835d, "MOV EDX, 08143833c"},
    {0x75917b, 0x75918a, "MOV EDI, 0ac10c50d"},
    {0x7602fd, 0x76030d, "MOV EDI, 07b43df78"},
    {0x760491, 0x7604a2, "MOV EAX, 013c45465"},
    {0x770707, 0x770717, "MOV EDX, 02d86e921"},
    {0x770c19, 0x770c2c, "MOV ECX, 023d836c7"},
    {0x77100d, 0x77101f, "MOV ESI, 0b7d7ee34"},
    {0x771fc5, 0x771fd6, "MOV EAX, 08ffdfc46"},
    {0x7729d4, 0x7729e7, "MOV EAX, 0aa2552d6"},
    {0x772a7d, 0x772a8d, "MOV EDX, 0dd02dff5"},
    {0x77ad3a, 0x77ad4b, "MOV EBX, 01f1e9f7b"},
    {0x77bb0c, 0x77bb1b, "MOV EAX, 056ea89cf"},
    {0x77d073, 0x77d07f, "MOV ESI, 07d4e5bf"},
    {0x77ec8d, 0x77eca0, "MOV ECX, 09c78fcfe"},
    {0x780de7, 0x780df7, "MOV EDI, 02ca58b26"},
    {0x78166f, 0x781682, "MOV EAX, 02d18848f"},
    {0x78282b, 0x78283e, "MOV ECX, 0d2d3a1f5"},
    {0x78342d, 0x78343d, "MOV EDI, 0aa0d1086"},
    {0x783d76, 0x783d86, "MOV EDI, 054d99c86"},
    {0x785687, 0x785696, "MOV EDI, 09c9fcf4b"},
    {0x785aa0, 0x785ab3, "MOV ECX, 03a77aa74"},
    {0x786162, 0x786173, "MOV EDI, 024367f84"},
    {0x7881d4, 0x7881e5, "MOV EAX, 0764de354"},
    {0x78831d, 0x78832f, "MOV EBX, 0c0aeb096"},
    {0x790812, 0x790822, "MOV EBX, 0e7393655"},
    {0x7a2b8c, 0x7a2b9c, "MOV ESI, 02cbbbb5a"},
    {0x7a2f41, 0x7a2f4e, "MOV EBX, 0995c72e3"},
    {0x7a34f5, 0x7a3505, "MOV EAX, 01fe3bec1"},
    {0x7a36e2, 0x7a36f5, "MOV EDI, 0d4027302"},
    {0x7a8d19, 0x7a8d29, "MOV EBX, 0518f95ba"},
    {0x7a97a8, 0x7a97b8, "MOV EDI, 0c0bad4af"},
    {0x7aa24a, 0x7aa25a, "MOV EDX, 086760316"},
    {0x7ac1ce, 0x7ac1dc, "MOV EDI, 0e6503aaa"},
    {0x7aedfb, 0x7aee0e, "MOV EAX, 02091582b"},
    {0x7af6aa, 0x7af6b9, "MOV ESI, 0aab731b2"},
    {0x7b2309, 0x7b2319, "MOV ECX, 0336b4c77"},
    {0x7b31c1, 0x7b31d0, "MOV EAX, 0a9dc7b7e"},
    {0x7b6d75, 0x7b6d85, "MOV EAX, 033156dcb"},
    {0x7b6f6f, 0x7b6f7f, "MOV EDI, 068c30e6"},
    {0x7b7731, 0x7b7744, "MOV EAX, 03a739a6c"},
    {0x7b7b36, 0x7b7b49, "MOV EBX, 0dd5eb0ac"},
    {0x7b8df4, 0x7b8e06, "MOV ECX, 0cda6236b"},
    {0x7b93e1, 0x7b93f1, "MOV EDX, 0909f211e"},
    {0x7bc122, 0x7bc12f, "MOV EDX, 0423c1dd2"},
    {0x7bdf0a, 0x7bdf1a, "MOV EDX, 0dd8ff70f"},
    {0x7be324, 0x7be334, "MOV EAX, 09cfc6204"},
    {0x7bee37, 0x7bee47, "MOV EAX, 08033cb72"},
    {0x7befd0, 0x7befe0, "MOV EBX, 0dd57229d"},
    {0x7bf33d, 0x7bf34b, "MOV EDI, 0303d25eb"},
    {0x7c2b2b, 0x7c2b3a, "MOV ESI, 0e5ea18bb"},
    {0x7c4298, 0x7c42a9, "MOV EDI, 0f9cead47"},
    {0x7c4c8e, 0x7c4c9f, "MOV ESI, 09ccb01a2"},
    {0x7c5d8b, 0x7c5d9c, "MOV ECX, 09d859997"},
    {0x7c720e, 0x7c721e, "MOV EBX, 0d0a84b9f"},
    {0x7c7f40, 0x7c7f4f, "MOV EDI, 0d3e4f2c7"},
    {0x7c8eb4, 0x7c8ec6, "MOV ECX, 0427b5851"},
    {0x7cbc5a, 0x7cbc6d, "MOV EAX, 02d194eb0"},
    {0x7d75e4, 0x7d75f4, "MOV EBX, 0b024bef9"},
    {0x7d83a8, 0x7d83b9, "MOV EBX, 0547f095b"},
    {0x7da2ef, 0x7da2ff, "MOV ESI, 0e5ce5b73"},
    {0x7db59e, 0x7db5b0, "MOV ECX, 099b9f3a8"},
    {0x7dcae3, 0x7dcaf1, "MOV EAX, 0b77e8d82"},
    {0x7deef0, 0x7def03, "MOV EAX, 0e734dc4e"},
    {0x7e5044, 0x7e5057, "MOV EDX, 07febc729"},
    {0x7e5eaa, 0x7e5eb7, "MOV EDI, 0ac34b755"},
    {0x7e698a, 0x7e699d, "MOV ECX, 030143199"},
    {0x7e8688, 0x7e8699, "MOV EAX, 0149e7219"},
    {0x7eb1c6, 0x7eb1d8, "MOV EAX, 0929f271f"},
    {0x7eb78e, 0x7eb79e, "MOV EDX, 08b216d4a"},
    {0x7ed54b, 0x7ed55e, "MOV ESI, 08017bf81"},
    {0x7f5a62, 0x7f5a71, "MOV EAX, 099f2503f"},
    {0x7f9ba4, 0x7f9bb5, "MOV EAX, 05175280f"},
    {0x7f9eb3, 0x7f9ec3, "MOV EDI, 0a955c470"},
    {0x7fa2bb, 0x7fa2c8, "MOV EAX, 02dfc1afa"},
    {0x7fd298, 0x7fd2a7, "MOV ECX, 01f5d41fc"},
    {0x7fd863, 0x7fd872, "MOV ESI, 0e0812fb9"},
    {0x7fe4b2, 0x7fe4c0, "MOV EDI, 099e325dd"},
    {0x7fec24, 0x7fec35, "MOV EBX, 054b340c3"},
    {0x7ff629, 0x7ff63a, "MOV EDX, 0b45090bf"},
    {0x7ffe0f, 0x7ffe1d, "MOV ECX, 0e5bc5382"},
    {0x7fff12, 0x7fff22, "MOV ECX, 08ac85e98"},
    {0x800146, 0x800157, "MOV ESI, 090201620"},
    {0x800e05, 0x800e17, "MOV ECX, 0e27686f6"},
    {0x8014ee, 0x801500, "MOV ECX, 0d2c7b21d"},
    {0x802290, 0x80229e, "MOV EAX, 0ec8ae7f8"},
    {0x8028b1, 0x8028c3, "MOV ESI, 054b9f847"},
    {0x803729, 0x803737, "MOV EBX, 0cddb39d5"},
    {0x804943, 0x804956, "MOV ESI, 013b8244d"},
    {0x804f0c, 0x804f1c, "MOV EAX, 06c9aae5b"},
    {0x805022, 0x805032, "MOV ESI, 055070ae1"},
    {0x805918, 0x80592b, "MOV EBX, 030124195"},
    {0x806705, 0x806714, "MOV EBX, 0147affd2"},
    {0x806c7e, 0x806c8c, "MOV EDX, 06e536285"},
    {0x806d43, 0x806d53, "MOV ECX, 07a9b5d13"},
    {0x806df1, 0x806e00, "MOV EAX, 054a8a4b4"},
    {0x806ee8, 0x806ef6, "MOV EBX, 02e42c699"},
    {0x80771d, 0x807730, "MOV EBX, 0e638907b"},
    {0x808bce, 0x808be1, "MOV EDX, 0f9e77f79"},
    {0x8092a8, 0x8092b6, "MOV EDX, 06efe8faf"},
    {0x809b0b, 0x809b1b, "MOV EDX, 034f306d1"},
    {0x809d8a, 0x809d9d, "MOV EAX, 0acf4b4d5"},
    {0x80af39, 0x80af46, "MOV EDI, 071094df2"},
    {0x80bc57, 0x80bc68, "MOV EBX, 0b4e5d2d2"},
    {0x80bf65, 0x80bf72, "MOV ESI, 076f6bf82"},
    {0x80c644, 0x80c656, "MOV EDI, 0e3df1200"},
    {0x80c874, 0x80c887, "MOV EAX, 090c5b6fe"},
    {0x80c9af, 0x80c9be, "MOV EBX, 026599582"},
    {0x80caf5, 0x80cb02, "MOV EDX, 030a145e7"},
    {0x80cca8, 0x80ccb8, "MOV ESI, 06bc5d1d1"},
    {0x80d0e8, 0x80d0f8, "MOV ESI, 055c8cecf"},
    {0x80d1e5, 0x80d1f4, "MOV EDI, 0ffb2b709"},
    {0x80d419, 0x80d42b, "MOV ESI, 052956b56"},
    {0x812478, 0x81248a, "MOV ESI, 0c51f8d42"},
    {0x813333, 0x813344, "MOV ECX, 018b4b2ca"},
    {0x813c2f, 0x813c3e, "MOV EDI, 0fb585983"},
    {0x814568, 0x814577, "MOV EBX, 0a393b14"},
    {0x815a39, 0x815a49, "MOV ECX, 01552376d"},
    {0x846e1c, 0x846e29, "MOV EDX, 0895a8f12"},
    {0x8497e9, 0x8497f9, "MOV ESI, 03c8b3592"},
    {0x849ccc, 0x849cdf, "MOV EAX, 03fbb1f8a"},
    {0x84a165, 0x84a172, "MOV EBX, 0e036d6b9"},
    {0x84d842, 0x84d855, "MOV EDX, 021242c95"},
    {0x84da31, 0x84da42, "MOV EAX, 0fedb8372"},
    {0x84ef44, 0x84ef54, "MOV EBX, 0ae164cd1"},
    {0x85405f, 0x85406f, "MOV EBX, 09a52989"},
    {0x8548b4, 0x8548c7, "MOV EAX, 0ac0639e0"},
    {0x8552d9, 0x8552ea, "MOV EAX, 091c4f5b9"},
    {0x861ae8, 0x861afa, "MOV ESI, 093840a1c"},
    {0x861f36, 0x861f45, "MOV ESI, 0419edd7b"},
    {0x8632b1, 0x8632c1, "MOV EBX, 085ab7503"},
    {0x863ca5, 0x863cb8, "MOV EDX, 07dbfb5b"},
    {0x8641a6, 0x8641b6, "MOV ECX, 0e173e1e9"},
    {0x8670e5, 0x8670f8, "MOV ECX, 0e0b9151f"},
    {0x867d1e, 0x867d2e, "MOV EBX, 09d26fa68"},
    {0x86841d, 0x86842a, "MOV ESI, 012ec7977"},
    {0x868939, 0x86894b, "MOV EDX, 068904b7c"},
    {0x869090, 0x86909e, "MOV EDX, 0f1c6291c"},
    {0x869e49, 0x869e5c, "MOV EAX, 0f1d2405f"},
    {0x86a52b, 0x86a53d, "MOV ESI, 06193d267"},
    {0x86d1f8, 0x86d206, "MOV EAX, 0ebf84584"},
    {0x8779f8, 0x877a07, "MOV ESI, 0f70a3528"},
    {0x877a7c, 0x877a8a, "MOV EDX, 0e550009b"},
    {0x877f4e, 0x877f5d, "MOV EAX, 0cd8400bd"},
    {0x87833c, 0x87834f, "MOV EBX, 0b44b3cde"},
    {0x87b769, 0x87b777, "MOV EDX, 04db966e"},
    {0x884288, 0x884298, "MOV EDX, 01553793c"},
    {0x89133a, 0x89134a, "MOV ESI, 02a7014c9"},
    {0x8922ab, 0x8922be, "MOV EBX, 0c38e0a6"},
    {0x89251d, 0x89252f, "MOV EBX, 0c98a0cfb"},
    {0x894556, 0x894565, "MOV EBX, 086018a98"},
    {0x898714, 0x898724, "MOV EDX, 0376c710b"},
    {0x898845, 0x898853, "MOV ECX, 0f3b765ab"},
    {0x89917f, 0x89918c, "MOV EAX, 0d6e5d43b"},
    {0x89b342, 0x89b354, "MOV EDX, 059941057"},
    {0x89e98d, 0x89e99d, "MOV ESI, 0494ffa1f"},
    {0x8a3476, 0x8a3487, "MOV EDI, 0d2b785"},
    {0x8a62ea, 0x8a62fd, "MOV EBX, 08bd76a45"},
    {0x8a71ce, 0x8a71dc, "MOV EDX, 0a210fda6"},
    {0x8a78eb, 0x8a78fb, "MOV ECX, 0540c5ef9"},
    {0x8a8645, 0x8a8655, "MOV EDX, 0627f206c"},
    {0x8ac0bd, 0x8ac0cc, "MOV ESI, 08e981340"},
    {0x8acdd5, 0x8acde5, "MOV EDI, 0febe5a7c"},
    {0x8b13a4, 0x8b13b4, "MOV ECX, 013ce0803"},
    {0x8b1de9, 0x8b1dfb, "MOV ECX, 04ea685c7"},
    {0x8b42c4, 0x8b42d5, "MOV EDX, 0476254ba"},
    {0x8b4919, 0x8b4928, "MOV EBX, 0f84be5e4"},
    {0x8b5c49, 0x8b5c59, "MOV EDX, 0609bbc80"},
    {0x8ba905, 0x8ba917, "MOV EDI, 05d22fa5c"},
    {0x8bab30, 0x8bab42, "MOV EAX, 05be049f2"},
    {0x8bbfc1, 0x8bbfd1, "MOV EBX, 011ac3604"},
    {0x8be635, 0x8be645, "MOV EDX, 016c52865"},
    {0x8c400e, 0x8c401d, "MOV ECX, 0ed9cd2db"},
    {0x8c90cb, 0x8c90db, "MOV ECX, 027208727"},
    {0x8cb8fa, 0x8cb90b, "MOV EDI, 0e4a5842d"},
    {0x8cbabd, 0x8cbad0, "MOV EBX, 0bed28bd1"},
    {0x8cc120, 0x8cc130, "MOV EBX, 0d4f92d37"},
    {0x8ce109, 0x8ce119, "MOV ECX, 0c3b20a27"},
    {0x8d01f7, 0x8d0208, "MOV EDX, 0238a774f"},
    {0x8d0a5f, 0x8d0a6e, "MOV EDX, 02d698aea"},
    {0x8d1a9c, 0x8d1aa9, "MOV EAX, 074ec115a"},
    {0x8d26e1, 0x8d26f0, "MOV ECX, 078b53a47"},
    {0x8d4752, 0x8d4762, "MOV EBX, 0e8dcb02c"},
    {0x8d4f17, 0x8d4f26, "MOV ESI, 0733cb8e2"},
    {0x8d5721, 0x8d5731, "MOV EAX, 07351160b"},
    {0x8dc0ee, 0x8dc0fd, "MOV EDX, 02337f2eb"},
    {0x8dcb7e, 0x8dcb8c, "MOV ECX, 055899497"},
    {0x8e0df1, 0x8e0e01, "MOV ESI, 0911efc6"},
    {0x8e14e2, 0x8e14f2, "MOV EBX, 0918e6486"},
    {0x8e2297, 0x8e22a8, "MOV EDX, 0ed078080"},
    {0x8e257f, 0x8e258d, "MOV EDI, 0d79f6837"},
    {0x8e3745, 0x8e3756, "MOV EAX, 04ff61b79"},
    {0x8e4efb, 0x8e4f0c, "MOV ESI, 0951858ca"},
    {0x8e4fd1, 0x8e4fe1, "MOV EAX, 0373fa366"},
    {0x8e6b59, 0x8e6b6c, "MOV EAX, 0a4e1d4fa"},
    {0x8e77d1, 0x8e77df, "MOV EDI, 0df1e173d"},
    {0x8e8a4e, 0x8e8a5d, "MOV EDI, 08e10e996"},
    {0x8e9953, 0x8e9963, "MOV ECX, 0777fadde"},
    {0x8ea9d7, 0x8ea9e9, "MOV ECX, 0f3eadf10"},
    {0x8ebfe3, 0x8ebff3, "MOV ESI, 050667a1a"},
    {0x8ee0dc, 0x8ee0ef, "MOV EAX, 01f3501a3"},
    {0x8ee2d5, 0x8ee2e6, "MOV EDX, 055aad44f"},
    {0x8ee738, 0x8ee747, "MOV ESI, 0b2fbff36"},
    {0x8ef253, 0x8ef266, "MOV EAX, 06ad8ec8d"},
    {0x8ef88f, 0x8ef8a1, "MOV ESI, 025480f72"},
    {0x8efd44, 0x8efd55, "MOV EDX, 0a4c2184c"},
    {0x8f01d3, 0x8f01e3, "MOV EDX, 07442d802"},
    {0x8f0b6d, 0x8f0b7b, "MOV EDX, 03a326175"},
    {0x8f11b9, 0x8f11c8, "MOV EBX, 04a8ffb13"},
    {0x90546e, 0x90547e, "MOV EDI, 09c89e0af"},
    {0x924cff, 0x924d0f, "MOV ESI, 092e232d3"},
    {0x9261f2, 0x926204, "MOV EDI, 0189bc8ff"},
    {0x928b10, 0x928b21, "MOV EBX, 07a13a572"},
    {0x928e93, 0x928ea6, "MOV EDX, 0c026ebb8"},
    {0x93aea9, 0x93aeb9, "MOV EDI, 01131e7ed"},
    {0x93d201, 0x93d20f, "MOV EBX, 064a2d76a"},
    {0x93f39b, 0x93f3ae, "MOV ESI, 0bbe3e8c3"},
    {0x940284, 0x940294, "MOV ESI, 0a22eb245"},
    {0x94058e, 0x94059d, "MOV EBX, 02d33939c"},
    {0x940a5c, 0x940a6c, "MOV EAX, 0456de1a1"},
    {0x940bbb, 0x940bce, "MOV ESI, 0deffe75a"},
    {0x946a64, 0x946a72, "MOV ESI, 0aa8e9c06"},
    {0x9484d4, 0x9484e5, "MOV EDX, 08e7e081c"},
     {0, 0, NULL}
    void *build_ext_model(char *cmd_sequence)
     t_extmodel *diamod;
     t_extmodel *pmod;
     char diaasm[ARGLEN];
     char cmd[ARGLEN];
     int n, nseq, errpos, offset, i, j, k, m, good, validcount;
     char *pa;
     char errtext[512];
     char t[512];
     t_extmodel model;
    m = 0;
     diamod = malloc(sizeof(t_extmodel) * NSEQ * NMODELS);
     memset(t, 0, 512);
     memset(errtext, 0, 512);
     memset(diaasm, 0, ARGLEN);
     if (cmd_sequence == NULL)
     return NULL;
     n = strlen(cmd_sequence);
     if (n == 0)
     return NULL;
     memcpy(diaasm, cmd_sequence, n);
     diaasm[sizeof(diaasm) - 1] = '\0';
     memset(diamod, 0, sizeof(t_extmodel) * NSEQ * NMODELS);
     pa = diaasm;
     errtext[0] = '\0';
     for (nseq = 0; *pa != '\0'; nseq++)
     Addtolist(0, 1, "treat : nseq = %d, where = %s", nseq, pa);
     errpos = 0;
     offset = pa - diaasm;
     if (nseq >= NSEQ)
     sprintf(errtext, "Sequence is limited to %i commands", NSEQ);
     n = 0;
     while (n < TEXTLEN && *pa != '\r' && *pa != '\n' && *pa != '\0')  cmd[n++] = *pa++;  if (n >= TEXTLEN)
     errpos = TEXTLEN-1;
     strcpy(errtext, "Command is too long");
     while (*pa == '\r' || *pa == '\n')
     cmd[n] = '\0';
     for (i = 0; cmd[i] != '\0' && i < (TEXTLEN - 4); i++)  {  if (cmd[i]!=' ' && cmd[i]!='\t')  break;  }  if (memicmp(cmd + i, "ANY", 3) == 0 && (cmd[i + 3] == ' ' || cmd[i + 3] == '\t' || cmd[i + 3] == '\0' || cmd[i + 3] == ';'))  {  i += 3;  while (cmd[i] == ' ' || cmd[i] == '\t')  i++;  validcount = 0;  for (n = 0; ; i++)  {  if (cmd[i] >= '0' && cmd[i] <= '9')  n = n * 16 + cmd[i] - '0';  else if (cmd[i] >= 'A' && cmd[i] <= 'F')  n = n * 16 + cmd[i] - 'A' +10;  else if (cmd[i] >= 'a' && cmd[i] <= 'f')
     n = n * 16 + cmd[i] - 'a' +10;
     while (cmd[i] == ' ' || cmd[i] == '\t')
     if (cmd[i] != '\0' && cmd[i] != ';')
     errpos = i;
     strcpy(errtext, "Syntax error");
     if (validcount == 0)
     n = 1;
     if (n  8)
     errpos = i;
     strcpy(errtext, "ANY count is limited to 1..8");
     if (nseq == 0)
     strcpy(errtext, "'ANY' can't be the first command in sequence");
     diamod[nseq * NMODELS].isany = n;
     diamod[nseq * NMODELS].cmdoffset = offset;
     i = 0;
     for (j = 0; i < NMODELS; j++)
     Addtolist(0, 1, "building : i = %d, j = %d, errtext = %s", i, j, errtext);
     good = 0;
     for (k = 0; k < 4 && i < NMODELS; k++)  {  if (i >= NMODELS)
     memset(&model, 0, sizeof(model));
     n = Assemble(cmd, 0, (t_asmmodel *)&model, j | 0x80000000, k, (i == 0 ? errtext : t));
     if (n > 0)
     good = 1;
     for (m = 0, pmod = diamod + nseq * NMODELS; m < i; pmod++, m++)  {  if (n == pmod->length && memcmp(model.code, pmod->code, n) == 0 && memcmp(model.mask, pmod->mask, n) == 0 && memcmp(model.ramask, pmod->ramask, n) == 0 && memcmp(model.rbmask, pmod->rbmask, n) == 0)
     if (m >= i)
     diamod[nseq * NMODELS + i] = model;
     diamod[nseq * NMODELS + i].cmdoffset = offset;
     else if (i == 0)
     errpos = -n;
     if (good == 0)
     Addtolist(0, 1, "end of building : errtext = %s", errtext);
     if (errtext[0] != '\0')
     Addtolist(0, 1, "end of building end end");
     if (nseq == 0 && errtext[0] == '\0')
     strcpy(errtext, "Empty sequence");
     if (diamod[(nseq - 1) * NMODELS].isany != 0)
     strcpy(errtext, "'ANY' can't be the last command in sequence");
     if (errtext[0] != '\0')
     MessageBox(hwmain, errtext, "SkyCks Plugin", MB_OK | MB_ICONERROR);
     errpos = errpos + nseq + m;
     if (errpos)
     return (diamod);
     return (diamod);
    BOOL WINAPI DllEntryPoint(HINSTANCE hi,DWORD reason,LPVOID reserved)
     if (reason == DLL_PROCESS_ATTACH)
     hinst = hi;
     return 1;
    extc int _export cdecl ODBG_Plugindata(char shortname[32])
     strcpy(shortname, "Skycks");
     return PLUGIN_VERSION;
    extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw,ulong *features)
     if (ollydbgversion < PLUGIN_VERSION)
     return -1;
    hwmain = hw;
    Addtolist(0, 0, "Skype CheckSum Agents Patcher Plugin");
     Addtolist(0, -1, " Copyright (C) 2006 Ouanilo MEDEGAN");
     return 0;
    extc void _export cdecl ODBG_Pluginmainloop(DEBUG_EVENT *debugevent)
    extc int _export cdecl ODBG_Pluginmenu(int origin,char data[4096],void *item)
     switch (origin)
     case PM_MAIN:
     strcpy(data, "0 &Spot Them All|1 &Update Comments|2 &Dump Comments|3 &Clean Comments|4 &About");
     return 1;
     return 0;
    extc void _export cdecl ODBG_Pluginaction(int origin,int action,void *item)
     t_dump *cpu;
     ulong base;
     char name[TEXTLEN];
     char errtext[TEXTLEN];
     char nop = 0x90;
     int res;
     char *buffer;
     char saddr[9];
     char names[20];
     FILE *file;
     int i, j, k;
     t_asmmodel model;
    if (origin==PM_MAIN)
     cpu = (t_dump *)Plugingetvalue(VAL_CPUDASM);
     switch (action)
     case 0:
     i = 0;
     Dumpbackup(cpu, BKUP_SAVEDATA);
     while (ptab[i].wtp)
     errtext[0] = 0;
     Assemble(ptab[i].code, ptab[i].wtp, &model, 0, 0, errtext);
     if (errtext[0])
     Addtolist(0, 1, "error : %s with %s", errtext, ptab[i].code);
     Addtolist(0, -1, "success at %p with %s", ptab[i].wtp, ptab[i].code);
     Writememory(model.code , ptab[i].wtp, model.length, MM_RESTORE);
     j = k = 0;
     j = ptab[i].end - (ptab[i].wtp + model.length);
     while (j)
     Writememory(&nop, ptab[i].wtp + model.length + k, 1, MM_RESTORE);
     //Setbreakpoint(ptab[i].wtp, TY_ACTIVE, 0);
     Dumpbackup(cpu, BKUP_DELETE);
     Dumpbackup(cpu, BKUP_LOADCOPY);
     case 1:
     i = 0;
     while (checks[i])
     memset(names, 0, 20);
     sprintf(names, "Chk #%d", i);
     Quickinsertname(checks[i], NM_COMMENT, names);
     case 2:
     buffer = malloc(4096 * 4);
     memset(buffer, 0, 4096 * 4);
     strcat(buffer, "int checks[] = {");
     //Findallsequences(cpu, build_ext_model("XOR R32, R32\r\nANY 8\r\nJMP OFFSET\r\nANY 8\r\nSUB R32, CONST\r\nANY 8\r\nDEC R32"), 0, "Skype CheckSum Layers");
     memset(name, 0, TEXTLEN);
     res = 0;
     base = (ulong)Plugingetvalue(VAL_MAINBASE);
     res = Findname(base, NM_COMMENT, name);
     Addtolist(0, -1, "base = %p, res = %d, name = %s", base, res, name);
     memset(name, 0, TEXTLEN);
     memset(saddr, 0, 9);
     while ((res = Findnextname(name)) != 0)
     if (strstr(name, "Chk #"))
     Addtolist(0, 0, "found : @", name, res);
     sprintf(saddr, "%#8x", res);
     strcat(buffer, saddr);
     strcat(buffer,",\r\n ");
     memset(name, 0, TEXTLEN);
     memset(saddr, 0, 9);
     sprintf(saddr, "%#8x", 0);
     strcat(buffer, saddr);
     strcat(buffer,",\r\n ");
     strcat(buffer, "}");
     file = fopen("tab.txt", "wb");
     fwrite(buffer, strlen(buffer), 1, file);
     MessageBox(0, "Job Done", "Dump", MB_OK);
     case 3:
     /*memset(name, 0, TEXTLEN);
     res = 0;
     base = (ulong)Plugingetvalue(VAL_MAINBASE);
     res = Findname(base, NM_COMMENT, name);
     memset(name, 0, TEXTLEN);
     while ((res = Findnextname(name)) != 0)
     if (strstr(name, "Chk #"))
     Addtolist(0, 0, "found : @", name, res);
     Insertname(res, NM_COMMENT, 0);
     memset(name, 0, TEXTLEN);
     memset(saddr, 0, 9);
     base = (ulong)Plugingetvalue(VAL_MAINBASE);
     Deletenamerange(base, 0xbeefff, NM_COMMENT);
     case 4:
     "Skype CheckSum Agents Patcher Plugin\n"
     "Copyright (C) 2006 Ouanilo MEDEGAN",
    extc void _export cdecl ODBG_Pluginreset(void)
    extc int _export cdecl ODBG_Pluginclose(void)
     return 0;
    extc void _export cdecl ODBG_Plugindestroy(void)


    Checksum Agent After Automatic Patch

    Checksum Agent After Automatic Patch

  • The binary also perform execution timing checks on functions in order to detect if being debugged or not, as far as debugged execution is really slower than normal execution. On positive detection, the binary set trap to the debugger, changing crucial values for the execution, or redirecting execution on bad paths, leading irremediably to the process death. This is easily defeated by the OllyDbg Phant0m plugin and its GetTickCount Hooks.
  • Some routines in the binary, performs its integrity check.. Few patches and let’s continue !
    Integrity Check Exemple #1

    Integrity Check Exemple #1

    Integrity Check Exemple #2

    Integrity Check Exemple #2

    Integrity Check Exemple #3

    Integrity Check Exemple #3


  • The sensible code of the SC binary is fulfilled with plenty of junk code, obfuscating the real code reading. But this measure just slows down the analysis and forced me to be more careful reading the assembly code. But in certain part of the code, some special tricks are used, making impossible to follow the execution. On those points, extreme logic comes out to be the sole solution to go further in the analysis.

    : The RC4 key (for the control stream encryption, see details further) expansion from seed routine is based on “exceptions redirections”. Code exceptions are intentionally provoked and handled in set handlers that continues the code at another address  of the routine, but any Ring 3 debugger loose the hand in the process. To handle this one, i’ve designed a tool, that i let run in Virtual Machine, which need a running Skype client to attach to, and which receive request of seed expansion and answer back with keys generated by the running Skype Client, after code injection.

    /* Skype Key Server */
    /* Author : Ouanilo MEDEGAN */
    #define RC4_KLEN 88
    HANDLE hProcess = 0;
    LPVOID RemoteAddr, KeyAddr;
    int Size;
    void __declspec(naked) InjectedCode()
     jmp BeginOCode
     INT 3
     INT 3
     INT 3
     INT 3
     _emit 0xE8
     _emit 0x00
     _emit 0x00
     _emit 0x00
     _emit 0x00
     pop eax
     sub eax, 0x09
     mov eax, dword ptr [eax]
     INT 3
     INT 3
     INT 3
     INT 3
     _emit 0xE8
     _emit 0x00
     _emit 0x00
     _emit 0x00
     _emit 0x00
     pop eax
     sub eax, 0x09
     mov eax, dword ptr [eax]
     call KeyAddrGet
     mov ecx, eax
     call SeedGet
     mov edx, eax
     mov eax, 0x0075D470
     call eax
     //mov eax, 0x7C80C058 //ON COMMON MACHINE : ExitThread Address
     //mov eax, 0x77E54A8F //ON "NEUF" MACHINE
     mov eax, 0x401304
     call eax
    DEC ECX //I
     DEC ESI //N
     DEC EDX //J
     INC EBP //E
     INC EBX //C
     PUSH ESP //T
     DEC ECX //I
     DEC EDI //O
     DEC ESI //N
     POP EDI //_
     INC EBP //E
     DEC ESI //N
     INC ESP //D
    int SizeOfCode()
     int Size;
     char *Proc;
     char Buffer[14] = {0};
    Size = 0;
     Proc = (char *)InjectedCode;
     memcpy(Buffer, Proc, 13);
     while (strcmp(Buffer, "INJECTION_END"));
     return (Size - 1);
    DWORD GetSkypeProcessHandle()
     HANDLE hProcessSnap;
     DWORD SkypeProcess;
    SkypeProcess = -1;
     hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
     if (hProcessSnap == INVALID_HANDLE_VALUE)
     printf("Error : CreateToolhelp32Snapshot (of processes) failed..\n");
     return (-1);
     PE32.dwSize = sizeof(PROCESSENTRY32);
     if (!Process32First(hProcessSnap, &PE32))
     printf("Error : Process32First failed..\n" );
     return (-1);
     if (strcmp("Skype.exe", PE32.szExeFile) == 0)
     SkypeProcess = PE32.th32ProcessID;
     while (Process32Next(hProcessSnap, &PE32));
     return (SkypeProcess);
    int Seed2Key(unsigned char *Key, unsigned int seed)
     /* FIXME */
     /* For the moment based on Skype.exe process in memory. Pick the proc Appart !*/
     DWORD NbWritten, ThID;
     HANDLE hThread;
     unsigned char *CodeBuffer;
    if (!WriteProcessMemory(hProcess, KeyAddr, (LPCVOID)Key, RC4_KLEN, (SIZE_T *)&NbWritten))
     printf("Skype Process WriteProcessMemory (Key) failed.. Aborting..\n");
     return (0);
     CodeBuffer = (unsigned char *)malloc(Size);
     memcpy(CodeBuffer, (void *)InjectedCode, Size);
     memcpy(CodeBuffer + 2, (void *)&KeyAddr, 4);
     memcpy(CodeBuffer + 18, (void *)&seed, 4);
     if (!WriteProcessMemory(hProcess, RemoteAddr, (LPCVOID)CodeBuffer, Size, (SIZE_T *)&NbWritten))
     printf("Skype Process WriteProcessMemory (Code) failed.. Aborting..\n");
     return (0);
    hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)RemoteAddr, NULL, NULL, (LPDWORD)&ThID);
     if (!hThread)
     printf("Skype Process CreateRemoteThread failed.. Aborting..\n");
     return (0);
    WaitForSingleObject(hThread, INFINITE);
    if (!ReadProcessMemory(hProcess, KeyAddr, (LPVOID)Key, RC4_KLEN, (SIZE_T *)&NbWritten))
     printf("Skype Process ReadProcessMemory (Key) failed.. Aborting..\n");
     return (0);
     return (1);
    int InitProc()
    ZeroMemory(&Si, sizeof(Si));
     ZeroMemory(&Pi, sizeof(Pi));
     Si.cb = sizeof(Si);
     /*if(!CreateProcessA("SkypeKeyServer.exe", "SkypeKeyServer.exe", NULL, NULL, FALSE, NULL, NULL, NULL, (LPSTARTUPINFOA)&Si, &Pi))
     printf("Error creating process..\n");
     return (0);
    hProcess = Pi.hProcess;*/
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, GetSkypeProcessHandle());
    if (!hProcess)
     printf("Failed Opening process..\n");
     return (0);
    KeyAddr = VirtualAllocEx(hProcess, NULL, RC4_KLEN, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
     if (!KeyAddr)
     printf("Skype Process VirtualAllocEx (Key) failed.. Aborting..\n");
     return (0);
     Size = SizeOfCode();
     RemoteAddr = VirtualAllocEx(hProcess, NULL, Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
     if (!RemoteAddr)
     printf("Skype Process VirtualAllocEx (Code) failed.. Aborting..\n");
     return (0);
    return (1);
    void EndProc()
     VirtualFreeEx(hProcess, KeyAddr, 0, MEM_RELEASE);
     VirtualFreeEx(hProcess, RemoteAddr, 0, MEM_RELEASE);
    int main(int argc, char* argv[])
     WORD wVersionRequested;
     WSADATA wsaData;
     int err, SelRes, Res, CbSz, i;
     SOCKET Sock;
     sockaddr_in LocalBind, ClientBind;
     fd_set Sockets;
     unsigned char Key[RC4_KLEN] = {0};
     unsigned int Seed;
    wVersionRequested = MAKEWORD(2, 2);
     err = WSAStartup(wVersionRequested, &wsaData);
     if (err != 0)
     printf("Unable to start WSA Lib\n");
     return (0xBADF00D);
    if (!InitProc())
     return 0;
    printf("SkypeKeyServer Started..\n");
    Sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     if (Sock == INVALID_SOCKET)
     printf("Could not create socket..\n");
    ZeroMemory((char *)&LocalBind, sizeof(LocalBind));
     LocalBind.sin_family = AF_INET;
     LocalBind.sin_addr.s_addr = htonl(INADDR_ANY);
     LocalBind.sin_port = htons(33033);
     bind(Sock, (struct sockaddr *)&LocalBind, sizeof(LocalBind));
     FD_SET(Sock, &Sockets);
    CbSz = sizeof(struct sockaddr_in);
     SelRes = select(FD_SETSIZE, &Sockets, NULL, NULL, NULL);
     if (SelRes)
     Res = recvfrom(Sock, (char *)&Seed, 0x04, 0, (SOCKADDR *)&ClientBind, &CbSz);
     if (Res != 0x04)
     ZeroMemory((char *)&Key[0], RC4_KLEN);
     for (i = 0; i < 0x14; i++)
     *(unsigned int *)(Key + 4 * i) = Seed;
     Seed2Key(Key, Seed);
     Res = sendto(Sock, (char *)&Key[0], RC4_KLEN, 0, (SOCKADDR *)&ClientBind, CbSz);
     return 0;


At this point, with the almost protection-free binary ( I’ve got, I was able to start the proper study of the protocol.

Here is the OllyDbg udd file (containing labels, meaningfull breakpoints and comments on a debugee) i’ve got in a pretty advanced step of the project : (match with C:\Program Files\Skype\Phone\Skype.exe).

The skype protocol analysis


The first step in the analysis, was to save various scenarios like a first connection without cached data, connection tries with bad authentications data (username/password), successful connections, chat initialization and many more, from the point of view of the transferred data over the Skype network. This had been done using OSpy to set hooks on the entry point of network functions and collect parameters sent to these functions, parameters including the data destined to be sent on the network or received from the network.

During previously analysis of the SC binary I’ve also noticed a critical point. The binary was filled with plenty of dynamically generated and very much meaningful debug strings. But in the classic build of the binary they were never displayed (surely they were intended for internal debugging use only). I had to force a few values in the binary by patching, to change the behavior of the binary. Then the procedure managing those debug string was expecting for a specific function to receive the generated strings. At that point, I had tuned the code of “OSpy” in order to make him collect the generated strings and to fill the report he generates with them, in a manner that synchronizes network data sending and receiving with those meaningful strings. In the end, I was able to get a more comprehensible report as shown in the image below (generated strings are tagged by “Sdbg” on the lines they appear):

Tuned OSpy Generated Scenario Report

Tuned OSpy Generated Scenario Report

/* oSpy Skype Debug Hook */
/* Author : Ouanilo MEDEGAN */

#include "stdafx.h"
#include "hooking.h"
#include "logging.h"

void skype_log(char *str)
	if ((str) && (strstr(str, "UI-[") == NULL))
		str[strlen(str) - 1] = 0;
		message_logger_log_message("Sdbg", NULL, MESSAGE_CTX_INFO, "%s", str);

	void *func_addr;
	void *var1_addr;
	void *var2_addr;

	int oldProtect, NbW;

	if (cur_process_is("Skype.exe"))
		func_addr = (void *)0xB9DFC8;
		var1_addr = (void *)0xB9E150;
		var2_addr = (void *)0xB9E154;
		return ;

	VirtualProtect(func_addr, 4, PAGE_READWRITE, (PDWORD)&oldProtect);
	*(DWORD *)func_addr = (DWORD)skype_log; //Override built-in & empty logging function
	VirtualProtect(func_addr, 4, oldProtect, (PDWORD)&oldProtect);
	VirtualProtect(var1_addr, 8, PAGE_READWRITE, (PDWORD)&oldProtect);
	*(DWORD *)var1_addr = (DWORD)0x01; //Set logging to YES
	*(DWORD *)var2_addr = (DWORD)0x00; //Dont Log Hour ! (~ 0x01)
	VirtualProtect(var1_addr, 8, oldProtect, (PDWORD)&oldProtect);


With such detailed scenarios reports, I had been able to determine little by little how every network packet is forged, its utility and to interpret the response received from the network’s peers. Indeed, reports permits me to locate interesting assembly code parts, involved in the forging of packets I want to understand, and to study the code, executing it step by step in order to understand completely how the packets were built. The reports also permits me to understand the various stages that composes a Skype session, from the disconnected state, to the initialized communication session, passing trough the first enter on the P2P network, the authentication process permitting us to appear authentic on the network, the presence broadcast, the dynamic contact list fetching, and the peer-responding process upon request.

Discovered Protocol Specificities

Before exposing, what I discovered about the Skype protocol mechanism, there is some of its technical specifications I must precise:

  • The Skype networks being a P2P one, every Skype client can act simultaneously as client and as server. Every SC is a “node” of the network. However, subject to qualifying properties such as bandwidth, computer’s CPU power, network configuration, or client uptime, any node is likely to be promoted as a “Super-Node” (SN). A Super-Node is a node that will be used by classic nodes as an interface with the Skype network, as it will be used to get information about the network, or it will routes network request to the classic node. There are also specials Super-Nodes which role is to relay other classic nodes communications in the case of special network configurations preventing direct connections. Each SC binary contains code proper to both Super-Node and classic node behavior.
  • Each node is identified by a “NodeID”, identifying the SC on the network, as long as it is generated from unique platform-specific values like hard disk’s serial number, or exploitation system serial.
  • There is also in Skype network, some centralized entities, indispensables to the network mechanism:
    • LoginServer: These ones role is to validate a client access (username/password) during his authentication, in order for him to appear as an authentic user on the network.
    • EventServer: They act like dynamic information cache for every authenticated user. They store contacts lists, chat history..

    Skype Entities

    Skype Entities

  • On the first connection, a SC uses only hard-coded bootstrap information like Super-Node IP address, and during that first connection it collect data about network, about user, and store them in a “cache” (actually in current versions, in a “shared.xml” file).
  • To communicate with the network, SC uses both UDP and TCP protocols. Every bit of communication is encrypted following the message transferred or the entities on the over side. Many encryption layers can be encountered for one message. But mainly, every communications is encrypted using the “RC4” algorithm (with a 128 bits key generated upon environmental data and partially predictable by the peer). Encryptions primitives like AES (Advanced Encryption Standard) or RSA are also used. Refer to Skype Network Administrator’s Guide (Skype Reverse Engineering : Genesis)  for more details about encryption handling.
  • The Skype protocol is a binary protocol. That means that unlike HTTP or FTP protocols, request a not humanly readable. The protocol is based upon numeric functions ids, typed parameters, and parameters ids. It’s vaguely similar to Microsoft RPC (Remote procedure call) protocol. That renders its comprehension more difficult. – Moreover, to make the protocol more difficult to understand, objects lists passed as parameters to functions can be compressed using a home-made compression algorithm, similar to arithmetic compression.
  • A SC goes trough several states during a complete session :
    • “Disconnected” state where the SC is offline and in idle state.
    • “Registering” state while the SC is setting up itself in the network.
    • “Registered” state where the SC is ready to interact with others users.
    • “Initializing text/file/voice/video session” state where the SC is negotiating ways to establish a session with another user.
    • “Session in progress” while the SC is communicating with another user.

Understood Protocol Mechanism

Today after 6 months of study, I’ve exactly understood the entire mechanism accomplished by the SC first in order to switch from “Disconnected” state to “Registered” State, second how it works while in “Registered” State, and last how it perform the text/file/voice/video session Initialization.

Now let’s describe globally how the SC goes from state Disconnected to state Registered, just after the user entered his username and password and launches the connection procedure.

The SC must pre-eminently establish a connection with a Super-Node that will be his parent node, that is to say the super-node that will play the role of its interface with the Skype network during the whole session. In order to do that, the SC will perform a host scan, sending UDP probe packet to known super-nodes (cached or bootstrap set ones), till he receives a positive probe response. Then the SC will try to establish a TCP connection with the super-node that positively responded to him. In case of failure, the SC continues the hosts scan probing the others known super-nodes. Let’s note that on negative probe response, the super-node send with its probe rejection, a list of super-nodes that the SC will use further in the host scan.

Once the SC successfully established a TCP connection with a super-node, it will send to this one, a “client accept” request. On negative response the SC will close the connection and continues its hosts scan. On positive response, the host scan is considered accomplished and the SC now has a parent node that will be able to be its interface with the Skype network.

Host Scan Sample

Host Scan Sample

Successful Parent Node Connection

Successful Parent Node Connection

Before going further, the SC must now authenticate the user. It will establish a TCP connection with a known LoginServer, and send to this one a packet involving many hashing algorithms and cryptography primitives, namely AES with a 256 bits long key, RSA with a 1536 bits long key, SHA, MD5 and at last RC4. That strong cryptography scheme has for role to protect user access (username/password) from being fished, as far as only a “Skype Technologies S.A.” LoginServer will be able to decrypt the packet sent because of the RSA private/public keys pair used. Indeed, by encrypting a specific part of the packet with a public key published by “Skype Technologies S.A.” and hard-coded in the SC binary, the client ensures that only “Skype Technologies S.A.” that possesses the associated private key will be able to decrypt the packet.

Let’s note that, before sending the packet to the LoginServer, the SC had generates an RSA private/public keys pair (each one long of 1024 bits) that will be used for the duration of the session. The public key of the pair is also sent in the packet destined to the LoginServer. Upon valid access, that is to say that if the user had entered valid username and password, the LoginServer will respond positively and sending back to the SC a data structure containing , the authenticated username, the validity’s duration of the authentication, and the public key that had been sent. That structure is signed (encrypted) with “Skype Technologies S.A.” private key, to ensure its authenticity, as far as only public keys published by “Skype Technologies S.A.” and hard-coded in SC binary will be able to properly decrypt the structure. That constitutes a proof that user presenting that structure had already been authenticated by a LoginServer. Let’s call that structure “Signed Credentials”.

Successful Authentication

Successful Authentication

The next step for our SC, once authenticated, is to broadcast on the network the presence of the user. In order to do that, the SC will forge a packet containing information about the node location (IP address and port, parent node IP address and port, node id etc…). Here will be involved RC4 and RSA using the user private key, and the packet also contain previously acquired user’s Signed Credentials. That scheme advantage is to ensure the authenticity of the sender, as long as if the specific part of the packet encrypted with RSA using the private key of the user can be decrypted with the public key contained in the authentic-approved Signed Credentials, it means that only the real authenticated user could have send that sensible data.

Let’s call that packet containing node location (it can also contain other miscellaneous information like the user real name, geographical location, etc…) and strongly authenticated, “User’s DirBlob”. That User’s DirBlob will be sent to specific super-nodes whose address will have been requested to parent node. User presence is then broadcasted on the network, and this one can be found by other users and be reached by them. SC is in the registered state.

Optionally, in that state, the SC can fetch the user contact list from an EventServer and check the presence of every contact. In order to do that, the SC connect itself via TCP to a known EventServer, authenticates itself as done with LoginServer and then request a list of hashes of each contact contained in his list. Once that list is fetched the SC search locally in his cache if there is contact with matching hash. In the case of unmatched hash, the SC will send to the EventServer a “hash details request” command to the EventServer that will respond back with a Contact’s DirBlob, which instead of containing node location as previously with the User’s DirBlob, contains Contact’s detailed information as login, real name, geographical location, etc…

Now the SC will search for every contact in the fetched contact list, trough the Skype network, whether he is online or not. It will be done by sending “contact search” command to specific super-nodes (super-nodes which are supposed to have receive the broadcast of the contact’s DirBlob if this one had registered itself on the network), and it will be responded back with the contact’s DirBlob containing his node location information. As broadcasted node location DirBlobs are stored on super-nodes during 72h, if a contact had registered himself many times within 72h, the searching SC will be responded with many contact’s DirBlobs. He had then to send a “ping command to each node contained in received DirBlobs and select the one responding to know the exact location of the contact’s node or even if he is online.

At this step the SC is registered, and has an up to date list of contacts with the real state of each contact in the list.

The registered state

Once in registered state, the SC is listening to its network interface, that is to say, his parent node, for request, or can send request of initializing session with online contacts. The SC can receive from the parent node, incoming ping, originally sent by a buddies searching to verify the user location, or can also receive network configuration tests, or also incoming session initialization propositions.

Incoming Ping Example

Incoming Ping Example

Incoming Session Proposition Example

Incoming Session Proposition Example

The session Initialization

In order to establish a communication with another user, SC has to negotiate with the user’s SC, a channel to use for the communication. The channel is defined by as secured AES stream which keys are exchanged via a specific process and also by the connection used, whether if it’s direct or via a Relay Super-Node.

Session Initialization Sample #1

Session Initialization Sample #1

Session Initialization Sample #2

Session Initialization Sample #2

Session Handling

If you read everything down to here, i assume you already got what we are talking about.. I’ll will from now on let code speaks for me.

Additional Notes : Skype Protocol Analysis (French)

Skype (v2.5) Protocol Analysis

Skype (v2.5) Protocol Analysis

Some yummy details (still in French :-|!) about the Skype (v2.5) protocol from connexion to presence (including authentication & contact list fetching).
(Click to view Or “Save As” to download).

Additional Notes : Skype Protocol Data Structures (French)

Skype (v2.5) Protocol Data Structures

Skype (v2.5) Protocol Data Structures

Here you will find, the details and usage (in French, sorry :-?!) of main data structures used in Skype v2.5 protocol.
(Click to view Or “Save As” to download).

Additional Notes : Skype Analysis Project Misc Notes (French)

Skype (v2.5) Analysis Project Misc Notes

Skype (v2.5) Analysis Project Misc Notes

Miscellaneous notes about the work (in French, sorry :-?!).
(Click to view Or “Save As” to download).

Thanks for reading, and stay tuned for the next issue : FakeSkype code release..


  1. Camilo Martin says: 06/07/2012

    Man, this is amazing, amazing! I’m very eager to see you realeasing source code, because if possible I’d love to make a better Skype client, the only thing that’s stopping me is the protocol being closed (and I’m not as smart as you are to reverse it) and running Skype makes the whole point of a better client kind of moot.

    • admin says: 07/07/2012

      This is kind of you my friend !! Source code almost there :) !! Hope this will help people in understanding the beast..

  2. aciid says: 15/08/2012

    God the skype client is such a bloated piece of shit

    Thank you for your hard work I’ve been looking for a unofficial(better) skype client for years now and looks like one’ll be here soon!


  3. vendz says: 22/03/2013

    hi, clever author! Any English version pdfs?

  4. hackerhacker8 says: 08/06/2013

    Search “Skype WP Source” in youtube. The full source code of skype for windows phone was leaked.

  5. Ankur Saxena says: 14/08/2013

    Dear Admin,
    It was really great to see the kind of effort you have put into for reverse engineering of the protocol. Hats off to you. I have developed a Client application that integrates with Skype API and records voice, video and chat messages. However the main problem i am facing now is to run the application in stealth mode where (it bypasses the authentication pop-up from skype and gets coupled with skype client silently without user interaction.) Kindly provide me some guidance as to how i can proceed with this as i have tried almost every possible way but still haven’t got any success. Thanking you in advance.

    Best Regards,
    Ankur Saxena

  6. boothound says: 24/11/2013


    Combien de temps avez-vous passé sur l’étude Skype ? quelques mois ? quelques années ?
    En tout cas, chapeau l’artiste…

    • admin says: 25/11/2013

      Bonjour :),

      Quelques mois, un tout petit peu plus d’un an. Merci cher ami.

  7. Markus says: 11/01/2014

    That was an incredible piece of detective work. I don’t understand it very well but I loved reading your story.

    • admin says: 13/01/2014

      Thanks :) !

  8. ADn says: 23/02/2014

    Dude I can’t believe this tremendous work you have done! Thanks so much for sharing it with us, mortals :P I would like to sink my nose in this “reverse engineering” world, could you put me in the right direction? What I need to learn first, and what then?… I will appreciate so much your help…

    • admin says: 26/02/2014

      Hey dude. Thanks for the support :) !

      The main key for learning is to spend A LOT OF TIME trying :). But my first experiences was with challenges. You can give a look to for example :) ! There is reverse challenges that can lead you to steps for learning.

Add a Comment