-[ WINAMP REVERSING ]--------------------------------------------------------- ---[ original italian text by .+Ma. - translation by ins4ne/s0ftpj ---[ published on BFi#7, file 18 of 22, 12/25/99 - http://www.s0ftpj.org/bfi Winamp Reversing or make your programs do whatever you want by .+Ma. *********************************** INTRO *********************************** The aim of this tut is to show what you can do by little manipulations of the PE... and with a sick brain (my nick, Italian word for sickness, was not random choosed :) If you go wild, maybe you'll be able to do something as crazy as this... I challenge you doing so! But now sit down, make a deep breath and try to follow the idea: I'm sure that at the end of the tutorial you'll look at your executables from a different viewpoint. But now, ready... go! USAGE: 2 Cokes 33cl Tropical fruits juice Pizza (YUM!) MUSIC: Afterhours - Non è per sempre (the album, encrypted of course ;) *********************************** TOOLS *********************************** File XORer 0.3 build 009 [by JFL] Sadd v1.0b by NeuRaL_NoiSE (thx!) Pebrowse v4.0 by PRUDENS inc. (http://www.spywindows.com) ************************************DOCS************************************* Everything you can get about PE, especially: -"PE-Crypters : a close look to the PE format by Kill3xx -"How to add a new section and a new function to a PE" by Pusillus (Ita) -The NeuRaL_NoiSE tutorials. Many talk about PE manipulation... and anyway if you get another one from a totally different topic it will anyway be a good thing to read it ;) *************************** THE TUTE, AT LAST :) **************************** Some time ago I got a really strange idea: modify winamp to make it read encrypted mp3s. This didn't seem an impossible thing to me, but it required two essential elements: a motivated cracker and some free time. At that time I missed both of them, while now I miss just the second... But the solution was rather easy: I just had to cut some sleeping hours to make the "winamp project" possible and write this tutorial (kids, don't do it at home)! The first question that everyone that heard about this project was: "what's the point in encrypting mp3s?" Well, how should I make them understand that it was just a funny exercise, something without a real motivation, just a matter of doing it? They probably (all the enlightened minds) should have understood... but I had to give a real motivation about the usefulness of my work :) Here are the conclusions I've been able to get: - Nowadays most of the free web space providers check for mp3 and if they find any they just close your free account. Since the trick of changing the extension is actually a little out of date, a good idea should be to encrypt the files so they aren't easily detected; - If you have to do a massive trade of mp3s on CD via snail-mail it could be quite risky... actually the encryption of the files on the CD isn't the most smart idea, but it is an idea... In fact while at the previous point you should decrypt the mp3 after you downloaded it from the Internet in this case you should need to decrypt the entire CD and re-master it: an on-the-fly decryptor should be the real solution; Now that the "why doing this" problem was solved, I just had to decide how to do it... in fact the second question I got was "But why don't you write an encrypted mp3 player?". My first answer was "because I'm a reverse engineer and if I don't reverse something I'm not happy with it"... but this wasn't too persuasive. So I decided that the official version will be: - After I'd understand how to modify Winamp to make it work with encrypted mp3s instead of the normal ones I should be able to do the same thing with almost every other program. As a result, even if your encryption algorithm is very easy it will be in a non-standard format and it should be harder to decrypt than a standard one, since is just recognized by your version of the program. ... funny, isn't it? :) Now I had to start at some point. Schematically my work (and, from now, yours) can be summarized this way: a) Program analisys or: "damn, it had to be a dll?" b) Adding a new section or: "there is quite some place but I like to play with the PE" c) Experiments or: "let's try not to encrypt the HDD, the ram, the monitor and the keyboard" d) API hook or: "not sure the name is right, but it sounded good" e) Decryption or: "all this work for two lines of code? fuck...." f) Encryption or: "yeah, I did a good job, but what should I listen to now?" g) Listening or: "It works? Naaah, can't be real" Let's analyze the entire work, step by step. *************************************************** a) Program analisys or: "damn, it had to be a dll?" *************************************************** Well, the winamp code that manages to load the mp3s is in fact contained in a dll. Actually all the winamp code that manages to load some kind of audio file can be found in a dll file. The one that is used to load a mp3 file is called in_mp3.dll. How can you get this? Very easy, just execute winamp, open the softice window with a CTRL-D (heh, you'll have to select the winamp double size feature from the menu ;) and then set a breakpoint at the ReadFile API. When you'll get the control back to the original program the dear old Sice will be reactivated when inside the API call: press F12 and you'll be transferred inside the code part that we were looking for and you'll get the name of the DLL where the code is. The fact that we will be working on a dll has some advantages and some disvantages: a considerable advantage is that we can suppose that all the calls to readfile to read the mp3 are inside it; even we can suppose that ALL the calls to a readfile are interesting for us (and a deeper analisys of the code will confirm this); the disvantage tho is that we'll have to learn a little more about dlls and it's PE... and is just at this point that we jump to the next section. ************************************************************************ b) Adding a new section or: "there is quite some space but I like to play with the PE" ************************************************************************ The first thing that you must do when adding some code to an executable is to choose if you could append it to an existing section (if there is some place) or add it in a new one. To do this you have first of all to check the actual state of the sections: Disassembly of File: in_mp3.dll Code Offset = 00001000, Code Size = 0001E000 Data Offset = 00025000, Data Size = 00005000 Number of Objects = 0005 (dec), Imagebase = 11000000h Object01: .text RVA: 00001000 Offset: 00001000 Size: 0001E000 Flags: 60... Object02: .rdata RVA: 0001F000 Offset: 0001F000 Size: 00006000 Flags: 40... Object03: .data RVA: 00025000 Offset: 00025000 Size: 00005000 Flags: C0... Object04: .rsrc RVA: 0005A000 Offset: 0002A000 Size: 00003000 Flags: 40... Object05: .reloc RVA: 0005D000 Offset: 0002D000 Size: 00003000 Flags: 42... Good. The first section, called .text, is the one that contains the code and, as you can see with any hexeditor, has a lot of unused space at its end. In fact, by knowing that it is 1E000 bytes long and it has an offset of 1000 from the beginning of the file, we could just try to check near the offset 1F000 with the hexeditor to find quite a lot of zeros. We should then use this space for our code, so... we will add a new section :) (I know you hate me for this, but remember that I'm doing this for you: this lesson has to be the most generic possible, so I can't assume that you'll always find some free space!) To add a new section some basic knowledge of the PE format is required. I missed this at my first experiments... that had also been disastrous of course :) Anyway get some good documentation (on Ringzer0 there should be more or less everything you should need) and you'll see that you won't have too many troubles in understanding this tut. A hint: backup your in_mp3.ddl or, even better, copy it to another name and work on that copy (it will be anyway added as a new plugin by winamp!). I called mine in_mal.dll :) First of all it is needed to find the characteristics of our new section, that is the data that describes it inside the PE header. To do this let's use a "spying" tool (PEBrowse) and then a definition of the PE sections table, like for example the "kill3xx" one, the great reference text ;) I'll mention it with big respect: - SName: 8 bytes string with the section name. Select the name you like most, i used ".mala" :) - SVirtualSize: it contains the physical space (see SizeOfRawData) of the data rounded to a multiple of the section alignment. Since section alignment is equal to 1000 (like you can read with PEBrowse at the Optional Header section), let's select 1000 for the SVirtualSize. - SVirtualAddress (RVA): this enables you to calculate the position that the section will have when loaded in memory by the loader. It has to be a multiple of the section alignment. In our example, since the last section has a RVA of 5D000 and Size 3000, the .mala RVA will be 5D000+3000=60000, that is already rounded to the section alignment. - SizeOfRawData: the physical space of the data on the disk rounded to file alignment. Since file alignment is 1000, let's put as for SVirtualSize a length of 1000 bytes. - PointerToRawData: the physical offset where you'll find data in the section. You just have to add the offset of the last section to the SizeOfRawData of the last section, rounding it to the file alignment. So: 2D000 + 3000 = 30000. Also this value is already rounded. - SFlags: the flags that identify the characteristics (code,data,etc.) and so the flags that will be used in protected mode paging (writeable, readable,etc.). We will have to put the same ones as the first one, that is 0x60000020. Notice that while some values are easily found by Wdasm, other ones have to be readed with PEBrowse... since you can find everything you should need in PEBrowse you'd better to leave Wdasm :) Now that you had to read all the technical boring stuff and you're ready to play with your hexeditor with the dll... stop it! The Sadd program written by the good old Neuro will do all the dirty work for you! It will automatically update also the Image Size, a very important field (especially in NT) that wasn't already described: it contains the length of the image once loaded in memory and, of course, it has to be changed when a section is added. Of course, if you should have to add in the section some functions to export this tool wouldn't be enough. But, luckily, it is not our business: the code that we will add won't be called by winamp but directly by the dll code (how lucky, so we don't care anymore for the "disadvantage" to work with a dll!). Very good: now that we added our new section we have to try it out... that's why we jump to the next section. ************************************************************************** c) Experiments or: "let's try not to encrypt the HDD, the ram, the monitor and the keyboard" ************************************************************************** Well, in fact the situation isn't so bad as it could seem... but usually I prefer to move on by small steps and I don't want to insert immediately the encryption routine without knowing exactly what I'd like to do. So let's do a small checkpoint. We want to read the encrypted mp3s. But how? Well, first of all by reading the file from the disk, like we already understood when we have set the breakpoint on the readfile api. Later, once readed the file (or, more generically, once readed a given amount of bytes from this file), we would like to have a chance to decrypt it in memory in a way that the program won't even know about this operation. How to do it exactly? Let's see the API: BOOL ReadFile( byte HANDLE hFile, // handle of file to read dword LPVOID lpBuffer, // address of buffer that receives data dword DWORD nNumberOfBytesToRead, // number of bytes to read dword LPDWORD lpNumberOfBytesRead, // address of number of bytes read dword LPOVERLAPPED lpOverlapped // address of structure for data ); Good. Look at the parameters that are given to the function: you can see a pointer to the buffer where the readed data is set and the number of bytes that have been readed. Perfect, is exactly what we needed: after we should read some bytes from the file, we could modify them in memory since we know the address and the number of them. How? Easy: we will intercept all the calls to ReadFile and redirect them to our routine that will first call the readfile and then will decrypt the block of bytes that has been readed. At the return the program will just think that it executed the ReadFile. And now it's time to start to write our new function: for now it will be just a copy of the call to the ReadFile, so we can easily control the given parameters and understand how the stack works. Actually we will just need to take the parameters to the call back from the stack, repush them, call ReadFile and then put off the stack the "original" parameters. At this moment we have to add a small theorical explanation: when a function that requires some parameters is called, they are PUSHed on the stack before the real call. For example since the ReadFile requires five parameters, they are the five last ones PUSHed (and they are the ones we are interested in). This concept has to be always clear, since it is VEEERY useful in many situations, for example when you want to understand a disassembly or when you have to check for serial numbers in memory. When a call is executed, on the top of the stack the return address for the function is also pushed: this address, that points to the address just after the call, is used by the program to get back to the calling position when it will find a RET instruction. This means that, once in our function, the first parameter (the one that is pointed by ESP) will be the return address, while the real parameters to the ReadFile function are set starting from the address ESP+4. To be more exact at the ESP+4 will be set the LAST parameter pushed (do you remember that the pushes have an inverse order from the one from the API definition?) and the others continues each 4 bytes. At this point the code to reply the original ReadFile could be: 55 push ebp // save ebp value NOTE: at this point all the elements on the stack are "moved" by 4 bytes! 8BEC mov ebp, esp // let's use ebp as a fixed value to read stack cells 8B4518 mov eax, dword ptr [ebp+18] // save to eax the 5th parameter 50 push eax // PUSH 5th parameter 8B4514 mov eax, dword ptr [ebp+14] // save to eax the 4th parameter 50 push eax // PUSH 4th parameter 8B4510 mov eax, dword ptr [ebp+10] // save to eax the 3rd parameter 50 push eax // PUSH 3rd parameter 8B450C mov eax, dword ptr [ebp+0C] // save to eax the 2nd parameter 50 push eax // PUSH 2nd parameter 8B4508 mov eax, dword ptr [ebp+08] // save to eax the 1st parameter 50 push eax // PUSH 1st parameter FF1520F00111 Call dword ptr [1101F020] // Call ReadFile 5D pop ebp // get the original ebp value back from stack **************************************************** INSERT DECRYPTION ALGORITHM HERE!!! **************************************************** C21400 ret 0014 // return deleting the last 5 cells from the stack Notice the last command, the RET 0014: this is needed because on the stack, after the return address there are still present the 5 parameters pushed by the original program! So it is needed to delete it so the stack will be back to the state as if only the original ReadFile should have been executed. At this point we just have to do a test: before we change all the addresses for all the calls, of course, let's try just one, the one at address 110014DD, that reads the tag id3 to display the song name. It is called just once at the beginning of the playing, so it's quite ok for some tests since it can't do much havoc :) * Reference To: KERNEL32.ReadFile, Ord:0218h | :110014DD FF1520F00111 Call dword ptr [1101F020] since our function begins at the address 11060000 (remember that the address is calculated by an easy sum by imagebase and RVA) we can modify the call at the address 100014DD with a :110014DD E81EEB0500 call 11060000 :110014E2 90 nop To calculate the relative value of the call you have just to substract at the value 11060000 the address of the instruction next to the call. In this example you just have to do 11060000-110014E2=0005EB1E. Remember also that the value in opcode format is exactly the one that is written up here. At this point you can execute the program (remember to add your dll to the mp3 player and disable the original one) and... it can't be real, but it works! :) *********************************************************** d) API hook or: "not sure the name is right, but it sounded good" *********************************************************** I'm not sure if this name is right... but it is just what I'd been thinking to do: "hook" the API calls and direct them where I wanted to. Well, let's hope it will work :) This operation has just been done in the previous section to do our test: now we have to do the same for all the calls to a ReadFile. By looking inside the disassembly for the "ReadFile" string I founded 11 calls, 9 of which were exactly identical FF1520F00111 Call dword ptr [1101F020] while the other 2 were different, respectively a CALL EDI and a CALL ESI, which values have been identified by the commands 8b3D20f00111 mov edi, dword ptr [1001F020] and 8b3520f00111 mov esi, dword ptr [1001F020] Good, so we have now to redo all that boring calculations we did before for all the relative values for all the nine calls? Yeah, sadly yes... but there is a small trick to make things easier :) Since the command takes 5 bytes (1 opcode + 4 operand) we can solve the simple equation: Operand = Address of the called function - (Command address + 5) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ this value is always fixed this one changes that is like: Operand = (Address of the called function - 5) - Command address ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ this value is always fixed this one changes Nothing special... but calculations are easier! This can be useful especially in such cases when you have to redirect from various addresses to the same function. So now we have just to do for each calculations the easy substraction 1105FFFB-(call address). The values at the end are: 01) 11001087: FF1520F00111 --> E874EF050090 02) 110014DD: FF1520F00111 --> E81EEB050090 03) 110015A8: FF1520F00111 --> E853EA050090 04) 11001649: FF1520F00111 --> E8B2E9050090 05) 110017be: FF1520F00111 --> E83DE8050090 07) 11007e05: FF1520F00111 --> E8F681050090 08) 11008333: FF1520F00111 --> E8C87C050090 09) 110083f1: FF1520F00111 --> E80A7C050090 11) 110088b2: FF1520F00111 --> E84977050090 Now we have just to deal with the two "special" calls... one way to patch that calls is to modify the command that saves the address to ESI (or EDI) with a command that saves the value 11060000 in that registers. 06) 110077f8: CALL EDI: modify to 110077f0 mov edi, dword ptr etc. 8b3D20f00111 --> BF0000061190 10) 11008665: CALL ESI: modify to 11008650 mov esi, dword ptr etc. 8b3520f00111 --> BE0000061190 Et voila'... it's done! That are the substitutions to be made, a bit boring but at the end it still works... does it sound obvious? :) At this moment, let's change our do-almost-nothing function to one that will do really something useful... dunno, decrypt a mp3 file! ************************************************************* e) Decryption or: "all this work for two lines of code? fuck...." ************************************************************* Previously, at the C section, when we described the code of our function I left an empty space where you should put the encryption algorithm. Now it's time to write one, by choosing first of all the encryption type. First of all it is necessary to specify the fact that such algorithm can't work directly on the entire file, but just on parts of the file that are readed time by time by the program. So it is not a good idea (nor easy to do at the encryption stage) to work moving bytes, but a better choice is definitely to manipulate them with some easy operations. In this example I decided to use a very simple xor, but you can choose another one by moving by my own one. I'd like to say a few things before showing the code: first of all we will need two elements that are on the stack, precisely the second and the fourth, that are respectively the address where the file bytes are readed and the number of bytes readed. Since when the end of the file is reached this will be zero we will have to check the number of readed bytes so we won't be going to encrypt some random memory places (ehehe...Neu knows I did this ;). Finally, since the xor key can be selected in different ways I'll leave in the code a label "val" that can be modified at the right moment... you could even call a dialogbox to ask for the password, from which the decryption key could be generated! Here comes the commented code: 56 push esi // Save the values of the 51 push ecx // 3 registers that we will 50 push eax // use 8b742414 mov esi, dword ptr [esp+14] // esi=starting address 8b4c241c mov ecx, dword ptr [esp+1c] // 8b09 mov ecx, dword ptr [ecx] // ecx=bytes to decrypt 85c9 test ecx, ecx // ecx=0? then finished. 740c jz endloop loop: 8a06 mov al, byte ptr [esi] // read 1 byte 34[val] xor al, [VAL] // xor the byte with value in VAL 8806 mov byte ptr [esi], al // put the byte again there 46 inc esi 49 dec ecx 85c9 test ecx, ecx // finished decryption? 75f4 jnz loop endloop: 58 pop eax 59 pop ecx 5e pop esi // restore values in regs Done! There is nothing strange as you can see... but what is nice is that you can put whatever you want in that loop :) For example I selected the hex value ED and the disasm of the final version of my function is: ***************************LAST VERSION DISASSEMBLY************************* :11060000 55 push ebp :11060001 8BEC mov ebp, esp :11060003 8B4518 mov eax, dword ptr [ebp+18] :11060006 50 push eax :11060007 8B4514 mov eax, dword ptr [ebp+14] :1106000A 50 push eax :1106000B 8B4510 mov eax, dword ptr [ebp+10] :1106000E 50 push eax :1106000F 8B450C mov eax, dword ptr [ebp+0C] :11060012 50 push eax :11060013 8B4508 mov eax, dword ptr [ebp+08] :11060016 50 push eax * Reference To: KERNEL32.ReadFile, Ord:0218h | :11060017 FF1520F00111 Call dword ptr [1101F020] :1106001D 5D pop ebp :1106001E 56 push esi :1106001F 51 push ecx :11060020 50 push eax :11060021 8B742414 mov esi, dword ptr [esp+14] :11060025 8B4C241C mov ecx, dword ptr [esp+1C] :11060029 8B09 mov ecx, dword ptr [ecx] :1106002B 85C9 test ecx, ecx :1106002D 740C je 1106003B * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:11060039(C) | :1106002F 8A06 mov al, byte ptr [esi] :11060031 34ED xor al, ED :11060033 8806 mov byte ptr [esi], al :11060035 46 inc esi :11060036 49 dec ecx :11060037 85C9 test ecx, ecx :11060039 75F4 jne 1106002F * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:1106002D(C) | :1106003B 58 pop eax :1106003C 59 pop ecx :1106003D 5E pop esi :1106003E C21400 ret 0014 ************************************************************************ f) Encryption or: "yeah, I did a good work but what should I listen to now?" ************************************************************************ Good, now you have a dll that enables you to listen to encrypted mp3s... and I'm gonna kick the ass of the guy that will say "but I don't have encrypted mp3s!" :) If you used my xor algorithm there isn't any problem, just write an easy program that XOR-es the file... or get the JFL File XORer from my web site (http://malattia.cjb.net, at the tools/downloads section)... I personally used this second way (Ehehe... why did you think I choosed such a stupid algorithm? :)) If you choosed a personal algorithm... then it's your problem, I think you'll have to write a tool from your own! >:))) Now I'm sure you are all intelligent students... but please don't write me saying that you can't listen to your favourite songs since you encrypted your playlist... ok? ;) *************************************************** g) Listening or: "It works? Naaah, can't be real" *************************************************** Et voila'... it works! Really! The confirmation is done by the fact that I'm just listening to an entirely encrypted album (yeah, I know you can't hear it, but trust me, ok?)... to be honest I didn't believe it myself too! :) ************************************OUTRO************************************ Woa, it's finished! Guys, I worked more to write this tut than completing the entire project! Now be nice, use your uncle +Ma's teachings and modify your program as you like... this was just an example, now free your mind and lemme see what you can do! For any info, greetings, insult, contribute, offer you can email me at malattia@gmx.net. If this address should be dead you can check the one that is at my web page at: http://malattia.cjb.net. **********************************THANKS********************************* Thanks to Ringzer0, the best cracker group in the universe and to the entire #crack-it, but especially to: Neuro, for the tons of hints on PE; Pusi, that was listening to me when I was blabbering about the mp3 encryption; Suby, that said I was sick when I was blabbering about the mp3 encryption; everyone that was listening to me when I was blabbering about mp3 encryption; everyone that didn't insult me when I was blabbering about mp3 encryption; everyone that was able to read this file to this point :) byez, .+Ma. LAST_NOTE: my friend Genius saved me from being highly shitted (if I can be shitted more than this) by noticing a small thing that wasn't described in this tut. Since it won't totally fuck-up the new dll, but could make some small undocumented problems, I decided to leave it as a homework to understand what was wrong. The solution to this homework will be published with the list of all the reversers that coworked by mailing me in the next BFI :) -[ EOF ]----------------------------------------------------------------------