تبلیغات :
آکوستیک ، فوم شانه تخم مرغی، صداگیر ماینر ، یونولیت
دستگاه جوجه کشی حرفه ای
فروش آنلاین لباس کودک
خرید فالوور ایرانی
خرید فالوور اینستاگرام
خرید ممبر تلگرام

[ + افزودن آگهی متنی جدید ]




صفحه 15 از 17 اولاول ... 511121314151617 آخرآخر
نمايش نتايج 141 به 150 از 163

نام تاپيک: آموزش کرک و قفل شکنی ( انگلیسی ) از بهترین کرکرهای روس و ...

  1. #141
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Hands on tutorial for inline patching packed targets.



    Target: Zoom Player Professional (inmatrix.com)

    Tools used:

    OllyDbg 1.09d
    PE iDentifier v0.91
    Note. A product version (as the product itself) doesn't matter, but it's important that all worked and you understand what you do. The second thing that you have to keep in mind is what all the addresses, values and numbers, which corresponds to a disassembled code are represented in hexadimal system, the rest numbers are decimal.

    Author: Wizard
    Date: 16.04.2004
    Difficulty: beginner
    Origin: Don't wish it were easier, wish you were better. Michelangelo


    Introduction
    Zoom Player Professional is one of the most advanced Media Players and DVD Front-End on the PC today, Designed to be simple at first glance while being remarkably dynamic and flexible when used to its full potential.

    Zoom Player works in two modes. A Media mode which can play any file supported by DirectShow (any file which plays in MediaPlayer) and a DVD mode which uses pre-installed DirectShow DVD filters to play DVD content.

    Reconnoitre
    First of all, I downloaded file zp331pro.exe from the official site and then installed it. Once I've done that, I checked out the main executable (zplayer.exe) file. It was sorta packed. To be sure in that I used PEiD to detect the exact packer. PEid detected some old UPX version, maybe because it was modified a bit, but maybe it was so indeed. Anyway PEid said this: "UPX 0.89.6 - 1.02 / 1.05 - 1.24 (Delphi) stub -> Markus & Laszlo".

    Now run the program to see the startup NAG.

    You have to wait for 3 seconds and then press the appropriate button. Once you've pressed the button - program starts and runs fine. Check what do we have. Go to the options menu by pressing Ctrl+P or by clicking the appropriate button on the player's panel (at the bottom-right corner).

    Click information bar in the left column. We see the strings: "Registered to: This copy is unregistered". You may also notice the register button at the bottom of the form. If you press it, it will bring you to the internet register page. That means, if the program hasn't a password/name dialog it should use some other registration way.

    Usually it's a registration file (keyfile). Why? Simple, why does it need inet for? Of course to register you as a legal user for money. After that the registered user will receive some file with key and he'll just put it into the program's folder (usually in the root). It's 99% of cases. Nobody (I mean "any" programs' authors) won't do such tricks as hidden password dialog somewhere by pressing the magic keys combinations or something else even more tricky. That's because usual users don't need a haemorrhoids in their ass, which they couldn't heal. Other words, end-users need simple registration procedure. Of course, it's only my subjective point of view and I can be wrong, but I just confide in my intuition and rely on my experience.

    Get a spade and dig
    Fire up Olly, press F3 and open file zplayer.exe from your ZoomPlayer directory (e.g. "C:Program FilesZoom Player"). You'll see this dialog, telling that the content (of our executable) could be compressed with some packer.


    Press "Yes" button to start analyzing the code. Once it's done you will be directly at UPX's entry point (00683ED0).

    00683ED0 : 60 PUSHAD ; &lt= you're here
    00683ED1 : BE00C05C00 MOV ESI,zplayer.005CC000
    00683ED6 : 8DBE0050E3FF LEA EDI,DWORD PTR DS:[ESI+FFE35000]
    00683EDC : C787C4B01C00 MOV DWORD PTR DS:[EDI+1CB0C4],80067C41
    00683EE6 : 57 PUSH EDI
    00683EE7 : 83CDFF OR EBP,FFFFFFFF
    00683EEA : EB0E JMP SHORT zplayer.00683EFA
    ... skipped ...

    All we need now is to find Real (Original) Entry Point (REP/OEP). You might ask: how to do that? Well, it depends on a cracker, his experience and intuition. Each man will do different. As we know that the program is packed with UPX we can simply scroll down with PageDown key. Personally I pressed it 8 times until I came to the right place. There's also the most simplest method to get there. Press Ctrl+F to start find command dialog and type in it "POPAD", but drop the flag "Entire block", press "Find" button to search the instruction... We're right at the place.
    ... skipped ...
    0068402B : 55 PUSH EBP
    0068402C : FF96 7CA62800 CALL DWORD PTR DS:[ESI+28A67C]
    00684032 : 09C0 OR EAX,EAX
    00684034 : 74 07 JE SHORT zplayer.0068403D
    00684036 : 8903 MOV DWORD PTR DS:[EBX],EAX
    00684038 : 83C3 04 ADD EBX,4
    0068403B : EB D8 JMP SHORT zplayer.00684015
    0068403D : FF96 80A62800 CALL DWORD PTR DS:[ESI+28A680]
    00684043 : 61 POPAD ; &lt= the place we've found

    00684044 : E9 9774F4FF JMP zplayer.005CB4E0 ; jump to the REP
    00684049 : 00 DB 00
    0068404A : 00 DB 00
    0068404B : 00 DB 00

    We searched for "POPAD" (61) instruction, because the program is packed with UPX. I do the same, when unpacking ASPack, but there I need to search a few more times, but for UPX just once (usually), or more times (seldom).

    Note. Of course everything could be much harder sometimes, but if it's a standard of UPX (even a little bit modified) you will always find out. Besides, you can try to find the REP (OEP) with automatic tracing, but this way sometimes (very seldom) can be wrong.

    Put a breakpoint with F2 at address 00684044. Press F9 to run the proggy. Ok, we're right at the place. Now press F7 or F8 (it doesn't matter now, 'cause it's just a jump) once to get to the REP.

    The sneaky arctic fox
    We're at the Real Entry Point now.

    005CB4E0 : 55 PUSH EBP ; &lt= the REP itself
    005CB4E1 : 8BEC MOV EBP,ESP
    005CB4E3 : B9 07000000 MOV ECX,7
    005CB4E8 : 6A 00 PUSH 0
    005CB4EA : 6A 00 PUSH 0
    005CB4EC : 49 DEC ECX

    Now, let's remember what inscriptions have we seen at the NAG. We will need only some phrase. Let it be "This is a Trial". We have to search these words in dump. To do this, do a right click at the address we're now, and then select "Follow in Dump/Selection".

    Go to the dump window. Its address should be now equal to the REP (005CB4E0). We did it to set the cursor in dump window to one of memory cells of the executable we're cracking. This's very important, 'cause otherwise we won't find our string. That's because such search doesn't cover all the windows memory and you have to select the memory range in which you will search by yourself (just like in SoftIce).

    Note. We have set the cursor the that address, because we're now right in the unpacked code. Thus, it has another memory address than UPX entry point. But as we're gonna search for a NAG's string, we must set the cursor to the appropriate memory range. That's because we won't find the string we wanna search in the packed part of the executable. We can only find it in the unpacked memory range.

    Press Ctrl+B in dump to start binary search dialog. Type "this is a trial" in ASCII field, set the flag "Entire block" (if you won't set this flag, the search will only go down from the position your cursor has now, but with it the search will cover all the executable related memory). Press "OK" and wait a bit. No luck, mate! Don't worry, just try to search the same string as Unicode. Type the same string, but in UNICODE field. Trying... Success! Personally I found that string at address 00541790.




    Note. Never set "Case sensitive" flag, when searching for a string in the memory.

    In dump select only the first byte of the string we've found. We select only one byte, because it's always enough to catch a string treatment (if the last is present). Now it should be marked with grey color. Right click on this (selected) byte, then go up through the menu and choose "Breakpoint/Memory, on access".



    Now just press F9 to let the program run and debugger will catch the moment, when the string (from NAG) will be read. You will land somewhere in kernel32 module.

    77E7AB08 : 0FB702 MOVZX EAX,WORD PTR DS:[EDX] ; &lt= you're here
    77E7AB0B : 8A0418 MOV AL,BYTE PTR DS:[EAX+EBX]
    77E7AB0E : 42 INC EDX

    ... skipped ...

    Remove the breakpoint we've set before. To do this, right click at the address you're now in code window (or in dump window) and select "Breakpoint/Remove memory breakpoint" from the appeared pop-up menu.



    Once you've done that go to address 77E7AB26 and press F4 to trace right until the command we need. That's because we don't need to run in the cycle below like a squirrel in a wheel.

    ... skipped ...
    77E7AB26 : C2 1800 RETN 18 ; trace out of here

    77E7AB29 : 2BF7 SUB ESI,EDI
    77E7AB2B : 8975FC MOV DWORD PTR SS:[EBP-4],ESI
    77E7AB2E : E901FEFFFF JMP kernel32.77E7A934


    After you will out of this call you have to trace with F8 for a long time until you will see the NAG. Yes, that screen we've seen before. We're doing all that stuff, because if the program treats to the trial string (before the dialog), that means the NAG will appear soon.

    You maybe wondering, why to do all that tricky stuff, if we can just set a breakpoint to some API call. Well, it's partly correct. First, we don't know on function to set that breakpoint, because it looks like that the program was written in Delphi. We can conclude that because of many unnecessary calls (too deep recursion). Secondly, we just don't have the import table. You may try to Press Ctrl+N to check the names. You will see only kernel names (when you'll be in the range 77E7...) and only the UPX functions (when you'll be in the range 404E..), but there're no real (unpacked) executable names, which can observed. Of course if we were cracking with SoftIce, we would try to set some breakpoint, using our intuition. But for you to say, if this's a Delphi composed proggy then standard API functions won't work, 'cause Delphi uses its own drawing style. You may only try some non-standard functions, like DestroyWindow, CreateWindow or others. All those things you can do with SoftIce, but let's get back to Olly.


    Note. I have to warn you. Don't try to make your life easier by pressing F9 to reach the dialog. That won't help, because in such case program just runs. I can give you a hint how to trace "without the brains". Just after you will be out of the call at address 77E7AB26, trace with F8 (yes, just hold it) till you see the NAG. Olly will stop the cursor at the needed address for you.
    Olly stopped at address 0057ACA7. Now click "Zoom Player" process in your taskbar to see the NAG. Once you'll see it wait for 3 seconds then press appropriate button to continue tracing.

    After the "accident"
    Ok, after you pressed the appropriate button Olly took the control again.

    0057ACA5 : 8B00 MOV EAX,DWORD PTR DS:[EAX]
    0057ACA7 : 8B10 MOV EDX,DWORD PTR DS:[EAX]
    0057ACA9 : FF92 EC000000 CALL DWORD PTR DS:[EDX+EC] ; the NAG itself
    0057ACAF : 837D DC00 CMP DWORD PTR SS:[EBP-24],0 ; &lt= you should be here
    0057ACB3 : 0F84FA000000 JE zplayer.0057ADB3
    0057ACB9 : B201 MOV DL,1

    Take a look around. If we're here (at 0057ACAF) then it must be a way to get here. Scroll a bit up to check, if is there something interesting.

    0057AC19 : E8 E2460100 CALL zplayer.0058F300
    0057AC1E : 803D90315D0000 CMP BYTE PTR DS:[5D3190],0 ; if (@[5D3190]==0) badboy
    0057AC25 : 0F8584000000 JNZ zplayer.0057ACAF ; jumps if registered
    0057AC2B : 807DC3 01 CMP BYTE PTR SS:[EBP-3D],1
    0057AC2F : 7555 JNZ SHORT zplayer.0057AC86

    Do you see what I see? At address 0057AC1E we see an interesting compare. It looks like after that compare the conditional jump at address 0057AC25 won't jump, because we're not registered users. Other words, that jump "should" direct the program over the NAG, right at address where we're now (0057ACAF). That also means that the memory cell with address [5D3190] contains the registration status (0 ain't registered, > 0 is registered).

    All we need to know now is when the program changes the status to unregistered. Other words, when it will write zero value in that memory cell.

    A simple majority
    Press Ctrl+F2 to terminate the program. We need to start the program again to catch a moment, when the cell [5D3190] will be written with 0 (unregistered status). But if we had seen the NAG already we won't catch that moment. That's why we have to start it over. So, run the program again. You will break before the REP at our first breakpoint (at 00684044). Press F8 once. Now we're at the start of the real (unpacked code) again at 005CB4E0.

    Now go to dump window. Press Ctrl+G, type 5D3190 and press "OK" to go for it. You will be at the right place.



    Do the same as we do above (when catching a treatment to the trial string). Other words, first, right click the selected byte (at 5D3190) in dump window. After that select "Breakpoint/Memory, on write". We need only to know when the program will write zero in the memory cell. We already know, where it will read it. Besides, we don't need the last thing at all, because if we patch the memory cell at the right place once (change unregistered status to registered) we'll get registered program without caring about where it will read that memory cell next time to avoid the NAG (maybe also enable disabled/hidden features, whatever).

    Let the program run with F9... Bang! Olly broke-up here.

    0057A28D : C645C300 MOV BYTE PTR SS:[EBP-3D],0
    0057A291 : C60590315D0000 MOV BYTE PTR DS:[5D3190],0 ; &lt= Olly broke-up here

    0057A298 : 8D854CFDFFFF LEA EAX,DWORD PTR SS:[EBP-2B4]
    0057A29E : 8B15 48275D00 MOV EDX,DWORD PTR DS:[5D2748] ; points to "zplayer.regkey"

    Aha, let's remember this place as the first place we've met unregistered state saving. By the way, if you'll trace a bit down you'll see that the program tries to load file "zplayer.regkey" and then to check it for validity. But we're keep going, 'cause we don't care about that key much, our goal is to register program without finding valid serials/keys, etc., but with reversing the unregistered state to registered. That's why keep on going by pressing F9 to see next place (if the program has it).

    Trying... Program ran and NAG has appeared. Ok, that means here's a classic algorithm. First, program moves to a cell unregistered status, and then it tries to load a license file/license ini-file/license key from registry (it doesn't matter, 'cause these are only the methods). Secondly, if it fails to load the license key or it's not valid then it just runs as usual, shows NAG, all pay-features are disabled, etc. If the key is valid (after checking it) the program will definitely move the registered status (in our case something > 0 [1..FF], because of that JNZ, remember) and it will be registered.

    Note. Everything can be not so easy sometimes. Register state cell could be initialized with unregistered status already (yes, during the compilation). Or there could be a few such memory cells. Besides, there could be not such a simple construction like ours:

    CMP BYTE PTR [RegisteredState],0
    JNZ GOOD_BOY
    But there could be some other compare tricks like this:
    MOV EAX,[RegisteredState]
    PUSH EAX
    ... other commands ...
    XCHG DWORD PTR [EBP+A],EDX
    CMP EDX,00BAD000
    JZ BAD_BOY
    Or something else even more perky. Everything depends on an author's fantasy. But it doesn't mean that you can't catch it. It only means that everything ain't so simple.

    Now we know that we have standard (classic) scheme, which means registered state memory cell can be written only twice (next situations only as examples):

    once during the compilation (unregistered/registered state) and then during the program runs (if the key is invalid/valid);
    only during the program runs, first, before the key-check procedure and after that if the key is valid/invalid.
    Therefore, we just need to replace one byte in command MOV BYTE PTR DS:[5D3190],0 to MOV BYTE PTR DS:[5D3190],1 at address 0057A291.

    We don't know exactly what value it should be, but if there was a JNZ instruction, then we suggest that it should be ANY value bigger than zero in byte interval (01..FF).

    By knowing all the above, all we've left is to try will it work or not. It's simple to test. Restart the program again. Press F9. It should break at REP once more. Press F7/F8 to get at REP. We're at address 005CB4E0 again. Press Ctrl+G, type 0057A291 (it's the address we've found earlier) in the field, press "OK". Now, press space and type "MOV BYTE PTR DS:[5D3190],1" instead of "MOV BYTE PTR DS:[5D3190],0", press "Assemble" to confirm the changes.

    As you can see only one byte is changed (from 00 to 01) at address 0057A297. Remember that fact for further patching. After that press F9 for a beta-testing. Hooray!!! It runs fine without the NAG. Besides, if you will go to the options again you won't find the "Register" button in the information bar. Nevertheless, that string still remains "This copy is unregistered". I suppose that's because the program didn't find the reg-file with code and name, but it thinks that it's registered anyway.

    Ok, restart the program again (with Ctrl+F2) and get ready to patch this baby.

    Making an inline patch on the fly
    Run the program with F9 to stop at well-known breakpoint (at 00684044). We know what to patch and where. All we need is to find a place where to make our inline patch. To do this we have to find what ImageBase has the executable. That info we need to find some free space (a sequence of zeroes), which we will use as our patch.

    To find out what ImageBase has the executable open the memory map with Alt+M shortcut. Find the line "zplayer" in owner column and "PE header" in type column.



    Notice the value in the address column. It equals 00400000. That's ImageBase itself. Of course you may also click that line and scroll down in the appeared window to find out the same value (it'll have the appropriate name - Image Base). Actually we need this value only to find a free space from the beginning of the executable. Exactly from "MZ" signature (first two bytes of the program). You will always find a sequence of zeroes in any executable's header. It's a reserved space, which never used. Thus, it can be used.

    We've got all the info to make a patch. Close the memory map and go to the dump window. Press Ctrl+G, type 00400000 (the value we've got earlier) and press "OK". We're at the beginning of the executable (right at the header's beginning of the unpacked executable). Scroll a bit down and look for a sequence of zeroes. Scrolling... What are we're seeing now? A little space at address 00400080. Well, you can use this space, but you rather scroll a bit down more. There should be a space from 00400300. Scrolling again... Aha! Just like I told you, there's a lot of free space at the forenamed address. Most of executables have it, and exactly from that address.

    Go to the code window. Your cursor should still stand at address 00684044. Press space to change the jump from "JMP 005CB4E0" to "JMP 00400300". Other words, we just changing jump from jump to REP to our patch.

    Press enter at the command you just changed, to go to 00400300. You see, it's full of zeroes. Let's create our patch now. Press space and type "MOV BYTE PTR [0057A297],1". Drop the flag "Fill with NOP's" and press "Assemble" (don't close assemble window). That's our patch!

    The one thing we have to do is to direct the program to the REP. Simply, type "JMP 005CB4E0", press "Assemble" again and close the assemble window.



    We're changing 00 to 01 at address [0057A297], because it's the last byte of the command. Other words, it's only the changes we've made to the command. Thus, there's no need to move to the memory the same commands (bytes), but only those, which need to be changed.

    We're done for now. All you have to do is to save all modifications to another or to the same file.

    The spirit of the sea
    I used Zoom Player for a few weeks, but when I started it one day I saw a strange message, which says something about the integrity check. I didn't even treat it seriously and pressed "OK", but the program just exited to my surprise.



    After that, I started the program again and I suddenly understood that it's a hidden protection, which wasn't detected before, because it just couldn't be detected. It seems that this somekind of simple self-check of executable. Other words, it's just a simple CRC check of the module content.

    All we need to do is to eliminate this unexpected message, which stops the program from running. To do this restart the program once again (if it runs). Set a breakpoint at address 00684044, and then press F9. Olly breaks at that address. Now press F8 to step to our inline patch at 00400300. Step till you reach the REP of the program (005CB4E0).

    Now do the same as we do when we were searching the NAG. Other words, do a right click at the address we're now, and then select "Follow in Dump/Selection". Press Ctrl+B, and try to search the "integrity check" as a Unicode string (don't forget to set the flag "Entire block" to cover all the memory range of the executable)... Uh, no luck! Hm, strange. Try to search as an ASCII string. Yes! I found it at 0054A444. Look a bit above, you will see the beginning of the string ("Zoom Player Professional").

    I think that we didn't find it as Unicode, 'cause probably the author made it to save the space. Yes, that's right. He made a simple string ("Zoom Player Professional") to concatenate it with other strings. That's it. That's why we didn't find it as Unicode string. Of course it's only my theory, so don't treat it as a dogma.

    Now set a memory breakpoint on access to the first byte of the string we've found. As you know we don't need, just like with the NAG string we just need to catch a treatment to that string. Therefore, select only one byte at address 0054A444, right click on this (selected) byte, then go up through the menu and choose "Breakpoint/Memory, on access". Now press F9 and wait for approximately 10 seconds. When Olly will break at some address (my was 004D1E82) remove the breakpoint. To do it, right click in code or dump window, then select "Breakpoint/Remove memory breakpoint" from the appeared pop-up menu. Trace with F8 a little until this place:

    0057DF3C : 803D8C315D0000 CMP BYTE PTR DS:[5D318C],0 ; the reason of jump

    0057DF43 : 7544 JNZ SHORT zplayer.0057DF89 ; jump over the NAG
    0057DF45 : 833D64245D0000 CMP DWORD PTR DS:[5D2464],0 ; the second reason
    0057DF4C : 753B JNZ SHORT zplayer.0057DF89 ; another jump
    0057DF4E : 6A00 PUSH 0
    0057DF50 : 8D45F4 LEA EAX,DWORD PTR SS:[EBP-C]
    0057DF53 : 8B0D58245D00 MOV ECX,DWORD PTR DS:[5D2458] ; @("Zoom Player Professional")
    0057DF59 : 8B1554245D00 MOV EDX,DWORD PTR DS:[5D2454] ; @("has failed integrity...")

    0057DF5F : E84C71E8FF CALL zplayer.004050B0 ; it concatenates two strings
    0057DF64 : 8B55F4 MOV EDX,DWORD PTR SS:[EBP-C] ; &lt= you will be here
    0057DF67 : 8D45F8 LEA EAX,DWORD PTR SS:[EBP-8]
    0057DF6A : E8C177E8FF CALL zplayer.00405730 ; converts string to Unicode
    0057DF6F : 8B45F8 MOV EAX,DWORD PTR SS:[EBP-8] ; EAX points to Unicode string
    0057DF72 : 668B0DC8E25700 MOV CX,WORD PTR DS:[57E2C8]
    0057DF79 : 33D2 XOR EDX,EDX
    0057DF7B : E884E0F2FF CALL zplayer.004AC004 ; this is that crazy message

    0057DF80 : 33D2 XOR EDX,EDX

    As you can see, if the content of the memory cell with address [5D318C] or with address [5D2464] is not zero, then you will see no message. That's the way we need. Ok, restart the program once again, press F9. You're at address00684044 again. Trace until the REP. Right click, select "Follow in Dump/Selection". Press Ctrl+G, in the appeared dialog type "005D318C", and press "OK". All we need now is to catch a moment, when this cell will be written with some value (probably zero, because the original [not patched] exe's CRC differs from the patched exe's CRC).

    Note. We need only to know, when one of the cells we be written, because both of 'em compares with zero, and after that jump (or don't) over the message. That's why we need only one of 'em. So, choose any. I chose the first, because it's a BYTE (the second is DWORD).

    To know, when the cell [5D318C] will be written with a value, right click on address 005D318C, select "Breakpoint/Memory, on write". Now just press F9. Once you've done that, you'll see something like this:

    00562DF2 : BA00040000 MOV EDX,400
    00562DF7 : B8DB030000 MOV EAX,3DB
    00562DFC : E85B500500 CALL zplayer.005B7E5C ; returns 0 (bad CRC) || > 0 (good)
    00562E01 : A28C315D00 MOV BYTE PTR DS:[5D318C],AL ; &lt= Olly broke-up here (EAX = 0)

    00562E06 : 33C0 XOR EAX,EAX

    Remember this place (00562E01) and press F9 again to see, if is there any other place before that compare at address 0057DF3C... Bams! And you see the the same message now. It means that there's only one place, where our cell [5D318C] is written with zero value. All we've left to do is to write some positive & bigger than zero value (e.g. 01) into that cell.

    Improving the inline patch
    First of all, you have to decide how to patch it, and then to add that patch you've decided into our inline patch. There're many way of patching it. Your first thought will probably be such. Let's change "MOV BYTE PTR DS:[5D318C],AL" (A2 8C 31 5D 00 => 5 bytes) to "MOV BYTE PTR DS:[5D318C],01" (C6 05 8C 31 5D 00 01 => 7 bytes). Well, not a bad idea, but alas it will increase the command from 5 to 7 bytes. You maybe say, so what. Nothing, but there's a command "XOR EAX,EAX" (33 C0) at address 00562E06, which's exactly two bytes long. If we'll patch in such way we'll just lose the command. As we didn't write this program, then we don't know, how the program will react without that two bytes long command.

    You may patch it in other different ways, but leave it. I think the most "correct" way is to patch so. Don't forget, that we're making an inline patch, so this patch differs from a usual patch (in not packed exe). With such kind of patch you can just imagine what to patch, but don't patch it directly in exe. Because you can't patch it directly in unpacked exe, you have to wait when exe is unpacked and then patch it. That's the main sense of the inline patch.

    What do we need for the final patch. Not too much, just think a bit. Imagine, that we just don't have the command "MOV BYTE PTR DS:[5D318C],AL" in the unpacked exe. Just imagine it. If so, how will the program act in such case? Right, it will always bring us the message, if of couse the second memory cell ([5D2464]) will be zero too. So, if we'll just fill that 5 byte long command with NOP's, then the program will never write the value with zero. That's what we need, but it's not quit enough. The last thing to do is to write the memory cell [5D318C] with some value bigger than zero (e.g. 01).

    The actual patch will be such. Restart the program in the last time, press F9 to get to the well-known breakpoint at 00684044. Press F8 once to get to our patch at address 00400300. We're gonna to add it a bit. Press F8 again (because our first patch works fine, so leave it) to set the cursor to 00400307. Press space now and type "MOV EAX,562E01", press "Assemble". It's the first byte of the address we wanna fill with NOP's. Type "MOV DWORD PTR DS:[EAX],90909090" to fill the first 4 bytes with NOP's. Then type "MOV BYTE PTR DS:[EAX+4],90" to fill the last (the 5th) byte with NOP. After that, type "MOV BYTE PTR DS:[5D318C],1" to set a value we need instead of zero. We set one (01), because so the program will always think that it's CRC is ok. And finally, type the last command of the patch "JMP 005CB4E0", which will return the control to the unpacked program after our patch.



    Yes, such patch is much bigger, but it works every time. Save the changes in the last time and run the program. It works fine.

    The last thing to say
    It's not so hard to find a place or a way to make an inline patch, especially for UPX. I just showed you one of ways of patching. By the way, if you don't familiar too much (or at all) with UPX inline patching, then I recommend you to read Detten's tutorial about how to do it. I recommend you his tutorial, because it's short, simple and pretty clear to start with. Besides, it contains some theory, which I didn't mention in this investigation.

    As the final words of this essay, I have to say, that there're as many ways as many people to do the same as we did above. You can even use different methods of cracking, nothing to say about inline patching, because the last thing might vary from cracker's experience, his programming knowledge, intuition, and of course from his logic. That's why don't worry about anything you can't do. Just keep on going forward and you will do it one day.

  2. #142
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : beginner

    Basic CD check patching tutorial. (Target included)





    Intro

    In this tutorial, we are going to patch a CD check. These days, most CDs have a commercial protection. (eg cu-dilla) You can find an unwrapper for (almost) every protection. But when you unwrapped it, you still need to patch the proggy to run without the CD.

    The example I'm going to patch, is a little proggy that checks if 'the matrix' CD is inserted. Get it here

    TOOLS USED : WDASM, hex-editor or Ollydbg

    Let's dig in



    Where to start?

    Disassemble the example program in W32Dasm.
    If you search for a CD-protection, the best place to start is the API GetDriveType. So hit the import functions button, and look for it.

    Now, what does this API do? Let's check in Win32.hlp :

    UINT GetDriveType(

    LPCTSTR lpRootPathName // address of root path
    );

    The return value is an unsigned INT, with following possible return values :


    Value Meaning


    0 The drive type cannot be determined.
    1 The root directory does not exist.
    2 DRIVE_REMOVABLE The drive can be removed from the drive.
    3 DRIVE_FIXED The disk cannot be removed from the drive.
    4 DRIVE_REMOTE The drive is a remote (network) drive.
    5 DRIVE_CDROM The drive is a CD-ROM drive.
    6 DRIVE_RAMDISK The drive is a RAM disk.
    So, the API checks what type of drive LPCTSTR points to. For a CD-ROM the return value will be 5.


    Now we are ready to check the deadlisting




    The code
    * Possible StringData Ref from Data Obj ->"A:" 0040100C 6836304000 push 00403036 ;Push pointer with address of root path
    * Reference To: KERNEL32.GetDriveTypeA, Ord:00F0h

    00401011 E800010000 Call 00401116
    00401016 83F805 cmp eax, 00000005 ; Check if drive is CD-ROM
    00401019 7411 je 0040102C ; If it is, jump to 'check CD'
    0040101B 803D363040005A cmp byte ptr [00403036], 5A ;check if drive = 'Z'
    00401022 7440 je 00401064 ; If it is, jump to 'CD not found'
    * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
    00401062(U), :004010B0(U), :004010E0(U)

    00401024 FE0536304000 inc byte ptr [00403036] ; Increment first byte of
    ; the string with root path
    ; so it points to the next drive
    0040102A EBE0 jmp 0040100C ; Jump back to check next drive
    What happens in the above code? We start to check if 'a:' is a CD-ROM. If it is no CD-rom, we increment the first byte of the root path-string.
    ( A: becomes B: ) We check the drive again, and so on till we find a CD-ROM drive.




    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401019(C)0040102C 6A00 push 000000000040102E 6A00 push 0000000000401030 6A00 push 0000000000401032 6A00 push 0000000000401034 6A00 push 0000000000401036 6A14 push 00000014 ; Push the max number of bytes to read00401038 68AA314000 push 004031AA ; Push address that will receive Volumename
    * Possible StringData Ref from Data Obj ->"A:"

    0040103D 6836304000 push 00403036 ; Push pointer to root path
    * Reference To: KERNEL32.GetVolumeInformationA, Ord:0162h


    00401042 E8DB000000 Call 00401122 ; Get the Volume Name of the drive
    00401047 51 push ecx
    00401048 33C9 xor ecx, ecx ; Make ecx zero
    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401060(U)

    0040104A 8A8100304000 mov al, byte ptr [ecx+00403000] ; Move byte of 'THE MATRIX' to eax
    00401050 3881AA314000 cmp byte ptr [ecx+004031AA], al ; Cmp volumename with 'THE MATRIX'
    00401056 750A jne 00401062 ; If a byte is different, we jump to 'check next drive'
    00401058 41 inc ecx ; Add 1 to counter
    00401059 83F90A cmp ecx, 0000000A ; First 10 bytes equal?

    0040105C 7202 jb 00401060 ; If not 10 bytes yet, go check next byte
    0040105E EB1E jmp 0040107E ; jump to 'Next CD check'


    The above code is quite simple, the program fetches the VolumeName of the CD-ROM drive, and checks it with a string.
    To know the contents of the string at address 00403000, press the 'data hex' button in Wdasm (you will see that it is 'THE MATRIX').
    If the VolumeName is 'THE MATRIX' we jump to the next CD check, otherwise we jump to 'Check next drive'.


    * Possible StringData Ref from Data Obj ->"A:"

    0040107E 6836304000 push 00403036
    00401083 68C2314000 push 004031C2
    * Reference To: KERNEL32.lstrcpyA, Ord:02DCh

    00401088 E8A1000000 Call 0040112E ; Copy root path in new string


    * Possible StringData Ref from Data Obj ->"Matrix.DVDivX.*"

    0040108D 684A304000 push 0040304A
    00401092 68C2314000 push 004031C2
    * Reference To: KERNEL32.lstrcatA, Ord:02D3h

    00401097 E88C000000 Call 00401128 ; Paste 'MatrixDvDivx.*' behind the root path
    0040109C 6860304000 push 00403060 ; Poiter to a WIN32_FIND_DATA structure

    004010A1 68C2314000 push 004031C2 ; Pointer to the string just created
    * Reference To: KERNEL32.FindFirstFileA, Ord:008Ch

    004010A6 E865000000 Call 00401110
    004010AB 83F8FF cmp eax, FFFFFFFF ; Is the file there?
    004010AE 7505 jne 004010B5 ; then jump to 'Next check'
    004010B0 E96FFFFFFF jmp 00401024 ; else jump to 'Check next drive'
    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 004010AE(C)

    :004010B5 A3BE314000 mov dword ptr [004031BE], eax ; Store File Handle:004010BA FF35BE314000 push dword ptr [004031BE]
    * Reference To: KERNEL32.FindClose, Ord:0088h

    004010C0 E83B000000 Call 00401100 ; Close the searchHandle
    004010C5 A180304000 mov eax, dword ptr [00403080] ; Move size member of WIN32_FIND_DAT to eax

    004010CA 3B055A304000 cmp eax, dword ptr [0040305A] ; Check size with dword
    004010D0 7504 jne 004010D6 ; If not equal, jump to 'Check next Drive'
    004010D2 EB07 jmp 004010DB ; Else, jump to good-guy message
    004010D4 EB05 jmp 004010DB
    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 004010D0(C)

    004010D6 E949FFFFFF jmp 00401024
    * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: 004010D2(U), :004010D4(U)

    004010DB 6A00 push 00000000 ;Good guy Message
    * Possible StringData Ref from Data Obj ->"Great!"

    004010DD 6843304000 push 00403043
    * Possible StringData Ref from Data Obj ->"CD Found"

    004010E2 683A304000 push 0040303A
    004010E7 6A00 push 00000000
    * Reference To: USER32.MessageBoxA, Ord:01BBh

    004010E9 E806000000 Call 004010F4
    004010EE 50 push eax
    * Reference To: KERNEL32.ExitProcess, Ord:0075h

    004010EF E806000000 Call 004010FA ; Exit Program
    In the above code, the following happens :

    First the root path and a file string get pasted together. (eg. 'F:' + 'Matrix.DVDivX.*' = 'F:Matrix.DVDivX.*') Then the program checks if a file which matches the description is there. If not, we jump back to 'check next drive'. If it is there, the program checks its FileSize with the value in 0040305A. (If you press the dataHex button again, you can see that value : 2B6F2000h (mind the reversed order) = 728.702.976 bytes )
    If the FileSize matches this value, the program assumes that the right CD is inserted. If not, we go to 'check next drive' again.




    Patching the code

    If you read all the above code, patching should be easy.

    I assume that your computer has a CD-ROM drive. When the program finds your CD-ROM drive, it will check the VolumeLabel. To prevent from jumping back to 'check next drive' we nop that jump out :

    00401056 750A jne 00401062 -> nop nop


    750A -> 9090


    Next, the program searches for the file, let's change the conditional
    jump so it always jumps to the next check :
    004010AE 7505 jne 004010B5 -> jmp 004010B5


    7505 -> EB05

    The last thing we have to patch, is the size-check :

    004010D0 7504 jne 004010D6 -> nop nop


    7504 -> 9090


    Get all the FileOffsets of the above codelines in Wdasm (displayed at the bottom of the screen in hex), fire up your hexeditor
    and change the appropriate bytes.

    Run the program, now it thinks the CD is inserted ! Job done

    The method to check the CD I used in my little matrix-proggy is very simular to how it's done in real programs. Of course, don't expected that the program you want to patch, will only check one file, or will have such clean code as my proggy )
    Good luck!

  3. #143
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : beginner

    Writing a bruter for the Ancient One Crackme.
    (MASM source code for the bruter included)



    TARGET: Bruteforcing Ancient One crackme by Zephyrous : here

    Well, I have to send some credits to bluffer for trying to solve this bruteforcer
    and giving me some help actually, hello there bud

    Zephy: For giving me an idea of how to bruteforce another (faster) way, thank you

    Detten: Hello! :-)


    Ok, lets debug the application and quickly set a breakpoint on GetWindowText()
    type some serial (e.g. "BoR0")

    Ollydbg breaks on our first letter.

    ---snippet---
    0040112E |. 83F8 08 CMP EAX,8
    00401131 |. 75 3D JNZ SHORT ch01.00401170
    00401133 |. E8 58FFFFFF CALL ch01.00401090
    00401138 |. 85C0 TEST EAX,EAX
    0040113A |. 0F84 08010000 JE ch01.00401248
    00401140 |. E8 7BFFFFFF CALL ch01.004010C0
    ---snippet---

    compare length of chars with 8
    if not equal, jump to 00401170 which is NOT where we need to be at.

    Ok, let's enter some serial that contains 8 chars (e.g. "BoR0BoR0")

    Jump is not taken, COOL! :p

    I didn't see anything interesting in the call that is on address 00401133,
    so lets see whats in the call that is on address 00401140.

    ---snippet---
    004010C0 /$ 68 90124000 PUSH ch01.00401290 ; /Arg1 = 00401290 ASCII "BOR0BOR0"
    004010C5 |. E8 66FFFFFF CALL ch01.00401030 ; ch01.00401030
    004010CA |. 35 5587BAA7 XOR EAX,A7BA8755
    004010CF |. F7D8 NEG EAX
    004010D1 |. 1BC0 SBB EAX,EAX
    004010D3 |. 40 INC EAX
    004010D4 . C3 RETN
    ---snippet---

    Mmm, looks like this is what we needed. Enter this call for more information :-)

    ---snippet---
    00401030 /$ 55 PUSH EBP
    00401031 |. 8BEC MOV EBP,ESP
    00401033 |. 56 PUSH ESI
    00401034 |. 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
    00401037 |. B8 C59D1C81 MOV EAX,811C9DC5
    0040103C |. 33C9 XOR ECX,ECX
    0040103E |> 33D2 /XOR EDX,EDX
    00401040 |. 8A1431 |MOV DL,BYTE PTR DS:[ECX+ESI]
    00401043 |. 33C2 |XOR EAX,EDX
    00401045 |. 41 |INC ECX
    00401046 |. 69C0 93010001 |IMUL EAX,EAX,1000193
    0040104C |. 83F9 08 |CMP ECX,8
    0040104F |.^72 ED JB SHORT ch01.0040103E
    00401051 |. 5E POP ESI
    00401052 |. 5D POP EBP
    00401053 . C2 0400 RETN 4
    ---snippet---

    As you can see by the algo, it xors every char into eax and multiplies the same with 0x1000193.
    Don't forget that EAX is initialized as 0x811C9DC5.

    We're back to 004010CA. Xors final eax with 0xA7BA8755, NEGs it, substract with borrow
    and then increases eax. Looks pretty tough to be solved by pen & paper huh?

    Here's our solution:

    ---snippet---
    .386
    .model flat, stdcall
    option casemap :none

    include masm32/include/windows.inc
    include masm32/include/user32.inc
    include masm32/include/kernel32.inc

    includelib masm32/lib/user32.lib
    includelib masm32/lib/kernel32.lib

    .DATA
    MYLOOP dd 0 ;you maybe ask why a variable? our registers are not safe :(
    PREFIX db "%8X", 0
    BUFFER db 9 dup(0) ;buffer contained of 8 chars and one terminator

    .CODE
    START:
    MOV EAX, 811C9DC5h ;starting code of zephy's algo
    XOR ECX, ECX ;...

    PUSHAD ;save all registers
    INC MYLOOP ;increase loop

    ;convert our serial into a string
    INVOKE wsprintf, ADDR BUFFER, ADDR PREFIX, MYLOOP

    POPAD ;bum!

    ;Main loop that xors our chars with eax and then multiplies the same
    LOOPHERE:
    XOR EDX, EDX
    MOV DL, BYTE PTR [BUFFER+ECX]
    XOR EAX, EDX
    INC ECX
    IMUL EAX, EAX, 1000193h
    CMP ECX, 8
    JB LOOPHERE

    ;The final eax modifying
    XOR EAX, 0A7BA8755h
    NEG EAX
    SBB EAX, EAX
    INC EAX

    TEST EAX, EAX ; if eax == 0
    JZ START ; back to start

    ;if eax != 0, a valid serial has been found

    invoke MessageBox, 0, 0, ADDR BUFFER, 0 ;notify the user
    invoke ExitProcess, 0 ;thanks ;-)
    end START
    ---snippet---


    Should take around 20 mins or so (1.6GHz). That'd be all for now.


    ah thanks for mention Boro

    hope i could add a few comments to this tut as you know i was loooking for a sppedier way that eliminates the twenty minutes

    i removed the wsprintf and coded an inline convertor which reduced the time to about 10 minutes
    and i came to #win32asm on efnet to bug some of them to find some more improvements parabytes there suggested some improvements in the convertor like stosd instead of stosb which were marginal improvemnts then scali took an interest in the code and coded a c++ and applied pentium optimization switches and it drastically reduced the time to about one minute iam pasting below the c++ code that was from scali

    #include <stdio.h>

    int main(void)
    {
    char* table = "0123456789ABCDEF";
    char key[] = "F000FFF0";

    for (unsigned int a = 0; a < 16; a++)
    {
    key[0] = table[a];

    for (unsigned int b = 0; b < 16; b++)
    {
    key[1] = table[b];

    for (unsigned int c = 0; c < 16; c++)
    {
    key[2] = table[c];

    for (unsigned int d = 0; d < 16; d++)
    {
    key[3] = table[d];

    for (unsigned int e = 0; e < 16; e++)
    {
    key[4] = table[e];

    for (unsigned int f = 0; f < 16; f++)
    {
    key[5] = table[f];

    for (unsigned int g = 0; g < 16; g++)
    {
    key[6] = table[g];

    for (unsigned int h = 0; h < 16; h++)
    {
    key[7] = table[h];

    unsigned int hash = 0x811C9DC5;

    for (unsigned int i = 0; i < 8; i++)
    {
    hash ^= (unsigned char)key[i];
    hash *= 0x1000193;
    }

    if (hash == 0xA7BA8755)
    goto end;
    }
    }
    }
    }
    }
    }
    }
    }
    end:
    printf( "%s\n", key );

    return 0;
    }


    commadline for gpp
    g++ -Wall -O3 bruter.c -o bruter
    [bluffer@]$ ./bruter F000FFF0 Bruting time ->69sec

  4. #144
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : beginner

    Not every application uses a simple GetDlgItemText or simular API to get the input from an editbox.
    This crackme by basse uses an alternate way to grab the keyboard input.



    Intro

    The next crackme has some interesting but unusual approaches. Curious? Read on

    I'm going to explain how this crackme works, but you won't find a valid solution in here, if you want to find one, you'll have to brute it...


    TOOLS USED : WDASM


    Let's dig in



    Where to start?

    Ok, Disassemble the crackme with WDasm. We see it is not packed. And because it is written using MASM, it's pretty clear code
    After a quick look in the string table and the import table we can notice the following :

    There is no GetDlgItemTextA or GetWindowText
    The author uses a hook (SetWindowsHookExA) to get the input...

    If we try to enter a combination 3 times, the program crashes...ans possibly your computer too! How is that possible?
    The author uses the 'foofbug' here, an opcode combination that crashes Pentium and Pentium MMX procesors. (Find more info on this subject here)
    Followed by some garbage code...no wonder we crash

    Ok, time to check out the code!




    The code
    ; Beginning of DlgProc

    0040102C 55 push ebp0040102D 8BEC mov ebp, esp0040102F 8B4508 mov eax, dword ptr [ebp+08]00401032 A30D314000 mov dword ptr [0040310D], eax00401037 817D0C11010000 cmp dword ptr [ebp+0C], 00000111 ;WM_COMMAND ?0040103E 0F85FF000000 jne 0040114300401044 8B4510 mov eax, dword ptr [ebp+10]00401047 663DB90B cmp ax, 0BB9 ; Test-button pressed ?0040104B 0F85D8000000 jne 0040112900401051 FF35FF304000 push dword ptr [004030FF] ; Push hash00401057 C705FF304000ADDED0BA mov dword ptr [004030FF], BAD0DEAD ; Restore init values00401061 C6050831400035 mov byte ptr [00403108], 35 ; Restore init values00401068 6A00 push 00000000
    * Possible Reference to Dialog: MYDIALOG, CONTROL_ID:0BB8, ""

    0040106A 68B80B0000 push 00000BB8
    0040106F FF7508 push [ebp+08]
    * Reference To: USER32.SetDlgItemTextA, Ord:0228h

    00401072 E827020000 Call 0040129E ; Empty the edit box
    00401077 58 pop eax ; pop hash
    00401078 3DF700FB02 cmp eax, 02FB00F7 ; check hash

    0040107D 7529 jne 004010A8 ; If not equal, inc counter
    0040107F 6A40 push 00000040 ; Else good-guy message
    * Possible StringData Ref from Data Obj ->"Rev"
    00401081 68AA304000 push 004030AA
    * Possible StringData Ref from Data Obj ->"Good job! You made it!"

    00401086 6879304000 push 00403079
    0040108B FF7508 push [ebp+08]
    * Reference To: USER32.MessageBoxA, Ord:01BBh

    0040108E E8FF010000 Call 00401292 ; Display good-guy message
    * Possible StringData Ref from Data Obj ->"Success!"
    00401093 6870304000 push 00403070
    00401098 FF350D314000 push dword ptr [0040310D]
    * Reference To: USER32.SetWindowTextA, Ord:0259h

    0040109E E807020000 Call 004012AA ; Put "success" in captionbar

    Ok, what do we have here? If we press the 'test' button, a hash value get's checked with 02FB00F7h, if we would like to patch, the jnz is the place
    As said before, we do not find the code in the DlgProc that calculates the hash. There is no WM_CHAR message too... but we get a hint from the import table (SetWindowsHookExA) that this proggy uses a hook to get the input...

    This is what the API reference says about SetWindowsHookExA :

    The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. An application installs a hook procedure to monitor the system for certain types of events. A hook procedure can monitor events associated either with a specific thread or with all threads in the system. This function supersedes the SetWindowsHook function.

    HHOOK SetWindowsHookEx(

    int idHook, // type of hook to install
    HOOKPROC lpfn, // address of hook procedure
    HINSTANCE hMod, // handle of application instance
    DWORD dwThreadId // identity of thread to install hook for
    );



    We find it here :
    :00401143 817D0C10010000 cmp dword ptr [ebp+0C], 00000110 ; WM_INITDIALOG:0040114A 7543 jne 0040118F:0040114C C6050831400035 mov byte ptr [00403108], 35 ; init var:00401153 6A00 push 00000000 ; hook all treads :00401155 FF3509314000 push dword ptr [00403109] ; handle:0040115B 68C0114000 push 004011C0 ; HookProcedure:00401160 6A03 push 00000003 ; WH_GETMESSAGE
    * Reference To: USER32.SetWindowsHookExA, Ord:025Dh

    00401162 E849010000 Call 004012B0
    00401167 0BC0 or eax, eax ; if Hook succeeds
    00401169 7405 je 00401170
    0040116B A303314000 mov dword ptr [00403103], eax ; Save hook handle
    Next, the focus is set on the editbox, we are ready for input


    What happens in the above code? Well, when the DialogBox is created the WM_INITDIALOG message is sent, so this code gets executed once at the beginning.
    First the variables for the hash calculation are initialized. (BYTE ptr [403108] = 35 ) and DWORD ptr [4030FF] is already initialized at BAD0DEADh

    Next, the hook is set up. It' s a hook for WH_GETMESSAGE and the hook routine starts at address 4011C0h.


    Let's check out this hook routine :


    004011C0 55 push ebp004011C1 8BEC mov ebp, esp004011C3 837D0800 cmp dword ptr [ebp+08], 00000000 004011C7 731A jnb 004011E3 ; If we have input, go to 'calculate hash'004011C9 FF7510 push [ebp+10] ; Else pass to next004011CC FF750C push [ebp+0C] ; hook in the chain004011CF FF7508 push [ebp+08]004011D2 FF3503314000 push dword ptr [00403103]
    * Reference To: USER32.CallNextHookEx, Ord:0014h

    004011D8 E89D000000 Call 0040127A
    004011DD C9 leave
    004011DE C20C00 ret 000C
    004011E1 EB73 jmp 00401256
    * Referenced by a (U)nconditional or (C)onditional Jump at Address:004011C7(C)

    004011E3 837D0800 cmp dword ptr [ebp+08], 00000000 ; Check again
    004011E7 756D jne 00401256
    004011E9 8B5510 mov edx, dword ptr [ebp+10]
    004011EC 817A0402010000 cmp dword ptr [edx+04], 00000102 ;WM_CHAR ?
    004011F3 7561 jne 00401256
    004011F5 8B4208 mov eax, dword ptr [edx+08] ; Move CHAR to eax
    004011F8 8B4A08 mov ecx, dword ptr [edx+08] ; Move CHAR to ecx

    004011FB 2A0508314000 sub al, byte ptr [00403108]
    00401201 880D08314000 mov byte ptr [00403108], cl
    00401207 D315FF304000 rcl dword ptr [004030FF], cl ; Start calculation
    0040120D A008314000 mov al, byte ptr [00403108]
    00401212 33C8 xor ecx, eax
    00401214 C1E007 shl eax, 07
    00401217 33C8 xor ecx, eax
    00401219 C1E007 shl eax, 07
    0040121C 33C8 xor ecx, eax
    0040121E C1E007 shl eax, 07
    00401221 33C8 xor ecx, eax
    00401223 C1E003 shl eax, 03
    00401226 33C8 xor ecx, eax
    00401228 310DFF304000 xor dword ptr [004030FF], ecx
    0040122E FF35FF304000 push dword ptr [004030FF] ; Store hash
    ...


    The next part, displays the hash in the captionbar (I think) Although I didn't see it (in XP) :(...

    0040126F C9 leave
    00401270 C20C00 ret 000C

    Ok, in the hook procedure all the CHARS are intercepted and the hash is further calculated every time you press a key.
    If you want to know a valid serial, you have to write a bruter with the code from the hook routine. keep in mind that the hash is initialized BAD0DEADh. The hash result should be 02FB00F7h.

    The second byte var, is not needed for the algo, so we can cut it out to speed up the bruting.
    This is the hash calculation part out of the hook routine (a little optimized


    mov al, byte ptr [serial+edi]<br>
    rcl dword ptr [hash],al <br>
    shl eax, 7<br>
    xor ecx, eax<br>

    shl eax, 7<br>
    xor ecx ,eax<br>
    shl eax, 7<br>
    xor ecx, eax<br>
    shl eax, 3<br>
    xor ecx, eax<br>

    xor dword ptr [hash], ecx<br>
    inc edi </p>


    The serial points to an address where you should store the combination currently bruting. edi is the counter.
    Let it run till byte ptr [serial+edi] = 0
    A little problem is that we can't know the keylength...if this crackme uses 10 chars and only lower cased chars, we have :


    i=1
    SUM (26^i) = 146.813.779.479.510 possibilities.
    i 10

    To put that in perspective, if you process 1 milion serials each second, you would still need 4.6 years :(
    And then the serial could be longer, and we don't even know the range (1-9, a-z,A-Z,...) So feel free to brute it
    Although the algorithm probably allows a lot of valid keys


    => Note from the author if you want to start bruting

    It's only numbers, 0 - 9, and the length is 10.
    If you get bored, try *******967 (the * you have to brute :-)

    Basse

    Now you should be able to brute it in little time...

  5. #145
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : newbie

    This is my tutorial about ASProtect. Well, actually it is my attempt to understand what ASProtect doing to packed programs. I wrote this tut maybe couple days ago and yet today I would change some parts if I had a time.

    There is an 'interactive' Ollydbg plugin tutorial in the attachment, or you can just read the regular tutorial below.

    Enjoy and post questions, commmets, corrections, ideas ..... I need all of them ;-)




    __..--~~""~~--.._( Unpacking ASProtect )_..--~~""~~--..__


    Lately I had some succses with unpacking ASProtect (half)manually so I have decided to share this experience with others (beginners).

    Like many other noobs, I have read dozen of tutorials which describe unpacking ASProtect, but blindly following that tutorials I couldn't unpack anything by myself. Those "...just press Ctrl+F9 now and you will emediatley land on OEP..." or "...enter tc>900000 and hit run..." have take me usually to some exception that Olly couldn't pass. So I started to digging and pasing through ASPr code just to tray figure something myself. Well, I had some sucsess ;-)


    1. We gonna need something for this tutor:

    - target: Easy Video Capture v1.30 (~600kb in size) , video-capture.info
    - OllyDbg 1.10 (try to have it's plugins, esp. CommandBar and Dump)
    - LordPE
    - ImportREC


    2. IsDebuggerPresent check:

    First stepp is of course, to install our app and then load it to Olly.
    ASProtect have option to detect debugger through IsDebuggerPresent API function. Some protected programs use that option and some doesn't which depends of app autor. I usually just run first time app in Olly just to see will I get that message "Debugger detected...". This option is just pain in the ass because we must spend some time to click on IsDebuggerPresent plugin in Olly if program uses this check and that is quite boring since usually I must restart target couple times. Buah, our target is using that check. Ok, find someware that plugin for Olly which fools this check because that is fastest way to pass it, but I'm gonna also describe manually how to do it in next part. And next part is:


    3. Finding OEP:

    Finding OEP is not some hard thinking proces, it's acctually well know Shift+F9 + counting exceptions and then watching jumps and lots of tracing through code.
    So, go to Olly Debugging Options and uncheck all that exceptions except those memory violations in KERNEL32.DLL.

    Also we must fool debugger check. In CommandLine plugin type "bp IsDebuggerPresent" and press ENTER; or if you have not that plugin right click on CPU window in Olly, select "Search for" ... "Names in all modules", find Eport KERNEL32.IsDebuggerPresent, right click and "Toggle brakpoint". Now just press Shift+F9 and after 15 times you should land in kernel:




    77E7276B > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
    77E72771 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
    77E72774 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2] <---Follow this address in dump
    77E72778 C3 RETN



    On first line is our breakpoint, remove it and go with F8 to 3-th line and follow that addres in dump:


    7FFDF002 01 00 FF FF FF FF 00 00 40 00 A0 1E 24 00 00 00 _.ÿÿÿÿ..@. _$...

    Maybe in your case this will look diferent, but first byte must be 01. That byte is confirmation that debuger is present and you need set it on zero. Rigt click on that 01 byte in dump and chose "Bynary - Fill with 00's". It should look like this now:

    7FFDF002 00 00 FF FF FF FF 00 00 40 00 A0 1E 24 00 00 00 ..ÿÿÿÿ..@. _$...

    You have just fooled debugger check. Probably all this you know from before.

    Now we must continue with Shift+F9. I press first until bp 15 times and after 16 and then program has started. So I have pressed it 31 times. Now we need again but this time press it only 30 times. Also, you culd not even to count untill you triger bp in kernel and then count only last 16-1. You should land now here:



    00B339EC 3100 XOR DWORD PTR DS:[EAX],EAX <---- Your here on the exception!
    00B339EE 64:8F05 00000000 POP DWORD PTR FS:[0]
    00B339F5 58 POP EAX <---- Place bp here!
    00B339F6 833D B07EB300 00 CMP DWORD PTR DS:[B37EB0],0
    00B339FD 74 14 JE SHORT 00B33A13
    00B339FF 6A 0C PUSH 0C
    00B33A01 B9 B07EB300 MOV ECX,0B37EB0
    00B33A06 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8]
    00B33A09 BA 04000000 MOV EDX,4
    00B33A0E E8 2DD1FFFF CALL 00B30B40
    00B33A13 FF75 FC PUSH DWORD PTR SS:[EBP-4]
    00B33A16 FF75 F8 PUSH DWORD PTR SS:[EBP-8]
    00B33A19 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
    00B33A1C 8338 00 CMP DWORD PTR DS:[EAX],0
    00B33A1F 74 02 JE SHORT 00B33A23
    00B33A21 FF30 PUSH DWORD PTR DS:[EAX]
    00B33A23 FF75 F0 PUSH DWORD PTR SS:[EBP-10]
    00B33A26 FF75 EC PUSH DWORD PTR SS:[EBP-14]
    00B33A29 C3 RETN <--- Or place bp here!



    Now, I'm gonna tell you something from my experience. I have notice mostly two variations of ASProtect: first group have this last pice of code that you see above exactly the same while second have differnt with some calls and jumps. At this first group it is a very easy to find OEP, while for second one it needs to be more patient. Also, there are no anti-debug tricks after this period. To continue with our target, place bp on one of two places that I have marked on abowe code and run app. We have reall boring job now - we must trace with F7 to see what code is doing. It will circle around and around with conditional jumps. If you notice that behaviouring, place bp under couple such jumps and run program to save time, for example:
    After you are on that last RETN, continue with F7. Look where jumps are throwing you and you'll see that loot of jumps has no purpose at all - they throw you at the same place where you going to pass when they don't execute. After some time you will find yourself here:



    00B45ED4 81EA 314F0F4A SUB EDX,4A0F4F31
    00B45EDA 0F81 1F000000 JNO 00B45EFF
    00B45EE0 E8 08000000 CALL 00B45EED <------------------------ Place bp here!
    00B45EE5 F0:69EE 8F1C25FA LOCK IMUL EBP,ESI,FA251C8F
    00B45EEC AB STOS DWORD PTR ES:[EDI]
    00B45EED E8 0B000000 CALL 00B45EFD
    00B45EF2 87B4DD 522320D9 XCHG DWORD PTR SS:[EBP+EBX*8+D9202352],E>
    00B45EF9 9E SAHF
    00B45EFA 7F 4C JG SHORT 00B45F48
    00B45EFC 95 XCHG EAX,EBP
    00B45EFD 59 POP ECX
    00B45EFE 5E POP ESI
    00B45EFF 81C2 2D4F0F4A ADD EDX,4A0F4F2D
    00B45F05 81EB 4D667D59 SUB EBX,597D664D
    00B45F0B 81FA 54EBFFFF CMP EDX,-14AC
    00B45F11 ^0F85 5BFFFFFF JNZ 00B45E72
    00B45F17 E8 0E000000 CALL 00B45F2A <----------------------- Place bp here!
    00B45F1C 4E DEC ESI
    00B45F1D 6F OUTS DX,DWORD PTR ES:[EDI] 00B45F1E 7C 05 JL SHORT 00B45F25


    You just keep on tracing with F7 and you'll see that these two jumps just spining you around. So place bp under them on those calls and run app. You will land on that second call and some code below will change. Continue with F7 and you'll find one more jump that is doing similar thing:



    00B44E14 ^75 E9 JNZ SHORT 00B44DFF
    00B44E16 5E POP ESI <--------------- Place bp here and run app!
    00B44E17 5B POP EBX
    00B44E18 5D POP EBP
    00B44E19 C2 0800 RETN 8
    00B44E1C 0102 ADD DWORD PTR DS:[EDX],EAX
    00B44E1E C3 RETN


    Place bp on marked place and run target. Continue with F7 and you'll land on POPAD opcode after which one there are lots of JMP's. This pice is standard in this version of ASProtect and this is place where ASProtect is stealing bytes (but later about that). Pass this junk of code with F7 to the last RETN at:



    00B45000 68 C3F44000 PUSH 40F4C3
    00B45005 68 DB4DB400 PUSH 0B44DDB
    00B4500A C3 RETN <--------- Until this RETN

    Then execute that RETN:

    00B44DDB EB 01 JMP SHORT 00B44DDE
    00B44DDD 9A 51579CFC BF1F CALL FAR 1FBF:FC9C5751
    00B44DE4 4E DEC ESI
    00B44DE5 B4 00 MOV AH,0
    00B44DE7 B9 5E140000 MOV ECX,145E
    00B44DEC F3:AA REP STOS BYTE PTR ES:[EDI]
    00B44DEE 9D POPFD
    00B44DEF 5F POP EDI
    00B44DF0 59 POP ECX
    00B44DF1 C3 RETN <-------------------- Put bp here!


    Now you can trace further with F7 (if you want to see what is doing here - nothing special) or put bp on this new RETN and start target. You will see that lot of code has changed at the bottom of the Olly window, but notice where this last RETN is returning you! It returns you to VideoCap.0040F4C3. Execute that RETN with F7 and youl land here:



    0040F4C3 FF15 30174100 CALL DWORD PTR DS:[411730] ; msvcrt.__set_app_type
    0040F4C9 59 POP ECX
    0040F4CA 830D F4AE4100 FF OR DWORD PTR DS:[41AEF4],FFFFFFFF
    0040F4D1 830D F8AE4100 FF OR DWORD PTR DS:[41AEF8],FFFFFFFF
    0040F4D8 FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode
    ....
    ....
    ....


    That oppcode at 0040F4C3 is our false OEP!

    Press Ctrl+A to analize code and scroll a litle bit up. Those zeroes are stolen bytes/opcodes/instructions and real OEP starts at first zero byte at 0040F496!



    0040F490 $-FF25 50174100 JMP DWORD PTR DS:[411750] ; msvcrt._ftol
    0040F496 00 DB 00 <-------------------------This shuld be real OEP.
    0040F497 00 DB 00
    0040F498 00 DB 00
    ...
    [cut]
    ...
    ...
    0040F4C0 00 DB 00
    0040F4C1 00 DB 00
    0040F4C2 . 00FF ADD BH,BH
    0040F4C4 . 15 30174100 ADC EAX,VideoCap.00411730
    0040F4C9 . 59 POP ECX
    0040F4CA . 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
    0040F4D1 . 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
    0040F4D8 . FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode


    Now you have founded false and right OEP! False is at the 0040F4C3 and real is at the 0040F496 address! Remember that two addresses!!! What is false and what purpose of false OEP I will explain in next part:


    4. False and right OEP, stolen bytes:

    What is this thing with false-real OEP and stolen bytes? From where these bytes are stolen and why? This used to confusing me in the beining reading others tutorials.

    The answer is very simple:
    You know that every program has it's beginning, first line of code to process. That first line is OEP. You also know that packers unpacking program in memory and then execute it right from that OEP. ASProtect is doing that litle diferent. ASProtect copyes first couple instructiones from packed program to itself code and substitute those in program with zeros. After unpacking program in memory it executes those couple instructions (that should be in original program) within it self code and then continue to run original program from that address after that zeroes. Now we gonna find stolen bytes. These bytes can be little hard to determine because ASProtect mix them with some junk code just to confuse reverser. Only in one version of ASProtect I have fand those stolen bytes in original form.
    Also you must know that usualy programs compiled with same compiler have same or similar first instructions. That can help you because if you open some other nonpacked program in Olly which is compiled with same compiler you can see how OEP shuld look like and you'll know when you see stolen bytes somewhere in ASProtect code.

    Now we gonna find that bytes. At first look, this program look like it's compiled in some MS Visual C++. Open couple nonpacked programs in Olly and scan them with PEiD and you will easy notice diference between Delphy, Borlland's C++, MS Visual C++ ... In most cases your target will be compiled in some MSV C++.
    So now restart target in Olly, pass that exceptions and debugger check again and stop at POPAD opcode after which you can notice lot of jumps:



    00B45134 61 POPAD
    00B45135 F2: PREFIX REPNE: ; Superfluous prefix
    00B45136 EB 01 JMP SHORT 00B45139
    00B45138 F0:EB 01 LOCK JMP SHORT 00B4513C ; LOCK prefix is not allowed
    00B4513B 698D 6424D683 C4>IMUL ECX,DWORD PTR SS:[EBP+83D62464],EBF>
    00B45145 02CD ADD CL,CH
    00B45147 2089 442400F2 AND BYTE PTR DS:[ECX+F2002444],CL
    00B4514D EB 01 JMP SHORT 00B45150
    00B4514F -E9 C1E8E136 JMP 37963A15
    00B45154 EB 01 JMP SHORT 00B45157
    00B45156 -0F83 E0413EEB JNB EBF2933C
    00B4515C 02CD ADD CL,CH
    00B4515E 2068 64 AND BYTE PTR DS:[EAX+64],CH
    00B45161 51 PUSH ECX
    00B45162 B4 00 MOV AH,0
    00B45164 58 POP EAX
    00B45165 FF50 09 CALL DWORD PTR DS:[EAX+9]
    00B45168 0FF39A F29A7751 PSLLQ MM3,QWORD PTR DS:[EDX+51779AF2]
    00B4516F B4 00 MOV AH,0
    00B45171 F0:E8 F2F269F3 LOCK CALL F41E4469 ; LOCK prefix is not allowed
    00B45177 58 POP EAX
    00B45178 EB 01 JMP SHORT 00B4517B



    This place contains our stolen bytes but also lot of junk code. Trace through with F7 and you will keep finding byte by byte. All that jumps and weird opcodes are just junk and I will show here just real stolen bytes:



    00B451B7 55 PUSH EBP This is first stolen byte and it belongs on OEP.

    00B451C0 8BEC MOV EBP,ESP This is the next stolen part.
    00B451C2 6A FF PUSH -1
    00B451C4 68 D8354100 PUSH 4135D8
    00B451C9 68 1CF64000 PUSH 40F61C
    00B451CE 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]

    00B463F9 50 PUSH EAX This one too.

    00B45206 64:8925 00000000 MOV DWORD PTR FS:[0],ESP Next part.
    00B4520D 83EC 68 SUB ESP,68

    00B45239 53 PUSH EBX

    00B4526B 56 PUSH ESI

    00B4529D 57 PUSH EDI

    00B452A6 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP This is last stolen pice.
    00B452A9 33DB XOR EBX,EBX
    00B452AB 895D FC MOV DWORD PTR SS:[EBP-4],EBX
    00B452AE 6A 02 PUSH 2


    Of course you will need some experience to recognize real bytes but that will come after few unpacked targets. This code above is all that is stolen. Trace further until you reach here:



    00B452B8 68 C3F44000 PUSH 40F4C3
    00B452BD 68 9350B400 PUSH 0B45093
    00B452C2 C3 RETN


    Look at that PUSH 40F4C3 opcode. Do you remmber address of the false OEP? If not, than scroll up in this tutorial where I told you to remember OEP's addresses! Yep, that opcode pushes false OEP address! Change that address to 0040F496 which is address of the real OEP by pressing SPACE and changing opcode to PUSH 0040F496. It should look now:



    00B452B8 68 96F44000 PUSH 40F496
    00B452BD 68 9350B400 PUSH 0B45093
    00B452C2 C3 RETN


    Now trace further:



    00B45093 EB 01 JMP SHORT 00B45096
    00B45095 9A 51579CFC BFD7 CALL FAR D7BF:FC9C5751 ; Far call
    00B4509C 50 PUSH EAX
    00B4509D B4 00 MOV AH,0
    00B4509F B9 5E140000 MOV ECX,145E
    00B450A4 F3:AA REP STOS BYTE PTR ES:[EDI]
    00B450A6 9D POPFD
    00B450A7 5F POP EDI
    00B450A8 59 POP ECX
    00B450A9 C3 RETN <------------- Put bp!


    Place bp on this last RETN, run target, remove bp, execute RETN with F7 and you'll land on real OEP address right on that first zeros. Pres Ctrl+A and it suld look like this:



    0040F496 00 DB 00
    0040F497 00 DB 00
    0040F498 00 DB 00
    0040F499 00 DB 00
    0040F49A 00 DB 00
    0040F49B 00 DB 00
    ...
    [cut]
    ...
    0040F4BD 00 DB 00
    0040F4BE 00 DB 00
    0040F4BF 00 DB 00
    0040F4C0 00 DB 00
    0040F4C1 00 DB 00
    0040F4C2 . 00FF ADD BH,BH
    0040F4C4 . 15 30174100 ADC EAX,VideoCap.00411730
    0040F4C9 . 59 POP ECX
    0040F4CA . 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
    0040F4D1 . 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
    0040F4D8 . FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode
    0040F4DE . 8B0D E8AE4100 MOV ECX,DWORD PTR DS:[41AEE8]
    0040F4E4 . 8908 MOV DWORD PTR DS:[EAX],ECX
    0040F4E6 . FF15 84174100 CALL DWORD PTR DS:[411784] ; msvcrt.__p__commode


    Now you need to put in all that stolen bytes that we have founded before. Do it by asembly or by binary editing. At the end it should look like this (blue was stolen):



    0040F496 /. 55 PUSH EBP
    0040F497 |. 8BEC MOV EBP,ESP
    0040F499 |. 6A FF PUSH -1
    0040F49B |. 68 D8354100 PUSH VideoCap.004135D8
    0040F4A0 |. 68 1CF64000 PUSH VideoCap.0040F61C ; JMP to msvcrt._except_handler3
    0040F4A5 |. 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
    0040F4AB |. 50 PUSH EAX
    0040F4AC |. 64:8925 000000>MOV DWORD PTR FS:[0],ESP
    0040F4B3 |. 83EC 68 SUB ESP,68
    0040F4B6 |. 53 PUSH EBX
    0040F4B7 |. 56 PUSH ESI
    0040F4B8 |. 57 PUSH EDI
    0040F4B9 |. 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
    0040F4BC |. 33DB XOR EBX,EBX
    0040F4BE |. 895D FC MOV DWORD PTR SS:[EBP-4],EBX
    0040F4C1 |. 6A 02 PUSH 2
    0040F4C3 |. FF15 30174100 CALL DWORD PTR DS:[411730] ; msvcrt.__set_app_type
    0040F4C9 |. 59 POP ECX
    0040F4CA |. 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
    0040F4D1 |. 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
    0040F4D8 |. FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode


    Now dump target with LordPE. If you scan it with PEiD you will found --> Microsoft Visual C++ 6.0. Our target is unpacked and stolen bytes are returned but we need to repir IAT.


    5. Fixing IAT:

    We will repair IAT using ImpREC but ASProtect is using one feature which make harder to do that job. I'm not pozitive sure if I'm right (somebody correct me if not) but ASProtect redirects some part of IAT jumps. I think that idea is to redirect jump in IAT table to some other location and there litle encrypt that jump, something like :



    XOR EAX,76543210
    jmp EAX


    Above is just example of code that ImpREC cannot read. When you unpack target, that jump in IAT that leads to this example now leads to nothing and our target is crushing. Tankfuly, there are peoples who know about this more than me and they have made some plugins for ImpREC to fix this. To repair IAT use the plugins in the attachment.

  6. #146
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : newbie

    3 ways to remove a nagscreen :
    - Kill the DialogBox call
    - Skip the Dialogbox procedure
    - Reduce the timervalue (to zero)


    1. Kill the DialogBox-Call

    In this tutorial, we will learn how to defeat nagscreens. We will see that there are different ways to do that.
    Our target is editor.exe.


    It has a nagscreen that stays there for 10 seconds :(
    Let's find out how we can kill it :

    The nagscreen, is in fact a DialogBox that is called before displaying the real program.
    To do that, the API DialogBoxParam is used. So just search for the API in your import table (eg in W32DASM).

    If you find more than one entry, and you don't know which one is the nagscreen, you can always check it in Soft Ice. (bpx DialogBoxParamA)

    We find it here :


    * Reference To: USER32.SendDlgItemMessageA, Ord:0000h


    00401411 E8408D0000 Call 0040A156
    00401416 6A00 push 00000000 ;Push init value
    00401418 682B124000 push 0040122B ;Push address of dialogbox-procedure
    0040141D 53 push ebx ;Push handle owner


    * Possible Reference to Dialog: DialogID_0384

    0040141E 6884030000 push 00000384 ;Push INT Resource
    00401423 6A00 push 00000000 ;Push dialogtemplate


    * Reference To: KERNEL32.GetModuleHandleA, Ord:0000h

    00401425 E8F28B0000 Call 0040A01C ;Call Modulehandle (to get the handle)
    0040142A 50 push eax ;Push handle DialogBox


    * Reference To: USER32.DialogBoxParamA, Ord:0000h

    0040142B E8C68C0000 Call 0040A0F6 ;Call the nagscreen
    00401430 E9A8000000 jmp 004014DD


    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 004013A2(C)

    00401435 837D1001 cmp dword ptr [ebp+10], 00000001
    00401439 0F849E0000 je 004014DD



    Now let's grab win32.hlp (you can find it in the Reference part of this site) and search for DialogBoxParam :
    int DialogBoxParam

    (
    HINSTANCE hInstance, // handle to application instance
    LPCTSTR lpTemplateName, // identifies dialog box template

    HWND hWndParent, // handle to owner window
    DLGPROC lpDialogFunc, // pointer to dialog box procedure
    LPARAM dwInitParam // initialization value
    );
    So if we reverse the code above to c++ syntaxis we have the following :

    DialogBoxParam (GetModuleHandle(NULL),NULL,MAKEINTRESOURCE(IDD_NA G), hwnd, Nag_Routine);


    All we have to do is NOP the entier code for the call out. Fire up your hexeditor, and change the offset A16 till offset A2F in 90's. (If you don't know how to find an offset or how to nop, read some other tutorials on this site (newbie rated))

    Run editor.exe, the nag is gone!


    Now what exactly did we do? We erased the code that calls the dialog. So, the code of the nag is still there, but it's not used anymore



    2. Skip the dialogbox-procedure

    Sometimes, the program checks if the nag is called. That way the above methode will fail. (eg PaintShopPro)
    Another easy way to bypass the nag, is let the program call the dialogbox, and then end the diaologbox immediatly.

    How? Let's find out :

    How can we find the dialogbox procedure?
    Easy, the address was used by DialogBoxParam! (address 401418 : push 0040122B)
    Ok, jump to 40122B code in W32Dasm.

    We are here :

    0040122B 55 push ebp
    0040122C 8BEC mov ebp, esp
    0040122E 8B450C mov eax, dword ptr [ebp+0C]
    00401231 2D10010000 sub eax, 00000110
    00401236 740A je 00401242 //WM_INITDIALOG ?
    00401238 48 dec eax
    00401239 7433 je 0040126E //WM_COMMAND ?
    0040123B 83E802 sub eax, 00000002
    0040123E 741A je 0040125A //WM_TIMER ?
    00401240 EB41 jmp 00401283


    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401236(C)

    00401242 6A00 push 00000000
    00401244 FF35E8B04000 push dword ptr [0040B0E8] //Push Timervalue.
    0040124A FF35ECB04000 push dword ptr [0040B0EC]
    00401250 FF7508 push [ebp+08]


    * Reference To: USER32.SetTimer, Ord:0000h


    00401253 E80A8F0000 Call 0040A162 //Call SetTimer
    00401258 EB2D jmp 00401287 //We can change this jump


    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 0040123E(C)


    0040125A 6A01 push 00000001
    0040125C 6A01 push 00000001
    0040125E FF7508 push [ebp+08]


    * Reference To: USER32.GetDlgItem, Ord:0000h


    00401261 E8AE8E0000 Call 0040A114
    00401266 50 push eax


    * Reference To: USER32.EnableWindow, Ord:0000h


    00401267 E8968E0000 Call 0040A102 //Enable the button
    0040126C EB19 jmp 00401287


    * Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401239(C)


    0040126E 668B5510 mov dx, word ptr [ebp+10]
    00401272 66FFCA dec dx
    00401275 7510 jne 00401287
    00401277 6A01 push 00000001
    00401279 FF7508 push [ebp+08]


    * Reference To: USER32.EndDialog, Ord:0000h


    0040127C E8878E0000 Call 0040A108
    00401281 EB04 jmp 00401287



    We notice the SetTimer API in the WM_INITDIALOG.

    If we want to exit this procedure, we have to call the EndDialog Function (40127c).
    We can easily change the jump at address 401258 'jmp 401287' into 'jmp 401277'
    That way, we start the dialogbox, and we close it before it is displayed
    This requires a patch of only one byte. change 'EB 2D' in 'EB 1D' (This changes the jump to jump 10h bytes less far).

    Run the program, no nag !

    3. Reduce the TimerValue

    As you can see in the above code, When the timer is set, we have some variable that are pushed on the stack for it. One of them (00401244) is the time (in millisec.)

    How do we know that? Try to figure this one out yourself! Use win32.hlp !
    push dword ptr [0040B0E8]

    To know what value is in there, we press the 'Data Hex'-button in W32Dasm and search for 40B0E8

    0040B080 2D 20 43 6F 70 79 72 69 - Copyri
    0040B088 67 68 74 20 31 39 39 36 ght 1996
    0040B090 20 42 6F 72 6C 61 6E 64 Borland
    0040B098 20 49 6E 74 6C 2E 00 00 Intl...
    0040B0A0 00 B0 40 00 48 B0 40 00 ..@.H.@.
    0040B0A8 48 B0 40 00 6C B0 40 00 H.@.l.@.
    0040B0B0 01 00 00 00 00 00 00 00 ........
    0040B0B8 E4 14 40 00 80 6A 40 00 ..@..j@.
    0040B0C0 AC 6A 40 00 00 00 00 00 .j@.....
    0040B0C8 08 C4 40 00 00 4C D2 40 ..@..L.@
    0040B0D0 00 E4 D2 40 00 00 01 00 ...@....
    0040B0D8 00 00 00 00 00 00 00 00 ........
    0040B0E0 00 00 00 00 00 00 00 90 ........
    0040B0E8 10 27 00 00 01 00 00 00 .'......


    Since Intel uses the 'Little Endian' way, our dword value is = 00 00 27 10 h = 10000 decimal
    10000 millsec = 10 sec This is the value we want to change !

    Find the value in your hexeditor, and change it too 00 00 00 00 = 0 sec. (or any other value you want)

    Now run the program, we see that the nag appears but the 10 second delay is gone...


    I showed you three ways to kill a nagscreen, but there are many more ways... To kill a nagscreen the 'right way' you should also remove the DialogBox in the Resource section. This reduces the file size But this is beyond the scope of this tutorial. If you are interested in more information about this, or if you have questions about this tutorial, post a comment or ask on the forum.


    If you manage to remove the NAG, you have a fast and small texteditor for free!

  7. #147
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : newbie

    Learn how to keygen using this easy crackme by tsh33p.
    Keygen source included.

    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*.....
    *Author:Tanatos
    |Date:28.12.2004
    *Target:KeygenMe by terr0r sh33p(tsh33p)
    |Dificulty:Easy
    *Tools:Olly Debug,C++ Compiler
    |Solution:Fishing/Keygen
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*.....

    Introduction:this is another easy keygenme...learn how to keygen it...

    The Essay:
    Run the keygenme enter any name you like and any serial...the name should be bigger than 3 chars...then press ok...remember the error you get...you don't get one...so we will do the oposite search for the message we should get if the combination would be a good one.
    Load the keygenme in olly go to search for->all referenced text strings and look for:"Yeah! You done it." that would be good...double click it...scroll a bit up:

    004010DC |. E8 DE000000 CALL <JMP.&USER32.SendMessageA> ; SendMessageA
    004010E1 |. 83F8 03 CMP EAX,3
    004010E4 |. 0F8C 93000000 JL keygenme.0040117D
    004010EA |. 8BD0 MOV EDX,EAX
    004010EC |. 33C9 XOR ECX,ECX

    ok set a breakpoint on 4010DC and then press F9.Enter the name Tanatos and serial 1234567890 then press ok.It breaks...good to know...after the line we breaked on comes the serial calculation part...so i'll just explain line by line from here:

    004010E1 |. 83F8 03 CMP EAX,3 <--Compares the name leght to 3
    004010E4 |. 0F8C 93000000 JL keygenme.0040117D <--If its smaller jumps else continues
    004010EA |. 8BD0 MOV EDX,EAX <--Moves the leght of the name to EDX
    004010EC |. 33C9 XOR ECX,ECX <--Clears ECX(makes it 0)
    004010EE |. 33DB XOR EBX,EBX <--Clears EBX
    004010F0 |> 0FB681 5020400>/MOVZX EAX,BYTE PTR DS:[ECX+402050] <--takes the value of the curent letter (the hex value)
    004010F7 |. 35 37130300 |XOR EAX,31337 <--EAX = EAX ^ 0x31337
    004010FC |. 05 EFBEADDE |ADD EAX,DEADBEEF <--EAX+=0xDEADBEEF
    00401101 |. 69C0 66060000 |IMUL EAX,EAX,666 <--EAX = EAX *0x666
    00401107 |. 2D B3BAAD1B |SUB EAX,1BADBAB3 <--EAX-=0x1BADBAB3
    0040110C |. C1E0 03 |SHL EAX,3 <--EAX = EAX << 3
    0040110F |. 35 0DD04DD3 |XOR EAX,D34DD00D <--EAX = EAX ^ 0xD34DD00D
    00401114 |. 03D8 |ADD EBX,EAX <--This is a variable that will store our serial
    00401116 |. 41 |INC ECX <--Rize the counter variable
    00401117 |. 3BD1 |CMP EDX,ECX <--Compares the counter to our name leght
    00401119 |.^75 D5 JNZ SHORT keygenme.004010F0 <--If its not equal it does the loop again.

    That would explain the code now for the keygen code...i coded it in c++ :

    #include <stdio.h>
    #include <conio.h>
    #include <string.h>
    char name[32];
    int n,i;
    long int serial=0,st=0;
    void main(void)
    {
    printf("Name:");scanf("%s",&name);
    n=strlen(name);
    if(n>2 && n<=32)
    {
    for(i=0;i<n;i++)
    {
    st=name[i];
    st=st ^ 0x31337;
    st+=0xDEADBEEF;
    st=st*0x666;
    st-=0x1BADBAB3;
    st=st << 3;
    st=st ^ 0xD34DD00D;
    serial=serial+st;
    st=0;
    }
    printf("Serial:%lXn",serial);
    }
    else
    {
    printf("Please enter a name with more than 2 chars and less than 32");
    }
    }

    Done explaining hope you understood something out of this...bye

  8. #148
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : beginner

    An alternative to writing a keygenerator is making an inline keygenerator. If the target can be serial fished, it can be inline keygenned. An example on a simple crackme.




    Intro
    In this tutorial we will crack Rith's crackme, and make an internal keygenerator for it.
    What is an internal keygenerator? Well, I call it a 'keygen for lazy reversers' The idea is to change the target program in a way that it displays the correct serial for you.


    Tools needed isassembler & Hexeditor

    Why make an internal keygen ?

    Well, There are (at least) 2 good reasons :

    - You don't have to reverse the algo completely
    - You won't make any mistakes decompiling the algo ; )



    Reversing the code

    004015BC E8E3020000 Call 004018A4
    004015C1 8B7E60 mov edi, dword ptr [esi+60] ;Name
    004015C4 8B5FF8 mov ebx, dword ptr [edi-08] ;Name length
    004015C7 83FB05 cmp ebx, 00000005 ;Name length

    004015CA 7C7E jl 0040164A ;Jump to badguy
    004015CC 8B4664 mov eax, dword ptr [esi+64] ;Serial
    004015CF 89442414 mov dword ptr [esp+14], eax
    004015D3 3958F8 cmp dword ptr [eax-08], ebx ;NameLength=Serial length?

    (*):004015D6 7572 jne 0040164A ;if not, jump to badguy

    004015D8 83FB14 cmp ebx, 00000014 ;Name length>14 ?
    004015DB 7F6D jg 0040164A ;if so, jump to badguy
    004015DD 33C9 xor ecx, ecx
    004015DF 85DB test ebx, ebx
    004015E1 7E54 jle 00401637
    004015E3 8B742410 mov esi, dword ptr [esp+10] ;Pi (20 digits)

    * Referenced by a (U)nconditional or (C)onditional Jump at Address:00401631(C)

    004015E7 8A040F mov al, byte ptr [edi+ecx] ;(ecx)th byte of Name

    004015EA 0FBE2C31 movsx ebp, byte ptr [ecx+esi] ;(ecx)th digit of Pi
    004015EE 0FBEC0 movsx eax, al
    004015F1 99 cdq
    004015F2 F7FD idiv ebp ;Divide name-byte by Pi-byte
    004015F4 8BC2 mov eax, edx ;Move remainder to eax
    004015F6 D1E0 shl eax, 1 ;remainder * 2
    ; The code that follows are just some checks and modification in order to get; a serial between certain ascii values. (So you can enter it in the proggy)
    004015F8 83F87B cmp eax, 0000007B
    004015FB 7E03 jle 00401600
    004015FD 83E81A sub eax, 0000001A
    * Referenced by a (U)nconditional or (C)onditional Jump at Address:004015FB(C)

    00401600 83F841 cmp eax, 00000041
    00401603 7D09 jge 0040160E
    00401605 BA82000000 mov edx, 00000082
    0040160A 2BD0 sub edx, eax
    0040160C 8BC2 mov eax, edx
    * Referenced by a (U)nconditional or (C)onditional Jump at Address:00401603(C)

    0040160E 83F85B cmp eax, 0000005B
    00401611 7E12 jle 00401625
    00401613 83F861 cmp eax, 00000061
    00401616 7D0D jge 00401625
    00401618 99 cdq
    00401619 BD0A000000 mov ebp, 0000000A
    0040161E F7FD idiv ebp
    00401620 83C230 add edx, 00000030
    00401623 8BC2 mov eax, edx
    * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: 0401611(C), :00401616(C)

    00401625 8B542414 mov edx, dword ptr [esp+14]
    (**):00401629 38040A cmp byte ptr [edx+ecx], al ;Serial byte=Our Serial byte?

    (**):0040162C 751C jne 0040164A ;if not, jump badguy
    0040162E 41 inc ecx ;point to next byte/digit
    0040162F 3BCB cmp ecx, ebx ;all bytes done?
    00401631 7CB4 jl 004015E7 ;if not, jump for next byte

    (***)00401633 8B742418 mov esi, dword ptr [esp+18]
    00401637 6A00 push 00000000
    * Possible StringData Ref from Data Obj ->"Congratulations!"

    (****)00401639 6834304000 push 00403034
    0040163E 6820304000 push 00403020
    00401643 8BCE mov ecx, esi
    00401645 E854020000 Call 0040189E ;Call MessageBoxA



    Patching the code

    To change this crackme in a keygen, we will need to patch the following :

    (**) mov byte ptr [edx+ecx], al
    HEX : 38 04 0A 75 1C -> 88 04 11 90 90
    (***) mov byte ptr [edx+ecx],00h
    HEX : 8B 74 24 18 -> C6 04 0A 00

    (****) push edx
    HEX : 68 20 30 40 00 -> 52 90 90 90 90

    (**) Saves each byte of the calculated serial to address in EDX+Counter
    (***) Add a NULL string terminator to the Serial

    (****) Display serial in the MessageBox

    When we change ** and *** and **** we have made a very basic, but working internal keygen.
    Now enter your name and a serial with the same length, hit the Register-button ... tada ... There is your valid serial!

    Have you noticed that we only had to change 14 bytes to change the proggy to an internal keygen? As I said, this is great for lazy programmers

    To complete this keygen, let's change the program so that we don't have to enter any serial.
    Nop the jump at (*), and we're done!
    HEX : 75 72 -> 90 90

    Name : Detten
    Serial : 6|j46j




    Regards,

  9. #149
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : intermediate

    Keygenning an application that uses a customized CRC32 algorithm to create a serial for a name + companyname.



    TARGET = FireGraphicXP v4.0
    TOOLS USED = Softice / IDA

    Intro
    We start the program, and a registerbox pops up The Check button is disabled though, so we can suspect the serial is checked 'on the fly' as we enter it in the editboxes.

    Fill the name, company, and serial field with your data, but leave last digit of serial open. Now, put a breakpoint on hmemcpy, or getwindowtext. And enter the final digit. It breaks!
    After a bit of tracing in softice, we notice that we land right in the checking routine that keeps looping as long that the registration dialogbox is on.
    This is the code we land in :


    The code
    .text:00414440 checkforenable proc near
    .text:00414440 push ebx
    .text:00414441 push edi
    .text:00414442 push 63h
    .text:00414444 push 1
    .text:00414446 push 1
    .text:00414448 lea edi, [esi+20h] ; Name
    .text:0041444B push 64h
    .text:0041444D push 43Ch
    .text:00414452 mov ebx, edi ; ebx = Name
    .text:00414454 mov eax, edi
    .text:00414456 call check_inputlength_routine ; Test length

    .text:0041445B test eax, eax
    .text:0041445D jz skiplengthchecks
    .text:00414463 push 63h
    .text:00414465 push 1
    .text:00414467 push 1
    .text:00414469 push 64h
    .text:0041446B lea ebx, [esi+84h] ; Company Name
    .text:00414471 push 43Dh
    .text:00414476 mov eax, edi ; Name
    .text:00414478 call check_inputlength_routine
    .text:0041447D test eax, eax
    .text:0041447F jz skiplengthchecks
    .text:00414485 push 4
    .text:00414487 push 1
    .text:00414489 push 1
    .text:0041448B push 5
    .text:0041448D lea ebx, [esi+0E8h] ; Serial part 1
    .text:00414493 push 43Eh
    .text:00414498 mov eax, edi
    .text:0041449A call check_inputlength_routine
    .text:0041449F test eax, eax
    .text:004144A1 jz skiplengthchecks
    .text:004144A7 push 4
    .text:004144A9 push 1
    .text:004144AB push 1
    .text:004144AD push 5
    .text:004144AF lea ebx, [esi+0EDh] ; serial part 2
    .text:004144B5 push 4ADh
    .text:004144BA mov eax, edi
    .text:004144BC call check_inputlength_routine
    .text:004144C1 test eax, eax
    .text:004144C3 jz short skiplengthchecks
    .text:004144C5 push 4
    .text:004144C7 push 1
    .text:004144C9 push 1
    .text:004144CB push 5
    .text:004144CD lea ebx, [esi+0F2h] ; serial part 3
    .text:004144D3 push 4AEh
    .text:004144D8 mov eax, edi
    .text:004144DA call check_inputlength_routine
    .text:004144DF test eax, eax
    .text:004144E1 jz short skiplengthchecks
    .text:004144E3 push 4
    .text:004144E5 push 1
    .text:004144E7 push 1
    .text:004144E9 push 5
    .text:004144EB lea ebx, [esi+0F7h] ; serial part 4

    .text:004144F1 push 4AFh
    .text:004144F6 mov eax, edi
    .text:004144F8 call check_inputlength_routine
    .text:004144FD test eax, eax
    .text:004144FF jz short skiplengthchecks
    .text:00414501 push 4
    .text:00414503 push 1
    .text:00414505 push 1
    .text:00414507 push 5
    .text:00414509 lea ebx, [esi+0FCh] ; serial part 5
    .text:0041450F push 4B0h
    .text:00414514 mov eax, edi
    .text:00414516 call check_inputlength_routine
    .text:0041451B test eax, eax
    .text:0041451D jz short skiplengthchecks
    .text:0041451F push 4
    .text:00414521 push 1
    .text:00414523 push 1
    .text:00414525 push 5
    .text:00414527 lea ebx, [esi+101h] ; serial part 6
    .text:0041452D push 4B1h
    .text:00414532 mov eax, edi
    .text:00414534 call check_inputlength_routine
    .text:00414539
    .text:00414539 skiplengthchecks: ; CODE XREF: checkforenable+1Dj
    .text:00414539 ; checkforenable+3Fj ...
    .text:00414539 lea eax, [esi+101h] ; serial part 6
    .text:0041453F push eax
    .text:00414540 lea ecx, [esi+0FCh] ; serial part 5

    .text:00414546 push ecx
    .text:00414547 lea edx, [esi+0F7h] ; serial part 4
    .text:0041454D push edx
    .text:0041454E lea eax, [esi+0F2h] ; serial part 3
    .text:00414554 push eax
    .text:00414555 lea ecx, [esi+0EDh] ; serial part 2
    .text:0041455B push ecx
    .text:0041455C lea edx, [esi+0E8h] ; serial part 1
    .text:00414562 push edx
    .text:00414563 lea ecx, [esi+84h] ; company name
    .text:00414569 push offset byte_0_525B08
    .text:0041456E mov edx, edi ; edx = Name

    .text:00414570 call check_serial ; eax == 1 if serial OK
    .text:00414575 neg al
    .text:00414577 push 1 ; nIDDlgItem
    .text:00414579 sbb eax, eax
    .text:0041457B neg eax
    .text:0041457D mov edi, eax
    .text:0041457F mov eax, [esi+4]
    .text:00414582 push eax ; hDlg
    .text:00414583 call ds:GetDlgItem
    .text:00414589 push edi ; bEnable (depends on check_serial call))
    .text:0041458A push eax ; hWnd
    .text:0041458B call ds:EnableWindow ; Enable/disable the register button

    .text:00414591 pop edi
    .text:00414592 xor eax, eax
    .text:00414594 pop ebx
    .text:00414595 retn 10h
    .text:00414595 checkforenable endp

    Basically it checks if the serial is complete, and then the call check_serial is executed. If we trace over it, we see that it returns 0 for our fake serial. It is pretty clear that the serial routine is in that routine

    Ok, let's take a look inside that routine :


    .text:00446D70 sub esp, 120h
    .text:00446D76 mov eax, dword_0_5232D0
    .text:00446D7B xor eax, [esp+120h]
    .text:00446D82 push esi
    .text:00446D83 push edi
    .text:00446D84 mov esi, edx
    .text:00446D86 push offset unk_0_5188AB ; lpString2
    .text:00446D8B push esi ; lpString1
    .text:00446D8C mov [esp+130h+var_4], eax
    .text:00446D93 mov edi, ecx
    .text:00446D95 call ds:lstrcmpA
    .text:00446D9B test eax, eax
    .text:00446D9D jnz short go_on
    .text:00446D9F
    .text:00446D9F serial_not_complete:
    .text:00446D9F
    .text:00446D9F xor al, al
    .text:00446DA1 jmp skipsome
    .text:00446DA6 ; ---------------------------------------------------------------------------
    .text:00446DA6
    .text:00446DA6 go_on: ; CODE XREF: check_serial+2Dj
    .text:00446DA6 mov eax, [esp+128h+arg_18] ; serial part 6
    .text:00446DAD mov ecx, [esp+128h+arg_14] ; serial part 5

    .text:00446DB4 mov edx, [esp+128h+arg_10] ; serial part 4
    .text:00446DBB push eax
    .text:00446DBC mov eax, [esp+12Ch+arg_C] ; serial part 3
    .text:00446DC3 push ecx
    .text:00446DC4 mov ecx, [esp+130h+arg_8] ; serial part 2
    .text:00446DCB push edx
    .text:00446DCC mov edx, [esp+134h+arg_4] ; serial part 1
    .text:00446DD3 push eax
    .text:00446DD4 push ecx
    .text:00446DD5 push edx
    .text:00446DD6 lea eax, [esp+140h+sz]
    .text:00446DDA push offset aSSSSSS ; "%s%s%s%s%s%s"
    .text:00446DDF push eax
    .text:00446DE0 call paste_routine ; paste serial together


    What happened so far? All the serial parts are pasted together as a string. There is no need to trace that routine, it just pastes together whatever is pushed right before it. (Nobody stops you from tracing in that paste routine though :p)
    .text:00446DE5 add esp, 20h
    .text:00446DE8 lea ecx, [esp+128h+sz] ; serial
    .text:00446DEC push ecx ; lpString (serial)
    .text:00446DED call ds:lstrlenA
    .text:00446DF3 cmp eax, 18h ; serial length == 24 ?
    .text:00446DF6 jnz short serial_not_complete
    .text:00446DF8 lea edx, [esp+128h+sz] ; edx = Serial
    .text:00446DFC push edx ; lpsz

    .text:00446DFD call ds:CharUpperA
    .text:00446E03 test edi, edi ; company name
    .text:00446E05 mov eax, edi
    .text:00446E07 jnz short start_hashing
    .text:00446E09 mov eax, offset unk_0_5188AB

    the string built from the serialparts is converted to uppercase
    .text:00446E0E
    .text:00446E0E start_hashing: ; CODE XREF: check_serial+97j
    .text:00446E0E push 241D30Bh ; 0037868299
    .text:00446E13 push eax ; company name

    .text:00446E14 push esi ; Name
    .text:00446E15 lea eax, [esp+134h+var_104]
    .text:00446E19 push offset aSSD ; "%s%s%d"
    .text:00446E1E push eax
    .text:00446E1F call paste_routine ; paste name + company name + digits
    .text:00446E24 lea eax, [esp+13Ch+var_104] ; eax = paste result userdata
    .text:00446E28 call Create_hash_table

    Here the paste routine is called again, this time with a fixed number, company name, and name they are pasted all after eachother. From now on I call this string 'userdata'
    Then this string is used as parameter in the nex call, in that call a CRC32 hashing table is built, and the userdata is hashed with that table. We will take a look at this call in a minute.
    You can also notice the "%s%s%d" string, which is pretty simular to the strings u need for wsprintf API. Check it out in your win32api.hlp if u like
    .text:00446E2D push eax ; CRC32 hash 1
    .text:00446E2E lea ecx, [esp+140h+var_104]
    .text:00446E32 push offset a08x ; "%08X"
    .text:00446E37 push ecx ; userdata
    .text:00446E38 call paste_routine ; overwrite userdata with CRC32 Hash 1 (in ascii)
    .text:00446E3D add esp, 20h
    .text:00446E40 mov ecx, 2 ; counter = 2

    .text:00446E45 lea edi, [esp+128h+var_104] ; userdata
    .text:00446E49 lea esi, [esp+128h+sz] ; serial part 1 & 2
    .text:00446E4D xor edx, edx
    .text:00446E4F repe cmpsd ; CRC32 hash 1 == serial digit 1 - 8 (2 dwords)
    .text:00446E51 jnz serial_not_complete

    next the hash created in the create_hash_table routine( which is returned in eax), is pushed and converted to an ascii string. this means we have converted one DWORD to an 8 digits long hex number in ascii, so 8 bytes long.
    Next there is a compare done of 2 DWORDS (8bytes) between serialpart1&2 and our hash (created from the userdata).
    .text:00446E57 mov eax, [esp+128h+var_118] ; lpSerial part 3
    .text:00446E5B mov ecx, [esp+128h+var_114] ; lpSerial part 4
    .text:00446E5F mov [esp+128h+var_104], eax ; overwrite dword CRC32 hash 1 with serial part 3
    .text:00446E63 lea eax, [esp+128h+var_104]
    .text:00446E67 mov [esp+128h+var_100], ecx ; overwrite 2nd dword CRC32 hash 1 with serial part 4
    .text:00446E6B call Create_hash_table
    .text:00446E70 push eax ; CRC32 Hash 2
    .text:00446E71 lea edx, [esp+12Ch+var_104] ; edx = userdata

    .text:00446E75 push offset a08x ; "%08X"
    .text:00446E7A push edx
    .text:00446E7B call paste_routine ; overwrite userdata with CRC32 Hash 2 (in ascii)
    .text:00446E80 add esp, 0Ch
    .text:00446E83 xor eax, eax
    .text:00446E85 mov ecx, 2
    .text:00446E8A lea edi, [esp+128h+var_104] ; userdata
    .text:00446E8E lea esi, [esp+128h+var_110] ; serial part 6 & 7
    .text:00446E92 repe cmpsd ; CRC32 hash 2 == serial digit 16 - 24 (2 dwords)
    .text:00446E94 setz al


    Same thing happens here, only now the 3rd and 4th part of the serial are used to create a hash. and it is checked with part 5 & 6 of our serial. So it seems part 3,4,5 and 6 don't depend on our data...kinda strange :/ When this check is also correct al is set to 1
    .text:00446E97
    .text:00446E97 skipsome: ; CODE XREF: check_serial+31j
    .text:00446E97 mov ecx, [esp+128h+var_4]
    .text:00446E9E xor ecx, [esp+128h]
    .text:00446EA5 pop edi ; Name
    .text:00446EA6 pop esi
    .text:00446EA7 call sub_0_4D6F4A ; internal appcheck
    .text:00446EAC add esp, 120h
    .text:00446EB2 retn 1Ch
    .text:00446EB2 check_serial endp


    So this function calculates serial part 1 & 2 out of our userdata, and calculate serial part 5 & 6 out of serial part 3 & 4. It returns 1 if serial was ok, else 0.

    Let's have a closer look at the Create_hash_table routine :

    .text:004036F0 Create_hash_table proc near
    .text:004036F0
    .text:004036F0
    .text:004036F0 var_400 = dword ptr -400h
    .text:004036F0
    .text:004036F0 sub esp, 400h
    .text:004036F6 push ebx
    .text:004036F7 push esi
    .text:004036F8 push edi
    .text:004036F9 mov edi, eax ; userdata
    .text:004036FB xor ebx, ebx
    .text:004036FD lea ecx, [ecx+0]
    .text:00403700
    .text:00403700 hash_table_loop: ; CODE XREF: Create_hash_table+137j
    .text:00403700 xor ecx, ecx
    .text:00403702 test bl, 1
    .text:00403705 jz short loc_0_40370C
    .text:00403707 mov ecx, 80h
    .text:0040370C
    .text:0040370C loc_0_40370C: ; CODE XREF: Create_hash_table+15j
    .text:0040370C mov eax, ebx
    .text:0040370E shr eax, 1
    .text:00403710 test al, 1
    .text:00403712 jz short loc_0_403717
    .text:00403714 or ecx, 40h
    .text:00403717
    .text:00403717 loc_0_403717: ; CODE XREF: Create_hash_table+22j
    .text:00403717 shr eax, 1
    .text:00403719 test al, 1
    .text:0040371B jz short loc_0_403720
    .text:0040371D or ecx, 20h
    .text:00403720
    .text:00403720 loc_0_403720: ; CODE XREF: Create_hash_table+2Bj
    .text:00403720 shr eax, 1
    .text:00403722 test al, 1
    .text:00403724 jz short loc_0_403729
    .text:00403726 or ecx, 10h
    .text:00403729
    .text:00403729 loc_0_403729: ; CODE XREF: Create_hash_table+34j
    .text:00403729 shr eax, 1
    .text:0040372B test al, 1
    .text:0040372D jz short loc_0_403732
    .text:0040372F or ecx, 8
    .text:00403732
    .text:00403732 loc_0_403732: ; CODE XREF: Create_hash_table+3Dj
    .text:00403732 shr eax, 1
    .text:00403734 test al, 1
    .text:00403736 jz short loc_0_40373B
    .text:00403738 or ecx, 4
    .text:0040373B
    .text:0040373B loc_0_40373B: ; CODE XREF: Create_hash_table+46j
    .text:0040373B shr eax, 1
    .text:0040373D test al, 1
    .text:0040373F jz short loc_0_403744
    .text:00403741 or ecx, 2
    .text:00403744
    .text:00403744 loc_0_403744: ; CODE XREF: Create_hash_table+4Fj
    .text:00403744 test al, 2
    .text:00403746 jz short loc_0_40374B
    .text:00403748 or ecx, 1
    .text:0040374B
    .text:0040374B loc_0_40374B: ; CODE XREF: Create_hash_table+56j
    .text:0040374B shl ecx, 18h
    .text:0040374E mov eax, ecx
    .text:00403750 and eax, 80000000h
    .text:00403755 neg eax
    .text:00403757 sbb eax, eax
    .text:00403759 add ecx, ecx
    .text:0040375B and eax, 4C11DB7h
    .text:00403760 xor eax, ecx
    .text:00403762 mov ecx, eax
    .text:00403764 and ecx, 80000000h
    .text:0040376A neg ecx
    .text:0040376C sbb ecx, ecx
    .text:0040376E and ecx, 4C11DB7h
    .text:00403774 lea edx, [eax+eax]
    .text:00403777 xor ecx, edx
    .text:00403779 mov eax, ecx
    .text:0040377B and eax, 80000000h
    .text:00403780 neg eax
    .text:00403782 sbb eax, eax
    .text:00403784 add ecx, ecx
    .text:00403786 and eax, 4C11DB7h
    .text:0040378B xor eax, ecx
    .text:0040378D mov ecx, eax
    .text:0040378F and ecx, 80000000h
    .text:00403795 neg ecx
    .text:00403797 sbb ecx, ecx
    .text:00403799 and ecx, 4C11DB7h
    .text:0040379F lea edx, [eax+eax]
    .text:004037A2 xor ecx, edx
    .text:004037A4 mov eax, ecx
    .text:004037A6 and eax, 80000000h
    .text:004037AB neg eax
    .text:004037AD sbb eax, eax
    .text:004037AF add ecx, ecx
    .text:004037B1 and eax, 4C11DB7h
    .text:004037B6 xor eax, ecx
    .text:004037B8 mov ecx, eax
    .text:004037BA and ecx, 80000000h
    .text:004037C0 neg ecx
    .text:004037C2 sbb ecx, ecx
    .text:004037C4 and ecx, 4C11DB7h
    .text:004037CA lea edx, [eax+eax]
    .text:004037CD xor ecx, edx
    .text:004037CF mov eax, ecx
    .text:004037D1 and eax, 80000000h
    .text:004037D6 neg eax
    .text:004037D8 sbb eax, eax
    .text:004037DA add ecx, ecx
    .text:004037DC and eax, 4C11DB7h
    .text:004037E1 xor eax, ecx
    .text:004037E3 mov ecx, eax
    .text:004037E5 and ecx, 80000000h
    .text:004037EB neg ecx
    .text:004037ED sbb ecx, ecx
    .text:004037EF and ecx, 4C11DB7h
    .text:004037F5 lea edx, [eax+eax]
    .text:004037F8 xor ecx, edx
    .text:004037FA mov [esp+ebx*4+40Ch+var_400], ecx
    .text:004037FE mov eax, ecx
    .text:00403800 xor edx, edx
    .text:00403802 mov ecx, 1Fh
    .text:00403807
    .text:00403807 loc_0_403807: ; CODE XREF: Create_hash_table+12Aj
    .text:00403807 test al, 1
    .text:00403809 jz short loc_0_403814
    .text:0040380B mov esi, 1
    .text:00403810 shl esi, cl
    .text:00403812 or edx, esi
    .text:00403814
    .text:00403814 loc_0_403814: ; CODE XREF: Create_hash_table+119j
    .text:00403814 shr eax, 1
    .text:00403816 dec ecx
    .text:00403817 cmp ecx, 0FFFFFFFFh
    .text:0040381A jg short loc_0_403807
    .text:0040381C mov [esp+ebx*4+40Ch+var_400], edx ; store in hash table
    .text:00403820 inc ebx
    .text:00403821 cmp ebx, 0FFh ; hash table completed ?
    .text:00403827 jle hash_table_loop

    All the above code is only to create a hash table. We will need to copy this piece of code for our keygen, or you can just dump the table. Both ways are good, because the table does not depend on variables.
    .text:0040382D mov ecx, edi ; userdata

    .text:0040382F or eax, 0FFFFFFFFh
    .text:00403832 lea esi, [ecx+1] ; p(userdata)+1
    .text:00403835
    .text:00403835 loop_data: ; CODE XREF: Create_hash_table+14Aj
    .text:00403835 mov dl, [ecx]
    .text:00403837 inc ecx
    .text:00403838 test dl, dl ; char (userdata) == 0 ?
    .text:0040383A jnz short loop_data
    .text:0040383C sub ecx, esi ; ecx = userdata length
    .text:0040383E mov edx, edi ; edx = userdata
    .text:00403840 jz short loc_0_40385C ;
    .text:00403840
    .text:00403840; **userdata = name + company name + 37868299
    .text:00403842
    .text:00403842 process_more_data: ; CODE XREF: .text:0040385Aj
    .text:00403842 movzx edi, byte ptr [edx] ; edi = (char) userdata

    .text:00403845 mov esi, eax ; esi = hash
    .text:00403847 and esi, 0FFh ; esi = char (hash)
    .text:0040384D xor esi, edi ; esi = char (esi) XOR char (userdata)
    .text:0040384F mov esi, [esp+esi*4+40Ch+var_400] ; esi = hashtable [esi]
    .text:00403853 shr eax, 8 ; eax = eax/256
    .text:00403853 Create_hash_table endp
    .text:00403853
    .text:00403856 xor eax, esi ; hash = hash XOR hashtable dword

    .text:00403858 inc edx ; pointer ++
    .text:00403859 dec ecx ; counter --
    .text:0040385A jnz short process_more_data
    .text:0040385C
    .text:0040385C loc_0_40385C: ; CODE XREF: Create_hash_table+150j
    .text:0040385C pop edi
    .text:0040385D pop esi
    .text:0040385E not eax ; NOT hash
    .text:00403860 pop ebx
    .text:00403861 add esp, 400h
    .text:00403867 retn
    .text:00403867 ; ---------------------------------------------------------------------------
    .text:00403868 align 10h
    .text:00403870 mov eax, offset unk_0_524F28
    .text:00403875 retn
    .text:00403875 ; ---------------------------------------------------------------------------



    First Conclusion
    If you traced the above code with softice, then you are able to construct a valid serial for your data. The first 2 parts of the serial are equal to the first hash(at VA 00446E2Dh in eax) and the 5th and 6th part are equal to the hash of the 3rd and 4th part. (at VA 00446E70h in eax). 3rd and 4th part of serial can be chosen at random... a bit weird... Ok, enter the serials, the buttons gets enabled it works!!! )) Quit the app completly, and restart it, baammm the registration box is back :/ It seems the 3rd and 4th serial part are probably not so random afterall...
    The attack part 2

    There must be a second larger check in the startup of the program...
    How can we reach it? we can use the softice symbol loader, and bpx on the pasteroutine or even the hashing table routine. (This won't work if they used a copy of those routines for the startup check).
    Another way is to fire up regmon, and check where the serial key is stored in the registry. Even more simple is just to do a search for a part of ur serial in the registry (using regedit). and then bpx that regkey. Anyway u do it, u should land in the following code somewhere :
    .text:00446EC0 sub esp, 204h
    .text:00446EC6 mov eax, dword_0_5232D0
    .text:00446ECB xor eax, [esp+204h]
    .text:00446ED2 push esi
    .text:00446ED3 push edi
    .text:00446ED4 mov esi, edx
    .text:00446ED6 push offset unk_0_5188AB ; lpString2
    .text:00446EDB push esi ; lpString1
    .text:00446EDC mov [esp+214h+var_4], eax
    .text:00446EE3 mov edi, ecx
    .text:00446EE5 call ds:lstrcmpA
    .text:00446EEB test eax, eax
    .text:00446EED jnz short loc_0_446EF6
    .text:00446EEF
    .text:00446EEF loc_0_446EEF: ; CODE XREF: sub_0_446EC0+40j
    .text:00446EEF ; sub_0_446EC0+8Ej ...
    .text:00446EEF xor al, al
    .text:00446EF1 jmp loc_0_446FD0
    .text:00446EF6 ; ---------------------------------------------------------------------------
    .text:00446EF6
    .text:00446EF6 loc_0_446EF6: ; CODE XREF: sub_0_446EC0+2Dj
    .text:00446EF6 push ebx ; lpString
    .text:00446EF7 call ds:lstrlenA
    .text:00446EFD cmp eax, 18h
    .text:00446F00 jnz short loc_0_446EEF
    .text:00446F02 test edi, edi
    .text:00446F04 mov eax, edi
    .text:00446F06 jnz short loc_0_446F0D
    .text:00446F08 mov eax, offset unk_0_5188AB
    .text:00446F0D
    .text:00446F0D loc_0_446F0D: ; CODE XREF: sub_0_446EC0+46j
    .text:00446F0D push 241D30Bh ; fixed digits

    .text:00446F12 push eax
    .text:00446F13 push esi
    .text:00446F14 lea eax, [esp+218h+var_204]
    .text:00446F18 push offset aSSD ; "%s%s%d"
    .text:00446F1D push eax
    .text:00446F1E call paste_routine
    .text:00446F23 lea eax, [esp+220h+var_204]
    .text:00446F27 call Create_hash_table ; create hash 1
    .text:00446F2C push eax
    .text:00446F2D lea ecx, [esp+224h+var_204]
    .text:00446F31 push offset a08x ; "%08X"
    .text:00446F36 push ecx
    .text:00446F37 call paste_routine ; userstring = hash1
    .text:00446F3C add esp, 20h
    .text:00446F3F mov ecx, 2
    .text:00446F44 lea edi, [esp+20Ch+var_204]
    .text:00446F48 mov esi, ebx
    .text:00446F4A xor edx, edx
    .text:00446F4C repe cmpsd ; check serial part 1 & 2 == hash 1
    .text:00446F4E jnz short loc_0_446EEF
    .text:00446F50 push 3EE24C3h ; second fixed digits
    .text:00446F55 lea eax, [esp+210h+var_204]
    .text:00446F59 push eax
    .text:00446F5A lea ecx, [esp+214h+var_104]
    .text:00446F61 push offset aS08x ; "%s%08X"

    .text:00446F66 push ecx
    .text:00446F67 call paste_routine
    .text:00446F6C lea eax, [esp+21Ch+var_104]
    .text:00446F73 call Create_hash_table
    .text:00446F78 push eax ; hash 2
    .text:00446F79 lea edx, [esp+220h+var_204]
    .text:00446F7D push offset a08x ; "%08X"
    .text:00446F82 push edx
    .text:00446F83 call paste_routine
    .text:00446F88 add esp, 1Ch
    .text:00446F8B mov ecx, 2
    .text:00446F90 lea edi, [esp+20Ch+var_204]
    .text:00446F94 lea esi, [ebx+8]
    .text:00446F97 xor eax, eax
    .text:00446F99 repe cmpsd ; check serial part 3 & 4 == hash 2
    .text:00446F9B jnz loc_0_446EEF
    .text:00446FA1 lea eax, [esp+20Ch+var_204]
    .text:00446FA5 call Create_hash_table
    .text:00446FAA push eax
    .text:00446FAB lea ecx, [esp+210h+var_204]
    .text:00446FAF push offset a08x ; "%08X"
    .text:00446FB4 push ecx
    .text:00446FB5 call paste_routine
    .text:00446FBA add esp, 0Ch
    .text:00446FBD mov ecx, 2
    .text:00446FC2 lea edi, [esp+20Ch+var_204]
    .text:00446FC6 lea esi, [ebx+10h]
    .text:00446FC9 xor edx, edx
    .text:00446FCB repe cmpsd
    .text:00446FCD setz al
    .text:00446FD0
    .text:00446FD0 loc_0_446FD0: ; CODE XREF: sub_0_446EC0+31j
    .text:00446FD0 mov ecx, [esp+20Ch+var_4]
    .text:00446FD7 xor ecx, [esp+20Ch]
    .text:00446FDE pop edi
    .text:00446FDF pop esi
    .text:00446FE0 call sub_0_4D6F4A
    .text:00446FE5 add esp, 204h
    .text:00446FEB retn 4
    .text:00446FEB sub_0_446EC0 endp


    I have commented this routine less than the other one, but you can see it is very simular. The only difference, is that the 3rd and 4th part of the serial are calculated by adding some fixed numbers to the first hash, and rehash that. This gives us a valid 3rd and 4th part of the serial, and the 5th and 6th part remain the same, just a hashing of the 2nd calculated hash.
    heh, ok maybe this is a bit confusing, let's try to put this in a scheme :
    name+company+37868299
    |
    |
    create hash 1
    |
    hash1 == serial part 1 & 2
    |
    |
    hash1+03ee24c3
    |
    |
    create hash 2
    |
    hash2 == serial part 3 & 4
    |
    |
    hash 2
    |
    |
    create hash 3
    |
    hash3 == serial part 5 & 6

    Ok, now we now that, all we have to do is rip the create_hash_table routine (which includes the data hashing) and use it 3 times in our keygen. That's all
    Our keygen routine
    This routine is ripped and slightly adjusted. It's the Create_hash_table routine.
    ;-------------------------------------------------------------------+
    ; key-generation procedure |
    ;-------------------------------------------------------------------+
    GenerateKey PROC anameWORD, aserialWORD
    ;--------------------------------------------------------------------
    ; ALGO RECONSTRUCTION (CRC32 TABLE + HASHING)
    ;--------------------------------------------------------------------
    mov edi, aname
    xor ebx, ebx
    lea ecx, [ecx+0]
    hash_table_loop:
    xor ecx, ecx
    test bl, 1
    jz jump1
    mov ecx, 80h

    jump1:
    mov eax, ebx
    shr eax, 1
    test al, 1
    jz jump2
    or ecx, 40h

    jump2:
    shr eax, 1
    test al, 1
    jz jump3
    or ecx, 20h

    jump3:
    shr eax, 1
    test al, 1
    jz jump4
    or ecx, 10h

    jump4:
    shr eax, 1
    test al, 1
    jz jump5
    or ecx, 8

    jump5:
    shr eax, 1
    test al, 1
    jz jump6
    or ecx, 4

    jump6:
    shr eax, 1
    test al, 1
    jz jump7
    or ecx, 2

    jump7:
    test al, 2
    jz jump8
    or ecx, 1

    jump8:
    shl ecx, 18h
    mov eax, ecx
    and eax, 80000000h
    neg eax
    sbb eax, eax
    add ecx, ecx
    and eax, 4C11DB7h
    xor eax, ecx
    mov ecx, eax
    and ecx, 80000000h
    neg ecx
    sbb ecx, ecx
    and ecx, 4C11DB7h
    lea edx, [eax+eax]
    xor ecx, edx
    mov eax, ecx
    and eax, 80000000h
    neg eax
    sbb eax, eax
    add ecx, ecx
    and eax, 4C11DB7h
    xor eax, ecx
    mov ecx, eax
    and ecx, 80000000h
    neg ecx
    sbb ecx, ecx
    and ecx, 4C11DB7h
    lea edx, [eax+eax]
    xor ecx, edx
    mov eax, ecx
    and eax, 80000000h
    neg eax
    sbb eax, eax
    add ecx, ecx
    and eax, 4C11DB7h
    xor eax, ecx
    mov ecx, eax
    and ecx, 80000000h
    neg ecx
    sbb ecx, ecx
    and ecx, 4C11DB7h
    lea edx, [eax+eax]
    xor ecx, edx
    mov eax, ecx
    and eax, 80000000h
    neg eax
    sbb eax, eax
    add ecx, ecx
    and eax, 4C11DB7h
    xor eax, ecx
    mov ecx, eax
    and ecx, 80000000h
    neg ecx
    sbb ecx, ecx
    and ecx, 4C11DB7h
    lea edx, [eax+eax]
    xor ecx, edx
    mov [ebx*4+offset hashtable], ecx
    mov eax, ecx
    xor edx, edx
    mov ecx, 1Fh

    jump9:
    test al, 1
    jz jump10
    mov esi, 1
    shl esi, cl
    or edx, esi

    jump10:
    shr eax, 1
    dec ecx
    cmp ecx, 0FFFFFFFFh
    jg jump9
    mov [ebx*4+offset hashtable], edx ; store in hash table
    inc ebx
    cmp ebx, 0FFh ; hash table completed ?
    jle hash_table_loop
    mov ecx, edi ; name + company name + digits
    or eax, 0FFFFFFFFh
    lea esi, [ecx+1] ; lp(name + company name + digits)+1

    loop_data:
    mov dl, [ecx]
    inc ecx
    test dl, dl ; char (name + company name + digits) == 0 ?
    jnz short loop_data
    sub ecx, esi ; ecx = name + company name + digits length
    mov edx, edi ; edx = name + company name + digits
    jz jump11

    process_more_data:
    movzx edi, byte ptr [edx] ; edi = (char) userdata
    mov esi, eax ; esi = hash
    and esi, 0FFh ; esi = char (hash)
    xor esi, edi ; esi = char (esi) XOR char (userdata)
    mov esi, [esi*4+offset hashtable] ; esi = hashtable [esi]
    shr eax, 8 ; eax = eax/256
    xor eax, esi ; hash = hash XOR hashtable dword
    inc edx ; pointer ++
    dec ecx ; counter --
    jnz short process_more_data

    jump11:
    not eax ; NOT hash

    ;--------------------------------------------------------------------------
    ; END OF CRC32 HASHING ALGO ---> HASH in EAX
    ;--------------------------------------------------------------------------

    invoke wsprintf, aserial, addr ToHexa, eax
    ret
    GenerateKey ENDP

    When we have made this, all we need is to reconstruct the little scheme above :

    pushad
    invoke lstrcat, ADDR thename, ADDR thecompany
    invoke lstrcat, ADDR thename, ADDR thedigits
    invoke GenerateKey, addr thename, addr theserial ; pointer to name, pointer to buffer
    mov esi, offset theserial
    mov edi, offset buffer
    xor ecx, ecx
    .WHILE ecx
    That's all we need, now write a nice GUI for it, and you have a working keygen
    Check out the source, it's pretty straightforward if u understand what happens in
    the application.


    Outtro
    This algo is based on a CRC32 hashing table, and as soon as u r able to reconstruct this table in your keygen, it is pretty easy to keygen it. In this case the same table was used 3 times.

  10. #150
    پروفشنال Morteza_SOS's Avatar
    تاريخ عضويت
    Apr 2006
    پست ها
    577

    پيش فرض

    Level : newbie

    Warcraft 3 seems to use the SetSecurityInfo() api to prevent us to obtain its process handle.

    Okay, I debugged and I played around Warcraft 3 for some time.

    Trying myself to understand this protection scheme, I bring
    the proof here how to defeat this protection.

    SetSecurityInfo() blocks us from accessing it's memory.
    Let me explain SetSecurityInfo() to you:

    ------------------------------------------
    The SetSecurityInfo function sets specified security information
    in the security descriptor of a specified object.
    The caller identifies the object by a handle.

    DWORD SetSecurityInfo(

    HANDLE handle, // handle to the object
    SE_OBJECT_TYPE ObjectType, // type of object
    SECURITY_INFORMATION SecurityInfo, // type of security information to set
    PSID psidOwner, // pointer to the new owner SID
    PSID psidGroup, // pointer to the new primary group SID
    PACL pDacl, // pointer to the new DACL
    PACL pSacl // pointer to the new SACL
    );
    ------------------------------------------

    Warcraft3 passes params to this function to block us from getting a valid process handle, or from altering its memory.
    We want this if we want to fiddle with it

    But where is Warcraft doing this? Simply, debug it and set a breakpoint on SetSecurityInfo.

    After debugging as results we get:

    ------------ game.dll snippet ------------
    .6F009962: 53 push ebx ;0
    .6F009963: 8D95E0FDFFFF lea edx,[ebp][-00000220]
    .6F009969: 52 push edx ; 0012FB4Ch
    .6F00996A: 53 push ebx ; 0
    .6F00996B: 53 push ebx ; 0
    .6F00996C: 6804000080 push 080000004 ; DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION
    .6F009971: 6A06 push 006 ; 6
    .6F009973: 50 push eax ; FFFFFFFFh
    .6F009974: FF55EC call d,[ebp][-14] ; SetSecurityInfo()
    ------------ game.dll snippet ------------

    From here, we have the SetSecurityInfo() API call with params:
    SetSecurityInfo(0xFFFFFFFF, 6, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, 0, 0, 0x0012FB4C, 0);

    We just have to pass NULL as a third parameter.

    Passing NULL as SecurityInfo param, makes sure the api doesn't do anything that can block us. To be exact by making the param NULL, the api doesn't set the specified security information in the discretionary access control list (DACL) of the specified objects security descriptor.

    We patch 6804000080 at address 6F00996C to 6800000000.

    Just edit game.dll with ANY hex editor and patch it.
    I accessed Warcraft 3 memory without any problems :-)

    Good luck working on your map hacks, namespoof hacks, etc.

Thread Information

Users Browsing this Thread

هم اکنون 1 کاربر در حال مشاهده این تاپیک میباشد. (0 کاربر عضو شده و 1 مهمان)

User Tag List

قوانين ايجاد تاپيک در انجمن

  • شما نمی توانید تاپیک ایحاد کنید
  • شما نمی توانید پاسخی ارسال کنید
  • شما نمی توانید فایل پیوست کنید
  • شما نمی توانید پاسخ خود را ویرایش کنید
  •