L ZZZZZZ RRRRR SSSSS L Z R R S L aaa Z aaa R R u u S L a Z a RRRRR u u SSSSS XX L aaaa Z aaaa R R u u S XXXX L a a Z a a R R u u S XXXXXX LLLLLLL aaaaa ZZZZZZZ aaaaa R R uuuuu SSSSSS XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXX XXXX proudly presents his 33.Cracking Tutorial (22.12.1999) XX Adding a section and enarging the last section of a PE file I. Introduction I.1 The tools II. The essay II.1 The CrackMe II.2 DocClear III. BTW I. Introduction Yesterday someone asked in IRC (#cracking4newbies) how to add a section to a PE file and today I saw the another request at +Sandmans Newbie Forum (crfb.cjb.net) and I had my last school day today and can't start to idle at 11am ;) So I decided to write a small tut how to manage this. I.1 The tools SoftIce (I have v3.25) HexWorkshop Procdump Interrupter A PE analyzer (I use my own one, but you should get a good one) Good music: I listen currently to the song "Raventhrone" by "Raventhrone" (raventhrone.com) II. The essay II.1 Enlarging the last section At first we need a target: I have coded a special crackme for this one which I packed with UPX 0.84 after it. It is very simple: If you enter "Test" as serial you get the "Your serial is correct" message, else "Your serial is wrong". We will try to patch it to write the correct serial to the dialog caption if the user enters a wrong serial. After it is packed with UPX we will load it in Procdump and see that the PE editor reveals this: Sectionname Virtual Size Virtual Offset Raw Size Raw Offset Characteristics UPX0 00005000 00001000 00000000 000000400 E0000080 UPX1 00001000 00006000 00000400 000000400 E0000040 .rsrc 00001000 00007000 00000200 000000800 C0000040 I won't explain the function of the sections and the values of the characteristics. If you want to have additional knowledge about them, please consult pe.txt. Our first goal is to enlarge the last section in order to put the data we need for the patch there. UPX is an exception of the standard packers around, as UPX wraps the existing sections and makes it's own sections to be the first and not the last as many other packers. So here we gotta enlarge the .rscr section which doesn't make a difference to enlarging a packed section. This info is just for the record ;) btw: You can add and modify complete section with Procdump, but one day a situation might appear where you gotta learn how to do it manually, so please go on, dear reader :) The first thing we have to do is finding the last section in the EXE file AND in memory. This is plain and simple, as the values of Raw Offset (file) and Virtual Offset (memory) just show these places. So, the .rscr section starts - in the file - at offset 800h. Here it is: 00000800 0000 0000 0000 0000 0000 0000 0000 0100 ................ 00000810 0500 0000 1800 0080 0000 0000 0000 0000 ................ 00000820 0000 0000 0100 0000 5800 0080 3000 0080 ........X...0... 00000830 0000 0000 0000 0000 0000 0000 0000 0100 ................ 00000840 0904 0000 4800 0000 7040 0000 4401 0000 ....H...p@..D... 00000850 0000 0000 0000 0000 0700 4D00 4100 4900 ..........M.A.I. 00000860 4E00 4400 4C00 4700 0000 0000 0000 0000 N.D.L.G......... 00000870 0000 0000 0000 0000 0000 0000 C070 0000 .............p.. 00000880 AC70 0000 0000 0000 0000 0000 0000 0000 .p.............. 00000890 CD70 0000 B870 0000 0000 0000 0000 0000 .p...p.......... 000008A0 0000 0000 0000 0000 0000 0000 D870 0000 .............p.. 000008B0 E670 0000 0000 0000 F670 0000 0000 0000 .p.......p...... 000008C0 4B45 524E 454C 3332 2E44 4C4C 0055 5345 KERNEL32.DLL.USE 000008D0 5233 322E 646C 6C00 0000 4C6F 6164 4C69 R32.dll...LoadLi 000008E0 6272 6172 7941 0000 4765 7450 726F 6341 braryA..GetProcA 000008F0 6464 7265 7373 0000 5365 7446 6F63 7573 ddress..SetFocus 00000900 0000 0000 0060 0000 0C00 0000 2232 0000 .....`......"2.. 00000910 0000 0000 0800 0000 0000 0000 0000 0000 ................ 00000920 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000930 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000940 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000950 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000960 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000970 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000980 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000990 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000009A0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000009B0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000009C0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000009D0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000009E0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000009F0 0000 0000 0000 0000 0000 0000 0000 0000 ................ You wonder why I know how long it is? Guess what's the value of Raw Size ;) The value of Virtual Size (memory) differs from the one of Raw Size (file) because PE files must have a certain section alignment which differs on disc and in memory and to get this alignment when the Raw Size is smaller some 00-Bytes are added to the section in memory to make it fit the alignment. So, here's the deal: We want to patch the file in a way that it displays the sentence "The correct serial is Test" if the user enters a wrong serial. To achieve this we will redirect the code from the check of the serial to our enlarged section and then we enter some code there and jump back to the "original" part of the file. The first thing we got to do is enlarging the section. From now on we will do *everything* manually. No more Procdump allowed ;) To know how to enlarge the section we have to find where this info is stored in the file. It is very easy to find. Just open it in your favourite Hexeditor and scroll down from the beginning of the file until you see the section names. Of course there is a more "sophisticated" way to find the section info, but this one is surely the quickest. You will find this: 000001B0 0000 0000 0000 0055 5058 3000 0000 0000 .......UPX0..... 000001C0 5000 0000 1000 0000 0000 0000 0400 0000 P............... 000001D0 0000 0000 0000 0000 0000 0080 0000 E055 ...............U 000001E0 5058 3100 0000 0000 1000 0000 6000 0000 PX1.........`... 000001F0 0400 0000 0400 0000 0000 0000 0000 0000 ................ 00000200 0000 0040 0000 E02E 7273 7263 0000 0000 ...@....rsrc.... 00000210 1000 0000 7000 0000 0200 0000 0800 0000 ....p........... 00000220 0000 0000 0000 0000 0000 0040 0000 C000 ...........@.... When you look at .rsrc you will see exactly the values we got from Procdump, so we would not even have to use Procdump (Pay attention that the high-bytes and low-bytes are turned around, so if you see 0010h it is not 10h=16d, but 1000h=4096d). Now just find the 200h from Procdump (00 02) here. Change it to 230h (30 02) and go to the end of the file. If you want extended information about the values that make up a section description: PE.TXT When you are at the end of the file you got to add 30h = 64d Bytes. This should be much more than enough, but better too many bytes, than too few. Now it's time to find the address we need to patch: I know what I have coded and I know that you can easily find the important passage, too, so I won't explain how to find it. I will choose the passage where the "Wrong" gets written to the edit field. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004010F9(U) | * Possible StringData Ref from Code Obj ->"Wrong" | :00401102 68FB104000 push 004010FB :00401107 68EB030000 push 000003EB :0040110C FF7508 push [ebp+08] * Reference To: USER32.SetDlgItemTextA, Ord:0228h | :0040110F E81C000000 Call 00401130 :00401114 C9 leave :00401115 C20400 ret 0004 My intention is to remove the first push and put a jump there which leads to the code we will add in the next part. We gotta find this part first, as we know where it is in the file, but not where it is in memory. But don't worry, it's cheap. You remember that .rsrc at Virtual Offset 7000 starts? I hope so ;) Now we know that the Raw Size of the section was 200h bytes. We added something, so our stuff should start at offset 7200h. To make it sure, use your hexeditor and put "Hope" at offset A00h in the file (where we added 30h bytes. When you break on SetDlgItemTextA in SICE and try to register it, look at 407200. If you find "Hope" everything's OK, if not you made a mistake. 407200? Why not 7200? You got to add the ImageBase of the EXE file. If you want detailed information: consult PE.txt, if not let me just say this: In 99,9% of all Win9X EXE files, 400000 is the Imagebase. Now, the next part: We have to replace the push instruction - for this purpose we use SICE: I changed the line :00401102 68FB104000 push 004010FB to :00401102 E9F9600000 jmp 407200 btw: it would be million times better, if you would use the existing SetDlgItemTextA routine but then the enlarging of the section would have no use ;) Uhm, btw: How do we patch the file? It's packed! Well, I believe we have to enlarge the section again ;) Add 10h bytes to the section characteristics and to the end of the file. I won't explain it this time, as it is *completely* the same as above. When you have done it, load the packed file into W32Dasm and scroll down to the end. You will see this: * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040632F(C) | :0040636B 61 popad :0040636C E98FACFFFF jmp 00401000 This is the jump that is taken when UPX has finished unpacking. We have to change it to jmp 407230 which is the offset of the second part of the section we added. To get the valid opcode for it use SICE in combination with my tool "Interrupter". It puts an CCh opcode at the entry point of every PE file and you can breakpoint on it using "bpint 03". Then you need to replace the byte that was overwritten by using "e eip XX" in SICE where XX is the number Interrupter returns you. Then "bpx 40636C" and when SICE breaks, enter "a eip" and choose "jmp 407230" to change the instruction and get the opcode: E9BF0E0000. To make the patch permanent use your HexEditor. Search for E98FACFFFF and replace it by E9BF0E0000. Now the next things: We gotta exchange the push-instruction (the one from SetDlgItemTextA) to "jmp 407200". This is done by adding code that patches the unpacked code. The following instructions are to be added to the second part of our enlarged section to convert the push to a jump: mov dword ptr [401102], 0060F9E9 ;; CONVERT PUSH TO JMP mov byte ptr [401106], 00 ;; SAME jmp 401000 ;; JUMP TO REAL ENTRYPOINT The opcodes for this are: C70502114000E9F96000 C6050611400000 E9BA9DFFFF Now it's time to add the bytes which displays "The correct serial is Test" (btw: add this string with your hexeditor to offset A15h). To achieve this, break on the line which jumps to 407200. When you are there take this jump and then we gotta add the following instructions: push 407215 jmp 401107 This pushes the string and then jumps back to the other two pushes and the call to SetDlgItemTextA. I know myself that just patching the first push to make it push the added string would be enough, but then you would have missed the fun redirecting part ;) Well, the opcodes for the instructions above are: 6815724000 E9FD9EFFFF Add them to your file at the beginning of the enlarged section at offset A00h. Disable all breakpoints and run the EXE file. When you run the file now, you will most probably get an error message ;) This is because you surely did not replace the CCh at the beginning of the file which was put there by Interrupter. Replace offset 620h by 60h and everything should work. At least here it works, if it doesn't work at your PC, please check everything and retry. You can also compare my enlarged.exe with your exe file. II.2 Adding a new section: Now, I made a copy of enlarged.exe to demonstrate how adding a section to an exe file works. The file now is called adding.exe and the first thing we have to do is to restore the old values of the .rsrc section, that means: Change back the size of the section from 250h to 200h. You have to know that a PE file has the information how many sections the file contains stored in its header definition. This info is stored at offset PEStart+6 which is here: 000000C0 5045 0000 4C01 0300 1FC0 6038 0000 0000 PE..L.....`8.... You can find out where the PE starts by looking at offset 3Ch of the EXE file or by searching for 50450000, the bytes every PE header start with. So, we can see that there are three sections to be found in this EXE file. Don't hesitate and increase this value by 1. Now go to the section definitions and add a new section called "Tutorial". You gotta know that every section definition is 28h bytes long. So you have to start writing "Tutorial" at offset 230h. It will look like this: 00000200 0000 0000 4000 00E0 2E72 7372 6300 0000 ....@....rsrc... 00000210 0010 0000 0070 0000 0002 0000 0008 0000 .....p.......... 00000220 0000 0000 0000 0000 0000 0000 4000 00C0 ............@... 00000230 5475 746F 7269 616C 0000 0000 0000 0000 Tutorial........ Watch out that a section name must have max of 8 chars. The next thing we have to do is deciding which Virtual Offset/Virtual Size/Raw Offset and Raw Size our section should have. To decide this, we need the following values: Virtual offset of formerly last section (.rsrc): 7000h Virtual size of formerly last section (.rsrc): 1000h Raw offset of formerly last section (.rsrc): 800h Raw size of formerly last section (.rsrc): 200h Section Alignment: 1000h File Alignment: 200h Section Alignment and File Alignment are the minimum sizes of a section in the memory and in the file. So, we have to give our new section at least a section alignment of 1000h and a file alignment of 200h. For further documentation: Pe.txt The Raw Offset of the section is just the offset in the file, which is A00h like we have chosen in the first part of this essay, to get the Virtual Offset of our section we have to calculate this: VIRTUAL SECTION OF .rsrc + VIRTUAL SIZE OF .rsrc = 8000h. Luckily it perfectly fits to the section Alignment (8000 MOD 1000 = 0) and we can choose 8000h for our Virtual Offset. If VIRTUAL SECTION OF .rsrc + VIRTUAL SIZE OF .rsrc would be 8200h for example, we needed to take 9000h as this is the next value which MOD SECTION ALIGNMENT is 0. So, let's fill the desription of our section: 00000230 5475 746F 7269 616C 0010 0000 0080 0000 Tutorial........ 00000240 0002 0000 000A 0000 0000 0000 0000 0000 ................ 00000250 0000 0000 4000 00C0 0000 0000 0000 0000 ....@........... Here everything is added and has its order. When you start the prog now, it will crash, as the "old" jump at the end of the unpacking section is still active. We gotta change :0040636C E9BF0E0000 jmp 00407230 to :0040636C E9BF1CFFFF jmp 00408030 If you execute it, it will still crash and when you look at 408000 in memory you will see that is is not initialized. There's a small thing we have forgotten: It is called "Image Base". Here a copy/paste from PE.txt The next entry is a 32-bit-value giving the preferred (linear) load address ('ImageBase') of the entire binary, including all headers. This is the address (always a multiple of 64 KB) the file has been relocated to by the linker; if the binary can in fact be loaded to that address, the loader doesn't need to relocate the file again, which is a win in loading time. The preferred load address can not be used if another image has already been loaded to that address (an "address clash", which happens quite often if you load several DLLs that are all relocated to the linker's default), or the memory in question has been used for other purposes (stack, malloc(), uninitialized data, whatever). In these cases, the image must be loaded to some other address and it needs to be relocated (see 'relocation directory' below). This has further consequences if the image is a DLL, because then the "bound imports" are no longer valid, and fixups have to be made to the binary that uses the DLL - see 'import directory' below. Pretty hard to get? Whatever, to make it short. The ImageBase must be - at least - the size of the last Virtual Offset + Virtual Size which is 9000h in our case. You will find the important data at offset 110h (PEStart+50h). Change it from 0080 to 0090 and restart. Now you got to change the jump at offset A41 (jmp 401030) to "jmp 401000" (E9BA8FFFFF) and the mov dword ptr [401102], 0060F9E9 ;; CONVERT PUSH TO JMP mov byte ptr [401106], 00 ;; SAME jmp 401000 ;; JUMP TO REAL ENTRYPOINT to mov dword ptr [401102], 006EF9E9 ;; CONVERT PUSH TO JMP mov byte ptr [401106], 00 ;; SAME jmp 401000 ;; JUMP TO REAL ENTRYPOINT Watch out that you have to recalculate ALL jumps and the "push 407215" now. When you have done it, replace the CCh at the beginning of the file again. If it works: well done; if not: compare my file adding.exe to the one you created. III. BTW So guys, that's all. If you want additional info about the PE format, please read PE.TXT at least 25 times. I hope you learned something from this essay (at least how to enlarge and add a section ;) Greetings go to: +Sandman, Acid Burn, alpine, Blind Angel, Borna Janes, Carpathia, CrazyKnight, DEATH, DEZM, dimwitz, DnNuke, duelist, Eternal Bliss, Fravia+, Iczelion, Jordan, KnowledgeIsPower, Knotty, Lucifer48, MisterE, Neural Noise, noos, Prof.X, R!SC, rubor, Shadow, SiG, tC, The AntiXryst, The Hobgoblin, TORN@DO, ultraschall, viny, Volatility, wAj, _y and all the guys I forget and I'll add next time. Mail: lazarus_hf@hotmail.com URL: hello.to/lazarus #Efnet: #cracking4newbies, #learn2crack (German)