Crack Secure Netterm 4.2.c.9 -- my first reverse engineering by Kibitz ------------------------------------------------------------ Knowledge: ------------------------------------------------------------ Good understanding of C? Some tutorial, e.g.: http://www.blubb.at/sweety/cracking/tuts/crackme3crack.txt http://www.hackersclub.com/km/files/cfiles/cfiles99/netterm.htm I think you do not need to know ASM to finish this crack. After this crack, you will have fairly good understanding of the assembly language -- better than most students with 1 semester of ASM course. I know one ASM course ended with some instruction like AR, MR, SR, not even MOV, PUSH, CMP. My knowledge on ASM was a little bit less than that course :) ------------------------------------------------------------ ------------------------------------------------------------ Softwares: ------------------------------------------------------------ SoftIce 4.4 (required) IDA Pro 4.04 (not required) W32DASM 8.93 (helpful++) Hiew (helpful+) MSVC++ 6.0 (helpful) Turbo C++ 3.0 (helpful) ------------------------------------------------------------ SecureNetterm can be found at http://www.netterm.com ------------------------------------------------------------ There were two tutorials on cracking Netterm, the earliest was by Renegade for 4.2.1, which I think will be no longer useful. The other one is for 4.2.7 by iNCuBuS++ / MiB : http://www.hackersclub.com/km/files/cfiles/cfiles99/netterm.htm ------------------------------------------------------------ Since this is my 1st crack exercise, I will include lots of details/fails in this article. Since I compared my result with iNCuBuS++'s tutorial, The conclusion is that SecureNetterm's keygen be similar to netterm's, with some modifications ofcuz. ====================================================================== Phase 0: ====================================================================== First, let's use w32dasm open netterm.exe, oops, it hangs forever. Then try use IDA Pro (huge compared to w32dasm), it opens it successfully, but not much important information you can get from that. Then try SoftIce. I found it is helpful to put: lines 60;wd 10;wc 34;width 90;code on;X; as SoftIce's initialization string. You can change it in SoftIce's symbol loader, or edit winice.dat (it is in some sub directory of windows dir). Lots of tutorials use hmemcpy for get a breakpoint for the input text from a dialog window, but since it is 16-bit function, it does not work for NT. (Yes, it works for win98). Other tutorials suggested using GetDlgItemTextA, okie, hit ctrl-D, pops up the softice window, type: bpx GetDlgItemTextA Oops, invalid. (It is case-insensitive.) Read some more help, I figured out i shall get some *.dbg files for NT. The one came with my MSVC6.0++ was too old, since I upgraded my NT 4.0 to sp6a. Goto microsoft's homepage, I found the Debug Symbols for Service Pack 6. That shall be what I want. Unzip them into c:\winnt\symbols, they sit in dll\ subdirectory. Now in SoftIce's symbol loader, load user32.dbg, convert to symbol, it hangs. Kernal32.dbg, Avdapi32.dbg, and ntdll.dbg can convert to symbol successfully, let SoftIce autoload these symbols on startup (can be set in symbol loader). Just to make sure, probably you need add these lines to winice.dat: EXP=c:\winnt\system32\kernal32.dll EXP=c:\winnt\system32\advapi32.dll EXP=c:\winnt\system32\ntdll.dll EXP=c:\winnt\system32\hal.dll EXP=c:\winnt\system32\user32.dll EXP=c:\winnt\system32\winsrv.dll EXP=c:\winnt\system32\MSVCRT40.dll ====================================================================== Phase 1: ====================================================================== Okie, now I can use :bpx GetDlgItemTextA. Set this bpx in SoftIce, ctrl-D to put it in background, run netterm.exe, hit OK on the nag dialog box, click Help->Register netterm, input sth, then hit OK button, softice pops up, Things in code window is like: USER32!GetDlgItemTextA 001b:77eb0709 55 push ebp The title of command window is like: (PASSIVE)--KTEB(807AF020)--TID(00AF)--USER32!.text+0003F707 The 1st thing u need to know is this USER32!.text+.... on the title. It tells you where are u. Now we are in user32.dll . In softice, type: bd * This disables the breakpoint. Since we are in User32.dll, we hit F12 a few times to step to the caller function, until finally we reach netterm.exe, play around like in iNCuBuS++'s tutorial, that would eventually give you some idea on how to monitor the contents of memory et al. Actually when u hit F12 once, u are in isilogo.dll, the title of command window contains sth like: ....... isilogo!.text+1028 . Press F10 a few times to step down, finally it went out without reach netterm.exe. I will tell you how to step back to get break point in netterm.exe later, but when i did that, I did not know yet. ====================================================================== Phase 2: ====================================================================== So I quit to explore this approach, I figured out a crack is probably easier for a newbie to do. So I will try to get rid of the nag screen. I can actually set this breakpoint: bc * <= clear junk breakpoints bpx DialogBoxParamA This will pop up SoftIce when the nag screen shows. Disable this breakpoint in softice: bd * Step out of User32.dll (press F12), click OK on the nag screen, SoftIce pops up again -- you are back to netterm.exe (u notice the title of command window?), but we are probably in a small subroutine of netterm.exe, so let's inspect the codes around this, except some unknown call xxxxx, nothing seems interesting. To speed up the process, let's hit F12 twice, still in netterm.exe, one more F12, out to USER32.dll, and hit a few more F12, softice went to background. Now we have idea which is the outmost function we can reach in netterm.exe, so in softice: be * <== re-enable the breakpoint Run netterm.exe, softice pops up, bd * F12, click OK,F12, F12, STOP, here we are in the outmost of netterm.exe: 001b:0040d3a0 call 00402154 001b:0040d3af add esp, 0c <== we are here let's use mouse click on the line before current line, pressure F9 to make a breakpoint. (or, in command window, type: bpx #001b:0040d3a0) bl <== show breakpoint list, make sure DialogBoxParamA is disabled. Navigate around the Code window of SoftIce, you will find nothing is particularly interesting, probably we need go to 1 step further. So ctrl-D send softice to background, quit netterm.exe, start it again. and now softice pops, we are here: 001b:0040d3a0 call 00402154 <== highlighted Now it is probably nice to type: code off <== less message, but will be useful later to turn on Press F8 to step into this function 00402154, we see: 001b:00402153 ret <== return of last (unknown) function 001b:00402154 push ebp <== we are here 001b:00402155 mov ebp, esp ....[a few PageDown]...... 001b:00402153 call [KERNEL32!lstrcpyA] ....... 001b:00402153 call [KERNEL32!GetPrivateProfileStringA] ......[lots lots of page down] 001b:00402ae6 call [KERNEL32!LoadLibraryA] ......[lots of PageDown] 001b:00404d38 call [USER32!ShowWindow] ........ At least we can see some function calls that we can guess its role now. Use F10 to step down, or move the cursor down use PageDn key, then press F7, we can let the program run further. After we stepped over: 001b:00402ae6 call [KERNEL32!LoadLibraryA] we see some message in the command window: LDR: DLL english.dll .... (loaded anyway) Step further, we passed: 001b:00404d38 call [USER32!ShowWindow] The main window shows! but the nag screen is not yet on. Let's make a break point here, we can clear or disable the previous breakpoints now, but always use a pencil to write something u think might be a landmark. (Interesting enuf, I can set breakpoint using "bpx ShowWindow" on windows NT, but on windows 98, when u try to send softice to background, it automatically pops up, which means there are lots of "ShowWindow" events happening and u have no way to make use of this on win98, u have to use the absolute address for we got above: #001b:00404d38.) Now make command window larger, and try to scroll back to see the history, we see that lots of other dlls loaded, e.g., isilogo.dll (remember, we met this in our Phase 1?) Step more, till we see the nag dialog box show up. Watch the command window, we see some more dll loaded, this name is interesting: isireg.dll The isilogo.dll and isireg.dll are in the netterm.exe directory, we think the name shows what it does: isilogo is for showing the logo, isireg is for registration. ====================================================================== Phase 3 (not much useful, but interesting): ====================================================================== So open isireg.dll in w32dasm, and it can be partially disassembled. We notice here: +++++++++++++++++++ EXPORTED FUNCTIONS ++++++++++++++++++ Number of Exported Functions = 0011 (decimal) Addr:4000CED0 Ord: 1 (0001h) Name: DateIsSetBack Addr:4000D3A8 Ord: 2 (0002h) Name: AppSetup Addr:4000D594 Ord: 3 (0003h) Name: AppRemove Addr:4000D2B8 Ord: 4 (0004h) Name: AppIsSetup Addr:4000D9A0 Ord: 5 (0005h) Name: AppDaysLeft Addr:4000D7DC Ord: 6 (0006h) Name: AppIsExpired Addr:4000D9E8 Ord: 7 (0007h) Name: AppIsRegistered <== what's this?!! Addr:4000D280 Ord: 8 (0008h) Name: GetCDriveSerial Addr:4000DF20 Ord: 9 (0009h) Name: SharewareReg Addr:4000E058 Ord: 10 (000Ah) Name: SharewareLimit Addr:4000E0A4 Ord: 11 (000Bh) Name: SetDevPassword Just rename isireg.dll to isireg0.dll, and we see the nag screen shows 0 days left, and the program quit after u hit OK. We cannot see the code for these functions though. But from the names, we can guess some of the prototypes of function. So I opened MSVC++ 6.0, New, then select Project, let appWizard create shared MFC (dll) project isireg for you. and I added one function in isireg.cpp: extern "C" bool EXPORT AppIsRegistered(){ return true; } Ofcourse also need add these lines in isireg.def: EXPORTS AppIsRegistered Enable debug info, and make an interesting isireg.dll. Replace the isireg.dll by our own version, disable softice's breakpoint, run netterm.exe. No! It is similar to no isireg.dll. Okie, let's complete all the functions: extern "C" bool EXPORT DateIsSetBack(){ return false; } extern "C" bool EXPORT AppSetup(){ return true; } extern "C" bool EXPORT AppRemove(){ return true; } extern "C" bool EXPORT AppIsSetup(){ return true; } extern "C" int EXPORT AppDaysLeft(){ return 60; } extern "C" bool EXPORT AppIsExpired(){ return false; } extern "C" bool EXPORT AppIsRegistered(){ return true; } extern "C" int EXPORT GetCDriveSerial(){ return 6328; } extern "C" bool EXPORT SharewareReg(){ return true; } extern "C" bool EXPORT SharewareLimit(){ return 0; } extern "C" bool EXPORT SetDevPassword(){ return 1; } Ofcourse also need add these lines in isireg.def: EXPORTS ; Explicit exports can go here DateIsSetBack AppSetup AppRemove AppIsSetup AppDaysLeft AppIsExpired AppIsRegistered GetCDriveSerial SharewareReg SharewareLimit SetDevPassword This time the new dll will cause some illegal memory access for the netterm.exe. But are we making some progress here? let's make breakpoint in each of the source code, instead of using softice, we use MSVC++'s debug, let netterm run and we stopped at code in: SharewareLimt() ... or SetDevPassword()? Cannot remember clearly now. Ofcourse, we were simply hoping netterm.exe checks AppIsRegistered(), but it was not called. The illegal memory access message is expected since the formal parameters and return values of these functions are totally matched with original ones. =================================================================================== So let's copy the original isireg.dll back, use softice again. remember we set a breakpoint at: 001b:00404d38 call [USER32!ShowWindow] enable it, run netterm, and after softice pops up, step a few steps (F10), until after a call: 001b:00404d4b call 004405d9 The nag dialog box show up, after u click OK, it shows in softice's command window: LDR: isireg.dll .... (blah .. blah..., loaded) Let's make a breakpoint here, disable: 001b:00404d38 call [USER32!ShowWindow] Run netterm.exe again, step into this function by press F8, we see: 001b:004405d9 push ebp .... 001b:004405ea call 00440727 -> 001b:00440727 001b:0044xxxx call [KERNEL32!LoadLibraryA] .... 001b:0044xxxx call [KERNAL32!GetProcAddressA] From here, with the infomation from w32dasm: Addr:4000E058 Ord: 10 (000Ah) Name: SharewareLimit we can map each function with number, e.g., [004721dc] is SharewareLimit() function ..... (some compare on return value) -- To this step, i figured out "eax" stores returned value from function :) so you know, u do not need to know much about Assembly language, a good sense on C function will do. 001b:00440602 push 00468ebc 001b:00440607 call [004721c0] <==some function in isireg.dll, forgot name now, not important anyway -- At this point, a good guess is "push" is for the argument of the function that will be called, a number looks like a int value, some address is a "pointer" 001b:0044060d push 3c <== 0x3c == 60 days :) 001b:0044060f push 00468edc -- in softice, after this step, type: dd 00468edc we see it points to "Intersoft International ..." 001b:00440614 push 00468eee -- point to "Secure Netterm ..." 001b:00440607 call [004721dc] <==SharewareLimit() Now we know it shall be: SharewareLimit(char*, char*, int) ...... <==return type is pointer ...... 001b:00440639 call [004721cc] <==SetDevPassword() .... <==and we know return type is a pointer Now let's make some modification on our code: extern "C" int EXPORT SharewareLimit(char* company, char* software, int limit){ strcpy(company, "Test limit, set to 90."); return 0; } extern "C" char* EXPORT SetDevPassword(char* company){ return company; } Amazingly, :) this time it works in a way, that you can control how many days left for your evaluation (and it is always 60 days left in our code above). But the nag screen still show up, and our step through in function 001b:00445d9, considering all the jmp, jz, jnz et al, will end up with a nag dialog! So we think that shall be some code before: 001b:00404d38 call [USER32!ShowWindow] ====================================================================== Phase 4: ====================================================================== Just a few lines before [USER32!ShowWindow], there is a cmp/jnz pair, let's check that: 001b:00404d1b call 0042e42c 001b:00404d20 add esp, 0c 001b:00404d23 mov ecx, [ebp+0c] 001b:00404d26 mov edx, [ecx+2c] 001b:00404d29 cmp dword ptr [edx+000037a4],00 001b:00404d30 jnz 00404d86 <= jump at non-zero These a few lines of code sits just after a series of reg-file reading, and we traced a few of the arguments (the stacks by "push" command), they are mainly things like "TERM", "AnsiColor0", "LogoOnDisconnect" et al, which you can find in your netterm2.ini file. So we think this might be the key location. after step to "jnz 00404d86", write down some infomation: edx=136EB0, edx+000037a4 = 13A654. Type in Softice: a This will enter assmble mode for u, and u type: jz 00404d86 <== jump at zero hit return, and ctrl-D to let the program continue, wow, no more nag screen! So let's restart and step into this function (U made a break point here?) 001b:0042e42c push ebp ... 001b:0042e43d push eax <= type "dd eax", we see it points to "netterm2.ini" ... 001b:0042e44a push 00466fa4 <= type "dd 466fa4", we see it point to: "OWNER" 001b:0042e454 push 00466fac <= "Legal" 001b:0042e459 call [KERNAL32!GetPrivateProfileStringA] .... 001b:0042exxx push 0046xxxx <= "CODE" 001b:0042exxx push 0046xxxx <= "Legal" 001b:0042exxx call [KERNAL32!GetPrivateProfileStringA] ..... Okie, lets edit netterm2.ini, put these three lines there: [Legal] OWNER=Kibitz CODE=41414141 It is a good habit to put serial like 41414141? since "41" hex is 'A', can be easily identified for netterm Okie, re-run netterm.exe, step to here again: 001b:0042e4b7 push edx <= "41414141", our trial serial 001b:0042e4b8 call 004463f0 <= what's this? let's see the result 1st b4 step into it ... we see eax is now 8, seems it is the length of our trial serial 001b:0042e4c0/c3 cmp eax, 10 <= we combined two lines of code for easy understanding so the serial length shall be 16 !! let's edit netterm2.ini make the serial to be "4141414141414141" and run the program again! 001b:0042e4c7 jnz 0042e543 .... .... 001b:0042e4ea push 08 ... 001b:0042e4f5 push edx 001b:0042e4f6 push 004669ac <=seems it is an encoding table (type: "dd 46699c") 0123456789abcdef 00000000a1234567 89cfikuq3A9Sz}m5 u|~j0-1`kepqr6dv 001b:0042e4f6 call 00446d20 <= UnKnownfunc([004669ac], [004669c8], 08) After this UnknownFunc(), this table becomes: 0123456789abcdef z}m5u|~ja1234567 89cfikuq3A9Sz}m5 u|~j0-1`kepqr6dv Remember the tutorial of keygen of Netterm 4.2.7? here the encoding table is similar .... Now: let's check the stack, either by "db (ss:esp)->0" after each push, or "dd 12f2f8", "dd 12f3f8", "dd 46699c" ("Kibitz") ("414141....") 0123456789abcdef z}m5u|~ja1234567 89cfikuq3A9Sz}m5 u|~j0-1`kepqr6dv 001b:0042e518 call 00450bd6 <= UnknownFunc2(08, "Kibitz", "414141...", [encoding-table]); 001b:0042e520/24 cmp eax, 00 <= return value=0, failed, then go back to loop check for another version 001b:0042e524 jz 0042e541 <= jumps to 0042e4d2, ... 001b:0042e529 mov eax,[ebp-8] 001b:0042e52c mov dword ptr [edx+000037a8],eax So the function 00450bd6 is the one to check the user/serial combination. Press F8 a few times, and u are in isilogo.dll !!! We see in phase 1, we also stepped into isilogo.dll, but it was in different location? isireg.dll's name was just too attractive, we wish we would have taken a look at isilogo.dll earlier than this! Run w32dasm, open isilogo.dll, it is disassembled completely, with detailed code on each of these exported functions: +++++++++++++++++++ EXPORTED FUNCTIONS ++++++++++++++++++ Number of Exported Functions = 0007 (decimal) Addr:1C001079 Ord: 1 (0001h) Name: CloseLogoDIB Addr:1C001040 Ord: 2 (0002h) Name: DoLogo Addr:1C002156 Ord: 3 (0003h) Name: DoRegistration Addr:1C0010CE Ord: 4 (0004h) Name: LogoPaint Addr:1C0015F5 Ord: 5 (0005h) Name: LogoPaletteSize Addr:1C0020B7 Ord: 6 (0006h) Name: ShowAbout Addr:1C0020FB Ord: 7 (0007h) Name: ShowRegister Yes, we guess DoRegistration() shall be the magic function. Goto that subroutine, probably it is easier for you if u can print that two pages of code for comparison with softice. Well, we need not read the code carefully, unless we want to make a keygen. Compare the code from w32dasm with the one from softice, now we know that UnknownFunct2() is DoRegistration()! OUR only requirement for this function to return a value 1 (i.e., set eax to non-zero) we see from w32dasm's code, sth is interesting: :1C002197 mov eax, 00000001 :1C00219C jmp 1C00228B <== this is about the end of subroutine So I just try to re-direct the flow to there, i picked up: :1C0021BB call 1C0025AC <== we will patch here :1C0021C0 jmp 1C00228B <== this also jumps to the end Now in softice, type: "code on", this will turn on the detail codes. The code for "call 1C0025AC" was E8EC030000, 5 bytes, Now let's run netterm.exe and step into this location in softice, step to the place, type: a <== assemble jmp 292197 nop nop nop Hit return and softice gave the code for this: EBD5909090 The three lines of nop is just to make the patch also be 5 bytes. (note: 001b:292197 in softice is same location as :1C00219C in w32dasm. Now let's use Hiew (actually any HexEdit will do, just need a hex search, but Hiew is smarter, it can go to the location :1C0021BB) to edit isilogo.dll. Change the 5 bytes from E8EC030000 to EBD5909090 and we are done! --------------------------------------------------------------------------- Final phase: ENJOY! but wait!! --------------------------------------------------------------------------- Now the nag screen is gone after u enter any 16 character long serial to registrate it. Let's try to connect a site ... WHAT? nothing goes on!!! Sth is wrong, let's put our original isilogo.dll back, ofcourse it pops up nag screen again. Let's pick SSH-1, and telnet to some unix host ... Netterm pops up asking you userid! Okie, we have a breakpoint here, bpx DialogBoxParamA When netterm pops up, we can use F12 get out of the inner subroutines, but it does not reach netterm.exe -- the control is giving to windows. Do not panic, after we get this breakpoint, let's hit F12, get out just 1 level, now navigate up in the code window of softice, till u see a line below "ret" or "ret xxx" instruction. Set the breakpoint at this instruction, disable the previous instruction, after try connect (ofcourse, hit Cancel after dialog box), the breakpoint works again. Disable this one, hit F12, u are in an outer function, and u are just 1 instruction below the "call xxxx" line. Navigate up again, set breakpoint at the beginning of subroutine (usually just after "ret" instruction).... Finally u can reach a breakpoint in netterm.exe (sweat!). By careful inspection, you find that it checks a byte [edx+37a8], login AND it with 2, and if it is 1, will continue ... Remember what we achieved is set [edx+37a4] (0013A654) to 1? Where is that [edx+37a8] (0013A658)? Look at the code we did not analyse: ... 001b:0042e529 mov eax,[ebp-8] 001b:0042e52c mov dword ptr [edx+000037a8],eax here is it, it set [ebp-8] to [edx+37a8]. So in DoRegistration() function, we have to set [ebp-8] to sth meaningful (the 2nd bit has to be true). Let's set it to FFFFFFFF. :) And indeed, we do not need to change those jump's in DoRegistration(), we simply write a new code at the beginning: mov eax, 1 mov [ebp-8], -1 ret 0010 Use softice trace to the location and let it assemble, we find we need another line to match the byte pattern. The code at the location was: 55 8BEC 81EC0403000 53 56 57 837D0800 Now we change to: B801000000 C745F8FFFFFFFF C21000 90 From the Hiew, we know the offset of the 16 bytes we need to change is 1556 (hex). So we can change it use our favorite editor, or write a small program crack it: I chose Turbo C++ 3.0 to compile it, since 16-bit program is smaller in size than 32-bit program in our case. The compiled 16-bit crknterm.exe is 8.43kb, the 32-bit version would be around 30kb. ============== beginning of crknterm.c =========================================== // exercise crack for Secure Netterm 3.2.c.9 // Change isilogo.dll, 16 bytes start from 1556h // add registration info in netterm2.ini file (serial need to be 16 chars) #include #include #include #define BYTES 16 #define OFFSET 0x1556 #define DLL "isilogo.dll" #define INIFILE "netterm2.ini" #define SECTION "[LEGAL]" #define NAME "OWNER=" #define SERIAL "CODE=" char src[] = "\x55\x8b\xec\x81\xec\x04\x03\x00\x00\x53\x56\x57\x83\x7d\x08\x00"; /** * mov eax, 1 * mov [ebp-8], -1 * ret 0010 * nop */ char tgt[] = "\xb8\x01\x00\x00\x00\xc7\x45\xf8\xff\xff\xff\xff\xc2\x10\x00\x90"; int main(){ char buff[256] = INIFILE; FILE *fp; int n; puts(""); if((fp = fopen(DLL, "r+b")) == NULL){ puts("Make sure you run me in the directory contains " DLL "!"); exit(1); } fseek(fp, OFFSET, SEEK_SET); if( (n=fread(buff, sizeof(char), BYTES, fp)) != BYTES || memcmp(buff, src, BYTES)!=0){ if(n==BYTES && memcmp(buff, tgt, BYTES)==0){ puts(DLL " is already patched!"); fclose(fp); } else { puts(DLL " is not the correct version!"); exit(2); } } else { fseek(fp, OFFSET, SEEK_SET); if(fwrite(tgt, sizeof(char), BYTES, fp) == BYTES) puts(DLL " patched successfully!"); else puts(DLL " cannot be written, make sure netterm.exe is not using it!\n"); fclose(fp); } puts("\nRun netterm.exe, go to menu Help->Register netterm\nInput any name as you want, but the serial number has to be 16 characters long.\nFor example: 1234567890123456"); return 0; } ============== end of crknterm.c ===========================================