Here is the pm from LLXX on how the target was cracked:
Hello,
Sorry to disappoint you, but the protection was much easier than I expected. I didn't even need to use IDA, and later when I loaded it into IDA it didn't disassemble very well, maybe that is where your difficulty exists. Always try easy way first! Target_1.exe checks once and was cracked in approximately 20 seconds, Target_2.exe checked twice and took 1 minute of my time
I'll explain now how I did it. First of all, get the general feeling of the target. Load into a text editor and look around. You find "Borland" string in there, this means it was compiled with Borland's C/C++ runtime library - important information to know, so you don't spend time tracing through the startup code like you did. Run the program. Target_1.exe first, gives the message that security is missing etc. Then, just use DOS debugger DEBUG.EXE to inspect. Run it from cmd prompt like:
debug Target_1.exe
You see the "-" which is prompt of action. You can type ? and press Enter to get the list of commands. First of all, since we know that Borland startup code is linked, the task is to find where main() function is. Enter u and press Enter to see the entrypoint. You should see the "mov ah 30" "int 21" which is very typical for startup code to do, it is just checking for DOS version. I did u 10 more times, and at :0122 finds INT 1A which you said couldn't find? This means the assumption is correct, indeed we are in the startup code. U once more, and see:
Code:
0143 BE9C2E MOV SI,2E9C
0146 BFC02E MOV DI,2EC0
0149 E8D000 CALL 021C
014C FF368800 PUSH [0088]
0150 FF368600 PUSH [0086]
0154 FF368400 PUSH [0084]
0158 E8D631 CALL 3331Recognise these pushes? It is very important! This is call to main() we have found. main() is at 3331, so run till 3331 with command g 3331 (go until 3331). Now you see the Registers displayed and push bp at the location 3331, this is the start of main(). This now takes to pay more attention to the detail, start stepping through the program. Use command p to step through without entering the CALLs. After p'ing 29 times hits Welcome screen. Press any key to exit the welcome screen, and stops at the CALL 51B3. P again, the screen clears and sees CALL 19BB. P again. See the error message comes up! Press ESC to terminate for now, 19BB returns and sees the OR AX, AX stopped at. Step once, then finds "JNZ 33A0" - now think, why would conditional jump be present after check of d0ngle? 19BB returns AX = 0, so that means FAILURE, so in this case it should not be zero, we forces it to jump by places command e cs:3396 eb which inserts EB byte (JMP opcode) into the cs:3396 where JNZ originally was. Nothing will display when you run this command, but use r command to show the current state, and finds JMP 33A0 has been shown, the byte indicates has changed from 7508 to EB08. Thinks we have found the critical jump, run g command to continue with the execution. It works! Continue entering the data, etc. Now finishes "Billing Summary" screen, press ESC and it will exit back to DEBUG, shows message "Program terminated normally" and the prompt. Run command q to exit. One supplementary exercise: It contains the year 2000 bug with the entered date. Figure out how to fix that
It actually takes much less time to step through and find the jump than to describe it above... you try it yourself and see how quick it was. Now, onto Target_2.exe - unfortunately PM system won't let me send more than 3.9Kb at once, see the next message...
------------------------------------------------------------
Opens the executable in the editor, look around. It is compiled with same Borland compiler, it seems. Also note the size: 326Kb. This is much larger than the other one, so have to watch out for the segmentation when inspecting the code. Run the file. See "security error" message. It seems to be the first output produced, so the security error must be closer to the start than in the other one.
Load it into debug. Again, u. You'll see almost the same sequence of instructions as in the other one, this is a good sign that the compiler is the same. Keep u, but this time sees not the INT 1A after 9 u's, but already the pushes. Perhaps this is what confused you, however you must think - it is slightly different but you see the very easy recognises
Code:
0115 FF367200 PUSH [0072]
0119 FF367000 PUSH [0070]
011D FF366E00 PUSH [006E]
0121 FF366C00 PUSH [006C]and you know that INT 1A was executed, so then where is it? You found in the disassembly that INT 1A, and I told you it was part of a function, so that function must've been called before that. Did you notice all the CALLs that took place before the PUSHs? It most likely was one of them. U again, and finds the:
Code:
0125 FF366A00 PUSH [006A]
0129 9AE390501D CALL 1D50:90E3That is definitely the main() function (it might not be at segment 1D50, but the offset 90E3 will be the same on your machine). However, notice that the call is different from the one in the other program. Also notice that this program is much larger than the other program. This is known as an intersegment or far call, i.e. the location of call is in another segment. Then, instead of g 3331 earlier, run command g 1d50:90e3 (substitute whatever segment your program is, since I don't think it's going to be 1d50 as well). Arrives the position:
Code:
AX=0100 BX=0EEA CX=0FB4 DX=0EEA SP=0FD2 BP=0FE4 SI=0EA8 DI=0EEA
DS=50C2 ES=50C2 SS=51B3 CS=1D50 IP=90E3 NV UP EI PL ZR NA PE NC
1D50:90E3 55 PUSH BPSees again that we have reached main() entrypoint.
Again 4000 byte PM limit... see part 3...
--------------------------------------------------
As with the other one, repeat p command until find something interesting. In this case, I had to do it 118 times - watch for this:
Code:
AX=6914 BX=004C CX=0000 DX=71D8 SP=0F5E BP=0FD0 SI=0EA8 DI=0EEA
DS=4640 ES=71D8 SS=51B3 CS=1D50 IP=92D9 NV UP EI PL NZ AC PO NC
1D50:92D9 9ABD43EB3B CALL 3BEB:43BDAgain, the segments will be different, but notice the CALL - the code is in segment 1d50, but it's calling far away to 3beb. When runs the command p I notice a slight pause before it returns. This is different from all the previous p's, so something interesting is happening. It might be trying to access the dongle. Pay more attention now. P two more times over the POP CX's, then notice AX = FFFF when find the instruction MOV SI, AX. P again, and find CMP AX, FFFF and then P over that, find JZ 92AC. This is important. From use of logic, think that if the CALL succeeded, it would not return FFFF (-1) which normally indicates ERROR. So, this should not jump, and it should be striked out with two NOPs. Use the command e cs:92e5 90 90 just like the previous program to strike out the JZ, and then r to see that it has been changed to NOP. Also, use u to see what the upcoming code is going to be. You'll see more far CALLs to 43bd, i.e. what seems to be the dongle code. That means that the protection check isn't over yet (if you try to run right now, only after killing the first jmp, you get "Security error #3"

. P over these, until you get to the MOV SI, AX followed by CMP SI, -01. Notice once again, AX is FFFF without the dongle. Following the CMP SI, -01 (-01 is equal to FFFF), at 9314, is JNZ 9329. Following the logic, if AX was not FFFF, i.e. the dongle is OK, it will jump to 9329. Instead of killing this jump with NOP, we have to force it. The command is e cs:9314 eb. Once again, r to confirm that it has changed to JMP 9329, then P over it and U to see the upcoming code. No more calls to 43bd, it seems. We can try to run it now, with g. It works (as far as I can tell...). Again, the description I wrote above makes it seem more difficult than it really is. Try it
IMHO, this prog wasn't really worth cracking for use, but as an educational experience, it's nice. Surprisingly easy to crack for a "dongle", and I haven't the dongle either. On a difficulty scale of 0 - F, Target_1.exe would be 0 and Target_2.exe maybe 0.5 at the most. This program reminds me of an exercise I had in a programming class once...
Try the crackme in the project area if you want something more interesting to crackg