home *** CD-ROM | disk | FTP | other *** search
/ Cracking 2 / Cracking II..iso / Texty / crackme / TEST2TUT.TXT < prev    next >
Encoding:
Text File  |  1998-01-27  |  13.4 KB  |  315 lines

  1.                         How to crack TEST2 by LordByte
  2.  
  3.                                    by Crook
  4.                                27 January 1998
  5.  
  6. Introduction
  7.  
  8.      TEST2  is  the  second TEST in the series of CrackMes issued by LordByte.
  9. The  higher  number  is  the harder CrackMe is. As you can expect TEST2 is not
  10. really  difficult. If you feel it's too easy for you just go ahead and try the
  11. TEST7.
  12.  
  13. Where you can get it
  14.  
  15.      You  can  get  it  from http://crackme.home.ml.org together with zillions
  16. (well, nearly ;) other ones.
  17.  
  18. Cracking
  19.  
  20.      At  first  I  must  say  you have to know assembler before trying to read
  21. this.  I'll present a complete disassembly of this little piece of code but to
  22. fully  understand  what  I'm talking about you must know what those magic MOVs
  23. and XORs are.
  24.  
  25. Let's go
  26.  
  27.      (Below  is  the  full source code of TEST2, produced by IDA - Interactive
  28. DisAssembler.  If  you want to crack more & more progs you should get it. More
  29. details  can  be  found  on great FraVia's site: http://fravia.org and the IDA
  30. itself can be found almost everywhere on the Web).
  31.  
  32. ------------------------------------------------------------------------------
  33.                 jmp     short $+2
  34.                 int     3               ; Trap to Debugger
  35. ------------------------------------------------------------------------------
  36.      This  is  just  a  typical  start of LordByte's program ;) The first jump
  37. which  is  effectively  useless.  The next instruction is to stop the debugger
  38. execution  at  this point. It's very useful to us because the original COM was
  39. pcaked  using  PKLITE and we are able to find entrypoint without unpacking the
  40. program.
  41.  
  42. ------------------------------------------------------------------------------
  43.                 mov     ax, 900h
  44.                 mov     dx, offset intro_str
  45.                 int     21h             ; DOS - PRINT STRING
  46.                                         ; DS:DX -> string terminated by "$"
  47.                 mov     ax, 0A00h
  48.                 mov     dx, offset password
  49.                 int     21h             ; DOS - BUFFERED KEYBOARD INPUT
  50.                                         ; DS:DX -> buffer
  51. ------------------------------------------------------------------------------
  52.      This two DOS calls just print the intro message and get the password from
  53. the user. Nothing complicated yet.
  54.  
  55. ------------------------------------------------------------------------------
  56.                 mov     ax, 351Ch
  57.                 int     21h             ; DOS - 2+ - GET INTERRUPT VECTOR
  58.                                         ; AL = interrupt number
  59.                                         ; Return: ES:BX = value of interrupt vector
  60.                 mov     si, offset old_ofs
  61.                 mov     [si], bx
  62.                 mov     bx, es
  63.                 mov     [si+2], bx
  64.                 mov     si, offset interrupt
  65.                 mov     dx, si
  66.                 mov     ax, 251Ch
  67.                 int     21h             ; DOS - SET INTERRUPT VECTOR
  68.                                         ; AL = interrupt number
  69.                                         ; DS:DX = new vector to be used for specified interrupt
  70.                 jmp     short next
  71. ------------------------------------------------------------------------------
  72.      Now  we're  saving vectors of 1ch interrupt and setting it to our vector.
  73. This  interrupt  is  called  (normally) 18.2 times a second allowing you to do
  74. things 'in background'. It's used for some (lame ;) antidebugging later.
  75.  
  76. ------------------------------------------------------------------------------
  77. terminate:
  78.                 pop     bp
  79.                 add     sp, 8
  80.                 mov     ax, 4C00h
  81.                 int     21h             ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
  82.                                         ; AL = exit code
  83. ------------------------------------------------------------------------------
  84.      This  proc  just  ends  the  execution of this program. It's not executed
  85. after  playing with vectors because there is a 'jmp short next' jump to 'next'
  86. label below.
  87.  
  88. ------------------------------------------------------------------------------
  89. next:
  90.                 mov     si, offset password+2
  91.                 mov     ax, 6F25h
  92.  
  93. xor_loop:
  94.                 xor     ax, [si]
  95.                 cmp     byte ptr [si+1], 0
  96.                 jz      anti_loop
  97.                 cmp     byte ptr [si+1], 0Dh
  98.                 jz      anti_loop
  99.                 inc     si
  100.                 jmp     short xor_loop
  101. ------------------------------------------------------------------------------
  102.      Yo! This is the 'juicy' part of the prog. Let's look closer what is going
  103. on there. Moving 6f25h to AX and then XORing it by every char in the password.
  104. But  we  must  be careful here. Why SI's initial value is 'offset password+2'?
  105. Because  the  first  char at 'offset password' is how many chars can be put in
  106. the  buffer  (put there before DOS call. It's the buffer length incremented by
  107. one,  because  of 0Dh char on the end which also must fit into the buffer. DOS
  108. checks this value and gives a beep when you're trying to enter more chars then
  109. it's  stated  here.  In this program maximum password length is 11 = 0Ch - 1),
  110. and the second byte at 'offset password+1' is how many chars user actually has
  111. entered.  So  we have in AX value of 6f25h and let's assume we enetered 'test'
  112. as a password. Let's have a look at the memory:
  113.  
  114. offset password: 0C 04 74 65 73 74 0D 00 ....
  115.  
  116.      Notice that 74 65 74 73 = 'test'. SI points to 'offset password+2', which
  117. is  74  65 73 74. Now the AX get XORed (if you don't know this function go and
  118. read  about  in  some  book, some paper on the Web or something, otherwise you
  119. won't  understand  how the crack is being done). But with what? First guess is
  120. 7465,  but it's wrong. It gets XORed by 6574h. As you can see the order of the
  121. bytes is reversed. That's how it is on x86 processors. Don't ask me why, go to
  122. local  Intel  office  ;).  Then  after  INCrementing SI it's XORed by, yes you
  123. guessed it this time, 7365h.
  124.  
  125.      This process ends when the 0Dh is reached, what is the mark of the end of
  126. the  string  appended  to  the  string  by  DOS.  All right, ebough about this
  127. snippet, let's go further.
  128.  
  129. ------------------------------------------------------------------------------
  130. anti_loop:
  131.                 cmp     cs:anti_debug, 0
  132.                 jnz     anti_loop
  133. ------------------------------------------------------------------------------
  134.      This  to  lines  do a simple thing: try to stop an unexpirienced cracker.
  135. Remember  what  the  program  did  when it was playing vectors? It also set an
  136. interrupt  1ch  (which  is  called,  as  you  can remember, approx. 18 times a
  137. second)  to  'interrupt'  procedure.  It's presented below, but in fact it's a
  138. liitle but farther in the code.
  139.  
  140. ------------------------------------------------------------------------------
  141. interrupt:
  142.                 dec     cs:anti_debug
  143.                 iret
  144. ------------------------------------------------------------------------------
  145.      It  simply  decreases the anti_debug variable. Now you can imagine what's
  146. going on. In the debugger the interrupts are disabled, so the loop never ends.
  147. But  when  the prog is not traced the interrupts are enabled, so anti_debug is
  148. being decreased so it must reach 0 sometime, and the loop would end then.
  149.  
  150. ------------------------------------------------------------------------------
  151.                 mov     si, offset old_ofs
  152.                 mov     dx, [si]
  153.                 push    ax
  154.                 mov     ax, [si+2]
  155.                 mov     ds, ax
  156.                 mov     ax, 251Ch
  157.                 int     21h             ; DOS - SET INTERRUPT VECTOR
  158.                                         ; AL = interrupt number
  159.                                         ; DS:DX = new vector to be used for specified interrupt
  160. ------------------------------------------------------------------------------
  161.      After  the  anti  debugging  trick has ended the vectors are reset to the
  162. original values and the value calcuted using xor_loop is pushed to the stack.
  163.  
  164. ------------------------------------------------------------------------------
  165.                 push    cs
  166.                 pop     ds
  167.                 add     ax, 409h
  168.                 shl     ax, 3
  169.                 sub     ax, 4A3Eh
  170.                 xchg    ax, dx
  171.                 mov     ax, 300h
  172.                 mov     bx, 0FFFh
  173.                 mov     cx, 90h
  174.  
  175. mess_loop:
  176.                 add     ax, bx
  177.                 xchg    ax, bx
  178.                 loop    mess_loop
  179.                 push    bx
  180.                 push    ax
  181. ------------------------------------------------------------------------------
  182.      mess_loop  does  nothing  but computing in AX c440h and in BX c69fh. This
  183. values  are constant on every computer, under any debugger everytime. It could
  184. be replaced with simple MOV AX,C440h MOV BX,C69Fh.
  185.  
  186. ------------------------------------------------------------------------------
  187.                 mov     ax, 3503h
  188.                 int     21h             ; DOS - 2+ - GET INTERRUPT VECTOR
  189.                                         ; AL = interrupt number
  190.                                         ; Return: ES:BX = value of interrupt vector
  191.                 mov     ax, bx
  192.                 ror     ax, 4
  193.                 xor     ax, 7343h
  194.                 push    ax
  195. ------------------------------------------------------------------------------
  196.      This  one  gets  the  03h  interrupt  vector (it is used in debuggers for
  197. breakpointing).  It's  not used later so we can forget about it. It tries just
  198. to mess up our understanding of the program.
  199.  
  200. ------------------------------------------------------------------------------
  201.                 push    bp
  202.                 mov     bp, sp
  203.                 mov     bx, [bp+4]
  204.                 xor     bx, 0B816h
  205.                 xor     [bp+8], bx
  206.                 jnz     write_wrong
  207.                 mov     ax, 900h
  208.                 mov     dx, offset right_pass
  209.                 int     21h             ; DOS - PRINT STRING
  210.                                         ; DS:DX -> string terminated by "$"
  211.                 jmp     terminate
  212. ------------------------------------------------------------------------------
  213.      At  last is the end. What is done here? The BX is loaded with [BP+4]. WTF
  214. is that? Look at this memory location under the debugger. It's C440h (computed
  215. earlier).  BX  gets  XORed  with B816h so finally BX is 7C56h. [BP+8] is XORed
  216. with  BX and if it's zero it's the right password! If it's not the password is
  217. wrong.
  218.  
  219. ------------------------------------------------------------------------------
  220. write_wrong:
  221.                 mov     dx, offset wrong_pass
  222.                 mov     ax, 900h
  223.                 int     21h             ; DOS - PRINT STRING
  224.                                         ; DS:DX -> string terminated by "$"
  225.                 jmp     terminate
  226. start           endp
  227. ------------------------------------------------------------------------------
  228.  
  229. Cracking the schema
  230.  
  231.      What  do we know about the right password? The value computed by xor_loop
  232. equals  to  7C56h. (Only n XOR n = 0). Now, let's think what the xor_loop does
  233. with  password.  Assume  password  is  11  (0bh)  chars  long. Let's write the
  234. password as 'abcdefghijk' plus 0dh char. How AX is XORed with these values?
  235. AX  =      6F25h
  236. XORed by:   b a
  237.             c b
  238.             d c
  239.             e d
  240.             f e
  241.             g f
  242.             h g
  243.             i h
  244.             j i
  245.             k j
  246.            0d k
  247.            =====
  248. Should be: 7C56h
  249.  
  250. Let's  notice  the  following  about the XOR: having any given value (x) and a
  251. second value (y) there is just one value which fulfills the equation:
  252.        x xor w = y
  253. And we also know the value of w, which is:
  254.        w = x xor y
  255. If you don't know why, study so mathemathics.
  256. Now  back  to  our  table. Basing on the fact I described above it's enough to
  257. compute  'k'  and 'a' value having 'bcdefghij' given by the user! Writing such
  258. a program is not hard and in cracker's jargon it's called keymaker.
  259.  
  260. 6Fh xor (b xor c xor d xor e xor f xor g xor h xor i xor j) xor k xor 0dh = 7Ch
  261. 25h xor a xor (b xor c xor d xor e xor f xor g xor h xor i xor j) xor k = 56h
  262.  
  263. Let's assume
  264.  
  265. n = b xor c xor d xor e xor f xor g xor h xor i xor j
  266.  
  267. then
  268.  
  269. 6Fh xor n xor k xor 0dh = 7Ch
  270. 25h xor a xor n xor k = 56h
  271.  
  272. after some calculations you get:
  273.  
  274. k = 1Eh xor n
  275. a = 73h xor n xor k
  276.  
  277. because k = 1Eh xor n
  278.  
  279. a = 73h xor n xor 1Eh xor n
  280. a = 73h xor 1Eh
  281. a = 6Dh = 'm' !
  282.  
  283. What you see above is a proof that the first char is CONSTANT! Having given 9
  284. chars that would be put inside the password we know the first char is 'm' and
  285. we can compute the last! Here is the program which does the very thing:
  286.  
  287. program Test2KeyMaker;
  288.  
  289. var
  290.   S : string;
  291.   n : Byte;
  292.   a, k : Byte;
  293.   i : Byte;
  294.  
  295. begin
  296.   Writeln('Simple keymaker for TEST2 by LordByte');
  297.   Writeln('Written 27.1.1998 by Crook');
  298.   repeat
  299.     Write('Enter 9 char string: ');
  300.     Readln(S);
  301.   until Length(S) = 9;
  302.   n := 0;
  303.   for i := 1 to 9 do
  304.     n := n xor Ord(S[I]);
  305.   k := $1e xor n;
  306.   a := $6d;
  307.   Writeln('The password is: ', Chr(a), S, Chr(k));
  308. end.
  309.  
  310. Ending
  311.  
  312.      So  that's  it. You must admit that wasn't so difficult. Take a next TEST
  313. in a row a try to crack it. You surely will learn something.
  314.  
  315.                                                                          Crook