home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / +Sandman / lsoth1.txt < prev    next >
Text File  |  2000-05-25  |  48KB  |  487 lines

  1. How to break the protection in the Immortal Descendants crackme 3
  2. ===================================================================
  3. ID crackme by Volatility
  4. Essay written by Lord Soth
  5.  
  6. Introduction
  7. =----------=
  8. Ok, in this essay I'll try to explain in the best possible way how to crack this nice little crackme that Vol coded (with the help of Eternal Bliss if I'm not mistaken).
  9. So first, I'd like to thank Volatility and Eternal Bliss for making this crackme.
  10.  
  11. So here we are facing a crackme exercise that has 2 key features in it. This crackme has a serial key protection. We will try to unlock it so it will behave nicely and be registered for us :)
  12. How do we do that ?
  13.  
  14. There are 2 answers for this question. We can attack this protection in 2 different ways as we'll see. The first being the direct and standard way of sniffing the serial number and reversing the calculation routine, and possibly other interesting parts of the target, the other would be to reconstruct a serial key file that this program looks for at startup.
  15. More on this later.
  16.  
  17. The tools we'll need : 
  18. Well, I'm not much of a fan of disassemblers and programs like SmartCheck (altho they can make your life easier sometimes). I am a SI freak myself :)
  19. So in this crackme we'll use SoftIce 3.24, FileMon and PE browse.
  20.  
  21. So, I've said there are 2 ways to crack this target. I'll start with the direct approach, which is to reverse the calculation routine by entering my name etc, etc..
  22. In the second part of this essay I'll describe how to reverse the target with a key file.
  23. In the last part, I'll give some info on creating a serial key gen (this is just for fun anyways, since we don't really need to register this program, it's not a program if you get my meaning hehe).
  24.  
  25. The direct approach - serial sniffing
  26. =-----------------------------------=
  27. Ok, a few words about this crackme. Vol warned us all that this crackme is a bit tougher to crack because it is coded in a way to try to fool crackers. What he tried to do is to try and hide things or checks from you and to make your life hard by not using standard API calls.
  28. Another thing that goes the distance for the author is the fact that this crackme is written in VB, and since VB uses a Virtual Machine (VM), which is called MSVBVM50.DLL . As most of you know, this VM is what makes your program work, its a runtime library of functions the VB program you code uses. Now, some of those functions are also using Kernel functions to accomplish stuff, but its done differently than most programs we know. Most programs are written in a standard way, either in C or C++ or whatever.
  29. In those cases, a solid way of cracking would be to set a BP on GetWindowText or any equivalent, then a BP on the user name, reversing what we see, and on and on, till we crack it.
  30. After a little while into this program we see that its not so simple, in fact, none of these APIs work on this program.
  31. Our first step is of course to locate our entered user name. As a first attempt, I used 'lord soth'.
  32. So, what can we do ?
  33.  
  34. Lets start with the basics, We'll fire up FileMon just to see what's happening behind the scenes, and we'll see this program looks for Reginfo.key file, which is probably the serial file we want to reconstruct, but we'll deal with this later.
  35. I myself, when cracking a program , I always open it in PE browse and look at the imported functions, to see what I can learn from it. In this crackme's case, of course, all we see is an import of MSVBVM50. In the imported function list there are a few names.
  36. Now we'll need a little insight as well. I looked for anything that might help me detect string manipulations, or character manipulations, since the crackme obviously have to do just that.
  37.  
  38. The best matches I found were : rtcSendKeys and __vbaStrVarVal, so i set a BP on them both.
  39. Just a side note, when setting a BP on an API call, in a dynamic library that is NOT loaded all the time, you must realize, that when the DLL is not loaded, the BP will change to a fixed value somewhere in memory. An example of such a DLL is MSVBVM, which is called only when a VB program is running. Normally, we set breakpoints on system APIs, which are always loaded, so we don't know about this problem, however, when dealing with VB, we'll set the BPs only when the program is loaded.
  40. Ok, so we set those BPs. As it happens, rtcSendKeys does not give us anything. We try entering our user name, and nothing happens. Also, we don't have any confirm button in the dialog. This dialog was designed that when the correct serial is entered for our username, the 'Register' button will be activated. Of course, we might wanna go for API calls that enable buttons and stuff, but then we'll have to reverse who called it, and since it's all in VB, there is no direct call from the target program to the Kernel APIs.
  41. But we notice something else is happening. Since it has to be enabled, the program has to check for the serial all the time. A good way to do so would be to check it every key stroke, but nothing happens and we didn't break yet on any of the APIs we chose.
  42. Well if you remember, these programs have to have a user name, and then calculate the serial using it, so maybe it only checks afer we finish typing a user name. But how does the program knows we finished entering our user name ? well, simple, if we go to the Serial key text box, it's pretty evident we're done with the user name. But still nothing happens.
  43. On the other hand, we have'nt typed anything in the serial box yet, so lets try something out.
  44. Type any number you want in the box, and BOOM, even before you realize, SI is up and screaming at you that you got a break due to blah blah blah.. __vbaStrVarVal.
  45. Returning to the caller routine by doing a P RET (F12 for me), we'll see the following piece of code (offsets will probly be different for you) :
  46.  
  47. 0040517F    LEA EAX,[EBP-00A0]
  48. 00405185    LEA ECX,[EBP-7C]
  49. 00405188    PUSH EAX
  50. 00405189    PUSH ECX
  51. 0040518A    CALL [MSVBVM50!__vbaStrVarVal]       Here's the lucky winner !
  52. 00405190    PUSH EAX        <<< Here we are!!
  53. 00405191    CALL [MSVBVM50!rtcAnsiValueBstr]     wonders what this is doing ??
  54. 00405197    MOV ESI,[MSVBVM50!__vbaVarMove]
  55. 0040519D    LEA EDX,[EBP-00D0]
  56. 004051A3    LEA ECX,[EBP-34]
  57. 004051A6    MOV [EBP-00C8],AX                     Our char is put on the stack
  58. 004051AD    MOV DWORD PTR [EBP-00D0],00000002
  59. 004051B7    CALL ESI
  60.  
  61. Ok, so lets see what we're dealing with here. We've landed at 5190, right after the call, we see which registers were changed (those highlighted), and of course, EAX holds the return value of a function, so it naturally changes. If you look at it, you'll see its in the crackme's address space, or near it. Lets look what it holds.
  62. Do a D EAX, and you should be able to see the first letter of your user name in the data window. Nifty, but how can we use this ?
  63. This pointer to the letter is pushed and there's another call, so lets see what this one returns for us. We take a look at the return value after the call at 5191, and we see EAX holds the ASCII value of our first letter, in HEX! how cool, but this still does'nt get us anywhere.
  64. We start thinking, if this prog is taking 1 char at a time, it has to store them somewhere and looking at the call at 51B7 (which is __vbaVarMove as you can see), we guess that this is used by VB to move the variable's content to somewhere else in memory. This place is actually the stack as it turns out at the end.
  65. We do some stepping, and after we return from the last call we get another pointer in EAX (the return value). This pointer now points to a memory location inside the program's stack.
  66. Lets have a look, do a D EAX, and you'll see a 16 byte sequence, and our letter is stored in the 9th byte, or in EAX+08.
  67. At this point we can try continuing so that other letters will be taken from the username in the same way, or we can try to set a memory read BP on the letter. In any case, both lead to the same result. We'll break next at __vbaStrVarVal again, however, this time the addresses will be different, how come ?
  68. Look all around the code segment, and you'll see that there is more than 1 instance of this segment in memory.
  69. Ok doing what we did for the first char, we display the contents of the stack after the last call, and we find a nice surprise. The first letter is still there, and above it in the data window is the second letter. There's a 16 byte gap between them though. Both are stored on the stack , and probably along with other data related. My first thought was that each is put on the stack because each is a variable in its own. This might be a nice touch, instead of reading and storing a whole string in memory, you divide it into chars to throw off the cracker.
  70.  
  71. Lets go on a bit. We continue execution a few times, and more letters appear, but here comes a tricky part. After the 5th letter, there are no more breaks! is something wrong with SI ? probly not, lets take a look.
  72. If you set a BP on 1 of the letters, SI will pop, if not, you'll go back to the program.
  73. Lets assume for a second we didn't BP on any letters, and we're back at the program, for the time being. Lets try another digit. Again SI pops on __vbaStrVarVal, and again we go through all this process, and at the end, we only see 5 letters stored on the stack.
  74. This is most bizzare, where are the other letters in our name ??
  75. Well, THINK!! Where does it say that the author has to use ALL the chars you put into your username?? Where does it say that the author can't manipulate the name and make some freak string out of it?? well the answer is NOWHERE!! hehe
  76. So we go by the assumption that Vol used only 5 letters.
  77.  
  78. Ok, so what do we do next ? Normally we would like to put a BP on the letter/s to see what reads them, and who manipulates them. We'll do that here also, but in a slightly different way. We will only put a BP on one letter, because the letters are seperated from each other by 16 bytes.
  79. True, we could have put a BP on the whole range, but that's a stack address space, which will not only degrade system performance considerably, it might pop a lot too, because stack data gets modified all the time.
  80. So lets put a BP on the address of the first letter of our username (usually in this type of BPs I always use the read option, just to filter out any unwanted writes to that address, unless of course I suspect something is replacing it or something).
  81. After we put a BP, we'll continue execution as usual. If we're at the text box typing a serial, we'll need to type a digit in order for the process to even be started (remember, previously I mentioned that this program checks only when you enter digits into the text box).
  82. Type any digit you want, and we'll break on __vbaStrVarVal, like before. Follow the above process, and locate the letters on the stack as I explained earlier. Once you do, put the BP on 1 of the letters (preferably the 1st hehe).
  83. Continue execution and SI will pop up and we're 1 step closer to figuring this out :-)
  84. Once it breaks, we'll see the following piece of code :
  85.  
  86. (by the way, my address for the first letter which was 'l' was 0063F3C8, which translates to the following command : BP 63f3c8 r)
  87.  
  88. 7FEC4BDD    MOV DX,[EDI+08]        This what triggered the break!
  89. 7FEC4BE1    LEA EAX,[ESP+18]    <<< We're here
  90. 7FEC4BE5    MOV ECX,[ESP+20]
  91. 7FEC4BE9    PUSH EAX
  92. 7FEC4BEA    PUSH ECX
  93. 7FEC4BEB    PUSH EBX
  94. 7FEC4BEC    PUSH EDX
  95. 7FEC4BED    CALL 7FEE0C0A
  96.  
  97. First of all, notice that we've broke on the instruction that comes AFTER the one that triggered the BP, this is because this is a memory read BP. Anyway, something bothered me the minute I saw this piece of code. At first, I could'nt see any kind of calculation anywhere near this piece of code, so I must assume it's in the following CALL. Whats even MORE disturbing, and yes, this is most disturbing is that the whole snippet of code is HIGH HIGH in memory. This only means one thing, that this snippet of code belongs to the Operating System somehow. But the nice little line at the bottom of the SI screen tells us this is the IDcrackme process we've broken into.
  98. Soon we'll figure out this little 'paradox'.
  99. We are expecting to find some kind of manipulation of our letters, but we land inside an area occupied by OS code (soon to be discovered as MSVBVM itself).
  100. As I stated, this part of the code belongs to MSVBVM, but how come ? The crackme program has to do all the math work , right ? well, read on...
  101.  
  102. Ok, so our char is in DX right now, and we see it's being pushed on the stack, so lets trace into the CALL to see what kind of good stuff we can dig up inside, this is what I've found :
  103.  
  104. 7FEC0C0A    MOVSX ECX,DWORD PTR [ESP+04]    ; Takes character
  105. 7FEC0C0F    SUB ESP,50
  106. 7FEC0C12    LEA EAX,[ESP+00]
  107. 7FEC0C16    PUSH EAX
  108. 7FEC0C17    PUSH ECX    ; Our char is once again pushed on the stack
  109. 7FEC0C18    CALL 7FECD030
  110.  
  111. Here our char is removed from the stack using offset ESP+04 (4 bytes into the stack), and moved into ECX, which then becomes a SIGNED integer value.
  112. Some more pushes, and we again get another CALL instruction, which pisses us off.
  113. Now, normally we look for shortcuts to try to land right in the protection scheme, but guys, sometimes its just not possible. Sometimes the programmer hides the calculation routine as best he/she can by moving the characters/strings to different locations in memory (often more than 1), and hiding the actual routine after a few CALLs or something similar.
  114. However, this is not the IDcrackme.exe file code, this is the MSVBVM.DLL code, and we don't see why it should act like this. But the answer is obvious, this is just another testimony of Micro$oft's overbloated programming languages, development tools, Operating systems, software, hardware, coffee machines, secretaries and of course inevitably Gill Bates.. (err, did I spell it wrongly ? hehehe)... oops, sorry, got a little distracted there.
  115. Back to our overbloated DLL, we'll need to dig in and find what the hell is going on in there, even though it might lead us to nothing at all!
  116. But alas, our zen powers tells us that this is indeed the right place to be in, so we're continuing :)
  117. Lets trace through the CALL and we'll land at 7FECD030:
  118.  
  119. 7FECD030    MOV EAX,[ESP+04]    ; Our char is here !
  120. 7FECD034    PUSH ESI
  121. 7FECD035    TEST EAX,EAX
  122. 7FECD037    JGE 7FECD04C    ; It appears we are branching here..
  123.  
  124. geez, you'd think they'll learn to code efficient DLLs, but noooooooo, MS can't do that, they never heard of the word efficient.
  125. Anyways, our poor character is baing taken off the atack and put in EAX, and then being checked for I donno what reason, but lets go on.
  126.  
  127. .
  128. .
  129. .
  130. 7FECD04C    MOV ESI,[ESP+0C]    ; remember this 1, we'll need it
  131. 7FECD050    PUSH 00
  132. 7FECD052    PUSH ESI
  133. 7FECD053    PUSH EAX        ; Character is here, being pushed once again!!
  134. 7FECD054    CALL 7FEBF459
  135.  
  136. This snippet of code pushed a 00 , a pointer to somewhere in memory, and our char. That poor char has been taken off and pushed on the stack so many times its head must be spinning!! hehe
  137. But then we get another CALL and we decide to go even deeper into the labyrinth, how brave of us.
  138. Just a little note on the pointer that ESI holds. When I crack, especially when the target program makes such uses of the stack and memory, I check as much pointers as I can. I just display the memory contents to where they're pointing to. I don't do this to everything that goes through the registers, only pointers in the target's address space (or close to it), or any other parts the program calls (like the MSVBVM50.DLL, and such), and sometimes I take a look at the stack as well, when a register is pointing to it.
  139. So after I viewed what's at ESI by doing a D ESI, I noticed absolutely nothing, but I had nowhere to look , so I continued on.
  140. Now we get to a very interesting part, which is the 'heart' of this section :
  141.  
  142. 7FEBF459    CMP DWORD PTR [ESP+0C],00
  143. 7FEBF45E    PUSH EBX
  144. 7FEBF45F    PUSH ESI
  145. 7FEBF460    MOV ESI,[ESP+10]
  146. 7FEBF464    PUSH EDI    ; save those regs, so they don't get ruined
  147. 7FEBF465    PUSH EBP
  148. 7FEBF466    JNZ 7FEB9CFF ; soon we'll see this might jump if finished calculation
  149. 7FEBF46C    MOV EBX,[ESP+14]    ; now we're getting the character
  150. 7FEBF470    MOV EDI,ESI
  151. 7FEBF472    MOV ECX,0A            ; move 10 into ECX
  152. 7FEBF477    MOV EAX,EBX            ; char is saved in EAX as well
  153. 7FEBF479    SUB EDX,EDX            ; zero this punk out :)
  154. 7FEBF47B    MOV EBP,0A            ; load EBP with 10
  155. 7FEBF480    DIV ECX                ; start of calculation
  156. 7FEBF482    MOV ECX,EDX            ; move remainder
  157. 7FEBF484    MOV EAX,EBX            ; restore the Char ASCII value
  158. 7FEBF486    ADD CX,30
  159. 7FEBF48A    SUB EDX,EDX
  160. 7FEBF48C    DIV EBP                ; again divide it by 10
  161. 7FEBF48E    MOV [ESI],CX        ; so that pointer was where the end result will be stored..
  162. 7FEBF491    MOV EBX,EAX
  163. 7FEBF493    ADD ESI,2
  164. 7FEBF496    TEST EBX,EBX        ; test to see if there's no remainder..
  165. 7FEBF498    JNZ 7FEBF572         ; this will actually jump to 7FEBF472
  166. 7FEBF49A    MOV WORD PTR [ESI],0000
  167. 7FEBF49F    SUB ESI,2
  168. 7FEBF4A2    MOV AX,[ESI]
  169. 7FEBF4A5    SUB ESI,2
  170. 7FEBF4A8    MOV CX,[EDI]
  171. 7FEBF4AB    ADD EDI,2
  172. 7FEBF4AE    MOV [ESI+2],CX
  173. 7FEBF4B2    CMP EDI,ESI            ; if both ptr's the same, end this action..
  174. 7FEBF4B4    MOV [EDI-02],AX
  175. 7FEBF4B8    JB 7FEBF5A2         ; this will actually jump to 7FEBF472
  176. 7FEBF4BA    POP EBP
  177. 7FEBF4BB    POP EDI
  178. 7FEBF4BC    POP ESI
  179. 7FEBF4BD    POP EBX
  180. 7FEBF4BE    RET 000C            ; outa here already
  181.  
  182. So we've come to our last stop (almost). I will save you some guessing, and tell you that the pointer that ESI holds points to where the numbers will be stored, I found this by stepping through the code, seeing what it calculates, and where it stored the result.
  183. But before that, and before our surprise, lets take a look at what is happening in this piece of code.
  184. Our char is put in EBX before anything starts, then moved to EAX. In the meantime, ECX and EBP are both loaded with 0A which is 10 in decimal as you all know. EDX is zeroed out, and we'll soon see why.
  185. The DIV ECX at F480 divides EAX by ECX and stores the result at EAX and the remainder at EDX as you know (if you don't, take a look at what the instruction does in some ASM guide, this instruction has several functions, this is 1 of them).
  186. Then the original ASCII of the char is put back in EAX. This means that the result does'nt interest us, how interesting. The remainder is being transferred into ECX and added with 30h. Now since ECX holds a remainder from a division by 10, its holding a number between 0 and 9, add 30h to it, and you get a number between 30h and 39h, which are the ASCII codes of the numbers.
  187. EDX is zeroed again, and the division by 10 is done again. Not sure why, maybe there are values that will cause weird stuff after only 1 time. Again, ask MS to explain :) hehe
  188. Now after the second division, the remainder is being transferred into EBX, and our char is lost, and replaced with the remainder of the first division.
  189. Now there's a test on the remainder. If there isn't one, the program does'nt go back to F4A2, and gets out of this procedure. If there's a remainder, we go back to F4A2, and the remainder is put in EAX again, and again divided by 10, and that remainder is added to 30h and we get another digit. All this happens till our remainder is 0, and we're done dividing (can't divide no more).
  190. Then the code from F49A to F4B4 will reverse the order of the digits, and we'll see the final result of this 'calculation'.
  191. Ok lets ponder for a second. What just happened here ? The char's ASCII was divided by 10 till we could'nt divide no more, and the remainding digits were stored as a 'string' on the area where ESI pointed to.
  192. What did we actually reverse here ?? A calculation routine in the middle of MSVBVM ?? NO.
  193. If you stop jumping to conclusions and take a look of the calculation and the result, you might get lucky enough to see that the end result is a certain value, but not just any value!!
  194. It's the ASCII of the char we entered, in decimal, not in hex like we use to see it.
  195. Now everything becomes clearer. This part and all those divisions by 10 are actually taking a hex ASCII value and converting it to decimal radix, this is so cool , we reversed a normal function of the MSVBVM50.DLL, what a bummer this must be. We go and think we found nothing, but you can't be more wrong, as you're about to see.
  196. At first I must admit, I figured Volatility used this value of the ASCII (in a string format), as a basis for his calculation of the serial or something. I was wrong, there is no more calculations as I'm going to show you in a minute.
  197. So I decided to continue tracing the code, after figuring out what it was doing. I traced and stepped and eventually I came back to the IDcrackme.exe address space (I could tell, cause it was 0040 something..).
  198. Before I went on, I wanted to set a BP so I can always go back to that part in case I want to do some more digging. I looked when the character was first read and set a BPX on that instruction. Scroll up and you'll see its the MOV DX,[ESP+04] at 7FEC4BDD.
  199. So I continued. What could I do now ?
  200. Remember, we only took 1 character from the stack, so I wanted to figure out what piece of code is actually calling the convertion routine from ASCII to a decimal string. This is where I used my latest BPX.
  201. I went back to the text box, entered another digit, and after the chars were stored on the stack, and I went on, I procedure stepped on all the calls. Just a reminder, when you procedure step on a call, all sticky BPs are activated, so I was expecting my BPX on the MOV DX to stop execution in one of those procedure steps, and it happened, in the following piece of code, which I immediately recognized as the part I returned to after the convertion routine.
  202. Here is the code with all its glory :
  203.  
  204. 0040559E    LEA ECX,[EBP=34]
  205. 004055A1    LEA EDX,[EBP-44]
  206. 004055A4    LEA EAX,[EBP-0090]    ; look at these stack offsets in all calls...
  207. 004055AA    PUSH ECX
  208. 004055AB    PUSH EDX
  209. 004055AC    PUSH EAX
  210. 004055AD    CALL [MSVBVM50!__vbaVarCat]        ; <<< here's the winner!! :)
  211. 004055B3    LEA ECX,[EBP-54]
  212. 004055B6    PUSH EAX
  213. 004055B7    LEA EDX,[EBP-00A0]  ; look again..
  214. 004055BD    PUSH ECX
  215. 004055BE    PUSH EDX
  216. 004055BF    CALL [MSVBVM50!__vbaVarCat]        ; <<< another winner!!
  217. 004055C5    PUSH EAX
  218. 004055C6    LEA EAX,[EBP-64]
  219. 004055C9    LEA ECX,[EBP-00B0]  ; look again..
  220. 004055CF    PUSH EAX
  221. 004055D0    PUSH ECX
  222. 004055D1    CALL [MSVBVM50!__vbaVarCat]        ; <<< another winner!!
  223. 004055D7    PUSH EAX
  224. 004055D8    LEA EDX,[EBP-74]
  225. 004055DB    LEA EAX,[EB-00C0]    ; look again, I promise this is the last time :)
  226. 004055E1    PUSH EDX
  227. 004055E2    PUSH EAX
  228. 004055E3    CALL [MSVBVM50!__vbaVarCat]        ; <<< and yet another winner!!
  229. 004055E9    MOV EDX,ECX
  230. .
  231. .
  232.  
  233. The winner call at 55AD is the place I returned from after the first convertion, and this is where the BPX on the MOV DX was first triggered, so its safe to assume somewhere in that call there's a call to the convertion algorythm. We see three more calls just like it, and we already figure out that they do the same. But we are using 5 characters out of the username, right?
  234. Well, the code does'nt lie, and we will see that the serial is actually being composed of 4 digits being processed.
  235. But that's for later. For now, these convertions store the result somewhere, and actually, somewhere there's a pointer in all that pushes and calls, and I don't actually remember which one it was, but it does'nt matter.
  236. Before we go on, take a look at all the places I told you to take a look hehe, and you'll see that the offsets inside the stack are exactly 16 bytes increments of each other, which strengths what we thought about the letters being 16 bytes apart from each other.
  237.  
  238. I found multiple numbers in the address space where my username was originally dwelling , and I recognized them as the end results of the convertion.
  239. So now I had to use my brain, or whats left of it hehe. Normally in a situation like this, we try looking for the comparison between the serial we've entered and the 'right' serial.
  240. However, its so evident that these numbers are somehow connected to the serial that I didn't have to look much further.
  241. Here is a snippet of code not far after the above one:
  242.  
  243. 00405629    LEA EDX,[EBP-24]
  244. 0040562C    LEA EAX,[EBP-00A0]
  245. 00405632    PUSH EDX
  246. 00405633    PUSH EAX
  247. 00405634    CALL [MSVBVM50!rtcMidCharVar] ; what a surprise, look at ECX :)
  248. 0040563A    LEA EDX,[EBP-00A0]
  249. 00405640    LEA ECX,[EBP-24]
  250.  
  251. Ok so I was tracing and procedure stepping, and checking all the pointers I could, especially those that EAX holds after a CALL to any API, because EAX holds the return value.
  252. An example would be the __vbaVarMove API, which I found out that the value of EAX when we return from the function (return value), is actually a pointer to the variable's new location.
  253. So I checked the return values of the call to rtcMidCharVar, and EAX held nothing important, but ECX was changed also, and doing a D ECX gave me a nice surprise.
  254. I saw a serial number just staring me in the face after I entered that command. So this might be a good API to pay attention to when cracking VB programs.
  255. I immediately saw that the sequence of numbers are combined of all the convertions of all characters somehow but I didn't know how. I tried entering that number, maybe with a little changes, till I registered the crackme for the first time.
  256. Now I knew I was in the right place, I only had to figure out the pattern or the way the serial is being put together from the convertion numbers I found before. It wasn't as hard as it turned out, and I didn't have to trace through anymore code to see it, although I could.
  257. With a little experimenting, I managed to register the program several times for different usernames. I then tried to see a pattern and I found 1.
  258. If you can't see it after registering a few times, here it is :
  259.  
  260. The 4 characters are converted into strings of their ASCII value in decimal, so a capital 'A' would be 41h if I'm not mistaken, which should be 65 in decimal.
  261. for the name lord soth I got : 108 (l), 111 (o), 114 (r), 100 (d), and that was it, since the crackme uses 5 characters.
  262. The serial was not just adding them all up or something. The program created a string combined of all of them, in my case it would look like this : 081111114100
  263. Then it would toss out the left digit (no matter what it was), and take the next 10 digits. In the case where there were'nt 10 left in the string, it would be the rest, so it was either 10, or what was left till the end of the string, whichever came first. That was the serial number.
  264. Now we can easily immitate it in a serial key generator, but look one more time at the code following and you'll see an interesting thing worth exploring (assuming one has time for casual exploration of compiled code).
  265. Take a look :
  266.  
  267. 0040565F    CALL [__vbaObjSet]
  268. 00405665    MOV ESI,EAX
  269. .
  270. .
  271. .
  272. 00405674    TEST EAX,EAX
  273. 00405676    JGE    0040568A
  274. .
  275. .
  276. .
  277. 0040568A    MOV EAX,[EBP-78]    ; do a D EAX and see what you get..
  278. 0040568D    XOR ECX,ECX
  279.  
  280. In this piece of code, which only the last part is relevant, you can do a D EAX, and in there you'll see the serial number you entered in the serial text box (remember all those digits you entered to try to catch the calculation routine ??).
  281. A little farther down the road you'll come accross a call to the DLL in the form of checking a substraction. This is where I suspect the actual comparison is at, but since we no longer need it, we won't go into this since I'm lacking some time and can't afford to go after everything in a target protection, as much as I would like to.. :(
  282.  
  283. SO, we're done with this calculation routine. It was a nice try, and a challenging 1 to crack, because so much stuff can confuse us in here. There is only 1 thing that annoys me with this protection, and its the use of the character's ASCII value as a base for the serial number.
  284. Once this is known, its pretty easy to guess and see the pattern. If it wasn't the ASCII value of the character that made up the serial, I would have to go deeper and look further till I find what makes the serial up, and it might even require me to go after the comparison.
  285. The fact that it was a plain ASCII value of characters saved me lots of work, and this is what we have to avoid when protecting (but why protect, only reason to do so might be so that your programs won't fall into the wrong hands and every lamer out there will have a powerful tool you might have coded yourself).
  286.  
  287. All in all it was a good and fun crack to make, and its just half of it. In summery, we saw that so much use of the stack (and I mean lots of it!!, so ineffective!), and so many calls can make our lives harder, but with a little zen, and a little patience, all will be revealed.
  288. Most programmers don't go to half this length to hide their protections, but this is a good lesson nonetheless.
  289. That ends the direct approach for cracking this exercise, I'll now go over to the key file reconstruction part, which is more interesting actually.
  290.  
  291. The key file reconstruction approach to cracking this exercise
  292. =------------------------------------------------------------=
  293.  
  294. Ok folks, so far we've been doing stuff we already know and done about a ziliion times (hmm why do them programmers keep at it if they know serial cracking is so easy?? hehe).
  295. Now we'll try to approach the problem a different way. As you may or may not recall, when we analyzed this crackme with FileMon, we saw it was looking for a file called 'reginfo.key' and it didn't find it, so we assumed it is created after we register the crackme. We were right of course, and after registering it, it did create a file called reginfo.key in the crackme's directory.
  296. So, now we know that, but lets assume for a sec we don't. Lets assume we only know the name of the file and that's it. From that we need to do the following steps:
  297.  
  298. 1. How the program read the file ?
  299. 2. What does the program read and where is it stored in memory ?
  300. 3. Try faking a serial, any serial, and see what the program is doing with it.
  301. 4. Find the calculation and comparison routines.
  302. 5. After we know how it is being calculated, creating a key file is nothing :)
  303.  
  304. And that's about it actually. One note, we will soon discover that we don't need to find the comparison in this crackme, because its just like the previous part. We sniffed out the serial and we didn't need to go after any compares of strings, checksums, etc.. This is the case here as well, and soon we will see that.
  305.  
  306. 1. How does the program read the file ?
  307. ---------------------------------------
  308. Ok, so we need to find out how this crackme reads the info. Normally in these situations a program will use one of the disk access API's. This could be several things:
  309. CreateFileA, ReadFileA, _lread, _lopen, etc.. and you get the idea, there are several usable API calls (although the _l series is intended for 16 bit compatibility).
  310. Now , I've tried going after those, and CreateFileA and ReadFileA ARE being used by the crackme to read the key file, but not in the way we would expect. We need to remember that this is VB we're talking about. VB is NOT an efficient programming language, in fact its far from having any efficiency. What it is , is rather convenient, maybe 1 of the most convenient languages available.
  311. The fact that this is VB means that every use of the kernel is handled through calls to the VB runtime module, or Virtual Machine (MSVBVM.DLL). When I tried going after CreateFileA, I didn't find any case of reginfo.key in any of the parameters that the program is supposed to be passing, but that was basically because CreateFileA has been called FROM a VB function.
  312. That was my first guess, and I'm pretty sure I'm right. Even more, the file name was xxx.tmp in the Windows\Temp directory, which the program creates in runtime. This file probly holds some stuff that the VB functions are using somehow, but is not too relevant for our case.
  313. The bottom line here folks is this: trying to crack VB programs by intercepting calls to the operating system (KERNEL, USER...), will NOT work, because everything is handled within the MSVBVM DLL file.
  314. This is not good for us because we're used to rely on them to land in the protection schemes, and now we don't wanna trace through useless code inside some VB function (and god knows theres LOTS of it in those functions!! hehe).
  315. Anyway, what will come to our rescue here is the fact that VB programs are composed of calls to functions, and some code in between the calls. The calls generally provide services for our VB program, but not the way C programs do it. Here the program uses the VB functions as part of their runtime mechanism, or calculation or whatever task at hand.
  316. This means that VB functions must be sometimes reversed to some extent in order to find what we are looking for. A good example would be the function we reversed earlier, which takes a number and converts it to a string.
  317. So the VB program is full of calls to the VB DLL, and any disassembly will easily show that. Now, we can't use SmartCheck on this crackme, because it detects SmartCheck (again this detection can be cracked as well, but we don't need to), but what we can do is use a disassembler or a program like PE browse to look at the imported modules for this program.
  318.  
  319. So I went ahead and did that, and looked for anything that will handle files, and behold!, there are functions that handle files:
  320. __vbaFileOpen and __vbaInputFile are being used by the crackme to read info from files.
  321. Ok, so I set my BPX's on those two API's, and this is where I found myself:
  322.  
  323. :u 404657 l 47
  324. 023F:00404657  57                  PUSH    EDI
  325. 023F:00404658  6A01                PUSH    01
  326. 023F:0040465A  6AFF                PUSH    FF
  327. 023F:0040465C  6A01                PUSH    01
  328. 023F:0040465E  FF15D4814000        CALL    [MSVBVM50!__vbaFileOpen]
  329. 023F:00404664  8B1DE0814000        MOV     EBX,[MSVBVM50!rtcEndOfFile]    ;now here <<<
  330. 023F:0040466A  8B3DB4814000        MOV     EDI,[MSVBVM50!__vbaInputFile]
  331. 023F:00404670  6A01                PUSH    01
  332. 023F:00404672  FFD3                CALL    EBX
  333. 023F:00404674  6685C0              TEST    AX,AX
  334. 023F:00404677  7525                JNZ     0040469E
  335. 023F:00404679  8D45D4              LEA     EAX,[EBP-2C]
  336. 023F:0040467C  50                  PUSH    EAX
  337. 023F:0040467D  6A01                PUSH    01
  338. 023F:0040467F  68941F4000          PUSH    00401F94
  339. 023F:00404684  FFD7                CALL    EDI
  340. 023F:00404686  83C40C              ADD     ESP,0C
  341. 023F:00404689  8D8D74FFFFFF        LEA     ECX,[EBP-008C]
  342. 023F:0040468F  51                  PUSH    ECX
  343. 023F:00404690  6A01                PUSH    01
  344. 023F:00404692  68941F4000          PUSH    00401F94
  345. 023F:00404697  FFD7                CALL    EDI
  346. 023F:00404699  83C40C              ADD     ESP,0C
  347. 023F:0040469C  EBD2                JMP     00404770
  348.  
  349. After using P RET (F12 for me), to return to the caller routine, I was at offset 00404664, right after the call to the FileOpen function. The return value was 200h, and my initial guess is that this is the file handle that the function returns, just like in the normal APIs.
  350. Since I saw there is a second VB function called __vbaInputFile, I assumed that OpenFile is used to open the file, and InputFile is used to read the contents of the file, so I stepped over the InputFile call and what a wonder: after the call to rtcEndOfFile at offset 00404672, ECX held a pointer to a location in the crackme's memory space, so I did a D ECX and the name Lord Soth and my fake number were both there just a bit ahead :)
  351. Ok, before we go on, lets ponder on this for a second or two. Looking down a bit ahead, we see that there is a call to a function that closes the file (not listed in the snippet). This means only one thing, and that is the program uses the above functions to read all the file and store it in memory untill we reach an end of file marker.
  352. Why is this important for us you ask ? well, since there are lots of way to read stuff from files, we need to find out what exactly is it that we're reading. Programs can use only a part of a file, from offset XXX and read a length of bytes, or whatever. Another way is using the famous API call GetPrivateProfileStringA. We all know that one, it takes an ASCII string from an ini file specified by us, and it also looks for a specific portion of the file.
  353. For GetPrivateProfileStringA, we'll have to look at the passing parameters in order to see what we're looking for. For example :
  354.  
  355. Lets assume for a sec that a program is using GetPrivateProfileStringA, and is pushing the file name, the section name and the key name, so the portion of the file that would interest us would look like this :
  356.  
  357. [Section name]
  358. Key name= Key value
  359.  
  360. Now it looks for the section specified in the section-name parameter, inside it looks for the key name and it gets the key value.
  361. This is one way of reading info from files and this is what key file reconstruction is all about. You use the program code you reverse to determine what exactly is it that you are supposed to write in the first place. In our example above, I will create a file with the file name I got from the parameters, inside it'll have a section, key name and key value, all according to the parameters passed to the API (you can view them using D XXX).
  362. Display the memory pointed to by the PUSH instructions, because these pushes are pointers to null terminated strings. That way I'll create my file, and I'll format it the way the program expects it to be, and the only thing I can change is the key value, which I don't know, so I'm putting something like 12345, and you get the picture.
  363. So we reversed it and we know what to put in the file. In our exercise we'll need to write our user name, and in the 2nd line our serial number. We know this because the program reads the whole file in 1 go and stores it in memory.
  364. This completes steps 1 and 2 , and these two steps are what key file reconstruction is all about.
  365. The rest is just the same as always, you have the name and serial in memory and you crack it the way you already know.
  366. I won't describe the whole process all over again, because we already did that in the previous section (yes it is the same type of algorithm).
  367. How did I find this out ? look at the following code, which dwells a little down the road:
  368.  
  369. :u 404715 l 32
  370. 023F:00404715  6A01                PUSH    01
  371. 023F:00404717  8D55D4              LEA     EDX,[EBP-2C]
  372. 023F:0040471A  52                  PUSH    EDX
  373. 023F:0040471B  8D8534FFFFFF        LEA     EAX,[EBP-00CC]
  374. 023F:00404721  50                  PUSH    EAX
  375. 023F:00404722  FF1500824000        CALL    [MSVBVM50!rtcLeftCharVar]
  376. 023F:00404728  8D8D34FFFFFF        LEA     ECX,[EBP-00CC]
  377. 023F:0040472E  51                  PUSH    ECX
  378. 023F:0040472F  8D9550FFFFFF        LEA     EDX,[EBP-00B0]
  379. 023F:00404735  52                  PUSH    EDX
  380. 023F:00404736  8B3DC4814000        MOV     EDI,[MSVBVM50!__vbaStrVarVal]
  381. 023F:0040473C  FFD7                CALL    EDI
  382. 023F:0040473E  50                  PUSH    EAX
  383. 023F:0040473F  8B1D48814000        MOV     EBX,[MSVBVM50!rtcAnsiValueBstr]
  384. 023F:00404745  FFD3                CALL    EBX
  385.  
  386. This is very familiar to me, this takes a char and converts it to ASCII value and then, a little more ahead there is a CALL ESI (I'll let you locate it). Now we don't see any MOV that loads any value to ESI, but ESI holds a value of a function, so lets do :
  387. U ESI
  388. We'll see it points to __vbaVarMove. This is exactly the equivalent part of what we reversed earlier, so I won't repeat it in much detail. If I approached this problem using the key file reconstruction method in the first place, I would of course have to go through all the process of breaking on the read of our username.
  389. Since we already did that, lets go on to the next step. This whole snippet and a bit down ahead takes 5 chars and put them on the stack, in a similar fashion to the one we saw earlier.
  390. Now comes the part where we reversed the VB function that turns ASCII into their value equivalence in hex , but in a string format.
  391.  
  392. :u 404989 l 54
  393. 023F:00404989  8D4DB4              LEA     ECX,[EBP-4C]
  394. 023F:0040498C  51                  PUSH    ECX
  395. 023F:0040498D  8D5594              LEA     EDX,[EBP-6C]
  396. 023F:00404990  52                  PUSH    EDX
  397. 023F:00404991  8D8534FFFFFF        LEA     EAX,[EBP-00CC]
  398. 023F:00404997  50                  PUSH    EAX
  399. 023F:00404998  8B1DC8814000        MOV     EBX,[MSVBVM50!__vbaVarCat]
  400. 023F:0040499E  FFD3                CALL    EBX
  401. 023F:004049A0  50                  PUSH    EAX
  402. 023F:004049A1  8D4D84              LEA     ECX,[EBP-7C]
  403. 023F:004049A4  51                  PUSH    ECX
  404. 023F:004049A5  8D9524FFFFFF        LEA     EDX,[EBP-00DC]
  405. 023F:004049AB  52                  PUSH    EDX
  406. 023F:004049AC  FFD3                CALL    EBX
  407. 023F:004049AE  50                  PUSH    EAX
  408. 023F:004049AF  8D8564FFFFFF        LEA     EAX,[EBP-009C]
  409. 023F:004049B5  50                  PUSH    EAX
  410. 023F:004049B6  8D8D14FFFFFF        LEA     ECX,[EBP-00EC]
  411. 023F:004049BC  51                  PUSH    ECX
  412. 023F:004049BD  FFD3                CALL    EBX
  413. 023F:004049BF  50                  PUSH    EAX
  414. 023F:004049C0  8D9554FFFFFF        LEA     EDX,[EBP-00AC]
  415. 023F:004049C6  52                  PUSH    EDX
  416. 023F:004049C7  8D8504FFFFFF        LEA     EAX,[EBP-00FC]
  417. 023F:004049CD  50                  PUSH    EAX
  418. 023F:004049CE  FFD3                CALL    EBX
  419. 023F:004049D0  8BD0                MOV     EDX,EAX
  420. 023F:004049D2  8D4DC4              LEA     ECX,[EBP-3C]
  421. 023F:004049D5  FFD6                CALL    ESI
  422. 023F:004049D7  8D8D14FFFFFF        LEA     ECX,[EBP-00EC]
  423.  
  424. And this will do the trick. Remember the calls to __vbaVarCat we found earlier ? those that in them we reversed the converting of ASCII to digits ?, well here they are again, doing the same thing.
  425. And again we'll see that the same type of algorithm is used to create the final serial.
  426. Here is the final and most interesting part:
  427.  
  428. :u 404a0b l 25
  429. 023F:00404A0B  8D8D34FFFFFF        LEA     ECX,[EBP-00CC]
  430. 023F:00404A11  51                  PUSH    ECX
  431. 023F:00404A12  6A02                PUSH    02
  432. 023F:00404A14  8D55C4              LEA     EDX,[EBP-3C]
  433. 023F:00404A17  52                  PUSH    EDX
  434. 023F:00404A18  8D8524FFFFFF        LEA     EAX,[EBP-00DC]
  435. 023F:00404A1E  50                  PUSH    EAX
  436. 023F:00404A1F  FF1588814000        CALL    [MSVBVM50!rtcMidCharVar]
  437. 023F:00404A25  8D9524FFFFFF        LEA     EDX,[EBP-00DC]
  438. 023F:00404A2B  8D4DC4              LEA     ECX,[EBP-3C]
  439. 023F:00404A2E  FFD6                CALL    ESI
  440.  
  441. Remember that ? after the call at 00404A2E, ECX holds a pointer to the address space of the crackme, using a D ECX we see the serial just staring at our face, so this is the end of it actually. All those code snippets come one after the other, with a bit of reoccurances and some extra code, but you get the idea.
  442. Our final conclusion is that the whole difference is the fact that in the direct approach we read each char and then worked on that, and here we read the whole string into memory, from a file, and worked the same way afterwards.
  443. In essence, thats what differs between file reconstruction to normal serial sniffing. What we just did is actually steps 3 and 4, so now we only need to code a serial gen and we're done with this baby. One side note, notice that the same algorithm is used, but the way it looks in ASM is a bit different between the direct approach's code and this one, and this is because Vol decided to write it more than one time , maybe to throw us off :)
  444.  
  445. Other ways of cracking this exercise
  446. =----------------------------------=
  447.  
  448. Well, there is not much point in thinking of another way, but if you want to you can always look for the part that re-enables the Register button and work your way from there, maybe patch a conditional jump and so on.. Only reason that might stop you is the fact that the enabling is again handled through the VB fucntions, and since this approach is mostly dead-listing oriented, and we only see the calls to the VB functions, it will mean we'll have to look both in the disassmebly of the crackme and in the disassembly of the MSVBVM DLL. Clearly thats too much for us, so I'll suggest/stick to the run-time approach of cracking.
  449.  
  450. Coding the serial, and explanation of the source code
  451. =---------------------------------------------------=
  452.  
  453. Ok so if you survived this long , you should know how the serial gets calculated, and now its time to code a serial gen. Pick your favourite language (mine is ASM), and work on it.
  454. I will explain what we need to do basically:
  455.  
  456. 1. Receive a user name.
  457. 2. Cut it down to 5 chars, or just use 5 chars from it.
  458. 3. Turn each char hex ASCII value into a decimal string of digits.
  459. 4. Put them all one after another.
  460. 5. Cut out the left-most digit, and use all the rest up to a max of 10 digits.
  461.  
  462. Look at the file called ID3-ser.asm for the following line:
  463.  
  464. "; get the user name from the user, of course.. who did u think? :)"
  465.  
  466. Up to this line all I did was clear the screen, print some strings etc.. Now I'm getting to the part where we read the user name.
  467. KBread is a loop that takes a char from the keyboard. If it's anything but an Enter or Delete, it will store it in memory. If its an Enter we jump out of the loop, if its a Del, we call a proc called DelChar that will decrease the string pointer, increase the length pointer (it is NOTed so it'll be decreased in effect), and move the cursor 1 position to the left on the screen. Note that if there is nothing to delete (we check this by CX), it will not move the cursor backwards and will reset the pointer of the string to its original value.
  468. If everything is cool and we store chars, we press Enter and we're out of that Loop, and into the part that starts with "CreateUserName".
  469.  
  470. The first thing we do in this part is check the string's length. If its 0, we bug out of the program crying because there is nothing to work on. If its not zero, we proceed. We initialize the pointer to the result string and the counter to 5 chars and we do the loop called "calc" which will take a char, and call Num2Str. Each call to num2str will convert the number into a decimal string and store that value into the memory pointed to by DI (the pointer to the end string). Then we loop this whole thing till we're done with 5 chars and we have a serial string.
  471.  
  472. The part following that loop gets the length of the string (could have done it better but I was too lazy to changed it afterwards hehe), and compares it to 10. Notice the INC SI before the check against the 0 (NULL). This is because we need to trim the left-most digit, so we actually start from the second digit in the string. We get the string length and we compare it to 10 as I said. If its lower or equal to, everything is fine. If its greater, we don't want more than 10, so we set the length to 10 and store it away in memory for later.
  473. After that we only need to print it, so we set everything up (cursor position and all), and we print is using the length we just stored, and walla, we're done! :-)
  474.  
  475. Then the program exits and we are finished with this nicely done exercise. The source is there, just look at it if you like. The serial has been assembled using TASM 5 (notice the annoying .startup and .exit directives hehe). Feel free to use it for your own personal use (what could you use it for anyways ? hehehe).
  476. I'd like to thank Volatility for this crackme and also Eternal Bliss for the anti-code we had in there.
  477. I hope you enjoyed this tutorial/solution and learned a thing or two, I know I sure did, mostly about how to approach VB cracking.
  478.  
  479. Well, thats it folks, cya next time..
  480.  
  481. Lord Soth , ICQ # 5178515
  482.  
  483.  
  484.  
  485.  
  486.  
  487.