[MSLRH] v0.2 - manually unpacking tutorial

Monday, September 05 2005 @ 12:02 AM CEST

Contributed by: haggar

Level : newbie

[MSLRH] v0.2 - manually unpacking tutorial

MSLRH 0.2 is quite simple protector with one interesting function. It has option to
emulate Entry Point of SFX code with signature bytes from others protectors/packers
to fool PEiD. This tutorial will try to descrybe this protector.

1. Tools and requirements>

- OllyDbg 1.10
- LordPE
- ImpREC
- Target and packer itself is here in this archive [bin]
- Tutorial was written on windows XP, don't know will it work on other systems.

2. Studing protector

First, uncheck all exceptions in olly debug options except those in kernel32.

Open our target file keygebme1.exe in Olly and take look at next picture. Ups, before
that scan file with PEiD to see what PEiD will sad about packer. PEiD gives "SVKP 1.11 ->
Pavol Cerven". PEiD is wrong. Problem is that PEiD has small signature for SVKP so it can
be very easy emulated by another packer or even you can throw it in some of yours programs.
And this is just what this packer is doing. It can emulate couple well known packers /
protectors to confuse some unexperienced cracker. But it is not hard to find that MSLRH
is what we have under our hands. Lets see how it looks:

I think that comments on the picture are enough to understand. Tracing and examing is
hard with all this junk and I wrote simple script that will remove most of junk. Run that
script (but when you are asked to use second part of the script, select NO) and then we
will go to examne code. Now is code really easy to read and you can see that this packer
doesn't have much of it's code. First thing that we will meet is RTDSC check. After using
script, there will be lot of NOP's around here so I didn't paste whole code fom olly. Here
is interesting part:

00408081   RDTSC                         ; Get nuber of cycles.
00408083   PUSH EAX                      ; Store it to the stack.
00408084   RDTSC                         ; Get again number of cycles.
0040809D   SUB EAX,DWORD PTR SS:[ESP]    ; Substract last value with one that is stored in stack.
004080A9   ADD ESP,4                     ; Pop first value from stack to free stack.
004080C3   CMP EAX,0FFF                  ; Now, compare is that difference bigger than FFF.
004080D2   JBE SHORT keygenme.004080EF   ; If not, jump to normal program work, else go to
...                                        exception that will crush program.
004080DE   INT3
004080DF   MOV AX,0FE
004080EC   OUT 64,AX
004080EF   NOP                           ; Here lands good jump.

RDTSC returns number of clock cyles to EAX (that is some number of instructions that has been
passed until that moment in whole system), then that value is pushed to stack and again is
called RDTSC which gives to EAX new value. Then those values are substracted. If you trace
trough program slowly, that difference will be much bigger and packer will assume that it has
been debugged. But if you just place bp on good spot and run it, that difference will be below
FFF and packer will not notice us. Not something that will slow us. Ok, there is nothing
interesting now for a while so scroll all way down up to this place:

First packer prepares for exception handling. Ok, you may ask, what the hell I'm talking about now.
This is not tutorial about exceptions so here is a quick and short info:

"The idea of exception handling (often called "Structured Exception Handling") is that your
application installs one or more callback routines called "exception handlers" at run-time and
then, if an exception occurs, the system will call the routine to let the application deal with
the exception. The hope would be that the exception handler may be able to repair the exception
and continue running either from the same area of code where the exception occurred, or from a
"safe place" in the code as if nothing had happened."

Our packer has installed exception handler here

004089C4  MOV DWORD PTR FS:[0],ESP
and then it will make one exception, INT 3.
If we don't understand how to manually deal with this or any kind of exceptions, we will make
something wrong and crush our program. But we can pass this in a such way that we left program
and windows to deal with exception as they do it without presence of debugger. We can do that by
simoly pressing Shift+F9. But there we have one problem - we don't know from which point after
exception program will continue it's normall work further. Well, I know from which point it will
continue, but that is very hard to find in obfuscated programs and also, that point can be anywhere
in the code. But this packer is after removing junk really easy to follow and we can find our
point after scrolling down:

This works something like PUSH-POP. Maybe I'm not quite right, but newer mind that. Below this you
will see one JB and lot of some junky code below. Take look at picture and you'll get clear all.
Code below JB is encrypted and this small loop wil just decrypt it. Do it and there you will find
procedure that will get address of GetProcAddress API, then check is it breakpoint placed on it, and
then it will enter in second decryptor loop which will decrypt another part of code below it:

00409DC0  CMP BYTE PTR DS:[EDI],0CC         ; Checking for a bp on GetProcAddress.
00409DC3  JNZ SHORT keygenme.00409DCE
00409DC9  JMP keygenme.004089B1
00409DD2  CALL keygenme.00409DD7
00409DD7  POP EDI
00409DD8  ADD EDI,1C
00409DE2  XOR EAX,65
00409DE5  MOV BYTE PTR DS:[ECX+EDI],AL      ; Decrypting code.
00409DE8  INC ECX
00409DE9  CMP ECX,134
00409DEF  JB SHORT keygenme.00409DDE
00409DF1  POP EBX 
00409DF2  POP EDI 
00409DF3  MOV SEG?,WORD PTR DS:[ESI+2A]     ; Encrypted code which wait for decryption.

After code below is decrypted, trace to there. This code is little obfuscated but you will
see that it's purpose is to get some API addresses using GetProcAddress and that it checks
for bp on every API and then it calls it. Example:

00409E12  CMP BYTE PTR DS:[EDI],0CC       ; Bp check before CALL below.
00409E15  JNZ SHORT keygenme.00409E20
00409E17  XOR ECX,ECX
00409E19  XOR EDI,EDI
00409E1B  JMP keygenme.004089B1
00409E20  CALL EDI                        ; kernel32.GetProcAddress

There are two API's that protector uses against debugging:

OutputDebugStringA - that is API that will exploit security bug in Olly and crush it. Armadillo
uses this API too. Just NOP that CALL it in our example:

00409E40  CALL EAX      ; kernel32.OutputDebugStringA

IsDebuggerPresent - what to say, you know what this means. Use plugin now to hide olly, that's
the easiest way.

00409ED4  CALL EAX                                 ; kernel32.IsDebuggerPresent
00409ED6  PUSH EAX
00409EDF  JS SHORT keygenme.00409EF0

After that, there is another loop that decrypts another pice of protectors code:

00409F39  XOR EAX,61
00409F3F  INC ECX
00409F40  CMP ECX,1D28
00409F46  JB SHORT keygenme.00409F35
Let it decrypt and scroll waaaaaaaaaaay down to:

0040A8DE  OUT 64,AX
0040A8E1  POP EAX           
Does that look familiar? Yes, it is again those macro junk code, use script for cleaning
again (and again chose NO for second part). Good, everything is clear now. Again, we have
same RDTSC checks and one INT 3, just ignore all because this decrypted code there is nothing
new or interesting. Scroll down to the this place which was not encrypted from the very beggining
of protectors code:

0040C735  CALL keygenme.0040C73A 

That pice of code which is little obfuscated hides procedure that decrypts original code
section of packed program. Now is time to use second part of my de-junk script. After usage
(I removed NOP's to reduce size of text) you will get picture like below. This part calculate
some CRC walue from protectors code and then it decrypts code section of packed program with
that value. It also holds some stolen bytes. Check my comments:

0040C742  POP ECX                        ; ECX=40C742 - address where this opcode is.
0040C74D  SUB ECX,5                      ; ECX=ECX-5  - it's upper limit addres for CRC calculation,
0040C75A  XOR EBX,EBX                      it will not take bytes from this procedure for calculation.
0040C766  MOV EAX,4726                   ; EAX=4726 is number of bytes of protector code.
0040C775  MOV EDI,ECX                    ; EDI=that upper address/end of code.
0040C781  SUB EDI,EAX                    ; Remove size of code because it will exclude emulated EP for PEiD trick.
0040C78D  MOVZX EAX,BYTE PTR DS:[EDI]    ; Start taking bytes from beggining.
0040C79A  ADD EBX,EAX                    ; EBX will hold CRC summ.
0040C7A6  INC EDI                        ; Increase address.
0040C7B1  CMP EDI,ECX                    ; Check did we came to the end.
0040C7BD  JB SHORT keygenme.0040C78D     ; If not - continue calculating.

That was CRC calculation. Then it's gona start decrypting of code section of original program:

0040C7BF  MOV EDI,keygenme.00401000      ; Base address of code section.
0040C7C4  MOV ECX,3000                   ; Size of code section.
0040C7D3  MOVZX EAX,BYTE PTR DS:[EDI]    ; Take byte from code section.
0040C7E0  ADD BL,BH                      ; Little caclulating here....
0040C7E2  XOR BL,BH
0040C7E4  XOR AL,BL
0040C7F0  MOV BYTE PTR DS:[EDI],AL       ; And place decrypted byte instead encrypted one.
0040C807  DEC ECX 
0040C812  JNZ SHORT keygenme.0040C7C9    ; Do all that again until section is decrypted.
0040C814  CALL keygenme.0040C819
0040C819  POP ECX
0040C81A  SUB DWORD PTR DS:[ECX+15],EBX  ; This will calculate OEP place.
0040C81D  POPAD
0040C81E  PUSH EBP                       ; Stolen bytes!
0040C821  PUSH -1
0040C823  PUSH keygenme.004040B8
0040C828  PUSH keygenme.00401F30
0040C82D  PUSH 6262C0                    ; This should be OEP value.
0040C832  RETN

I think that you understand everything, but few words more:

Protector has decrypted code section, but with bad CRC value because we have remove junk
code so code section is destroyed and not decrypted. Also, this last PUSH 6262C0 holds value
that subtracted with CRC value gives OEP address of packed program.
This SUB DWORD PTR DS:[ECX+15],EBX will subtract it and we should get OEP to which will last
RETN throw. But again, because we modified code, that value is incorrect and our program will
crush just for that. Also that is not quite correct OEP because we have couple stolen opcodes
from OEP, but that is not problem. How to fix that? Read next chapter.

3. Unpacking

This is really easy job now when we know what we have to do. Restart target in olly and use
IsDebuggerPresent plugin to hide olly. Then set in the command line hardware breakpoint on
execution on OutputDebugStringA API (hardware because normal-software protector can find). It
goes like this "he OutputDebugStringA" and hit ENTER button. In Olly debug options IGNORE ALL
exceptions this time, we don't need them anymore. Run target and you should break in kernel
to our debug string API:

77E9B493 >PUSH 22C
77E9B498  PUSH kernel32.77E9BE60
77E9B4D9  RETN 4

On your machine this API can look different (maybe). Remove hardware bp and instead first opcode
in API, place ast one, the RETN 4. It should look like this:

77E9B493 >RETN 4
77E9B496  NOP
77E9B497  NOP
77E9B498  PUSH kernel32.77E9BE60
77E9B4D9  RETN 4

Now press Alt+F9 to return to protector's code and scroll down. Down you will find that CRC loop
and OEP jump that is not encrypted:

0040C80F  JMP SHORT keygenme.0040C812  

Now just place bp on abowe opcode and run target. You will break here. Trace once with F7:

0040C812  JNZ SHORT keygenme.0040C7C9
0040C814  CALL keygenme.0040C819
0040C819  POP ECX
0040C81D  POPAD
0040C81E  PUSH EBP          

Place new bp on abowe place and run target again. Now code section is decrypted and you will jump
to "false" OEP. Write somewere stolen opcodes:

0040C81E  PUSH EBP                  ; Stolen this and all below...
0040C821  PUSH -1
0040C823  PUSH keygenme.004040B8
0040C828  PUSH keygenme.00401F30
0040C82D  PUSH keygenme.0040140A    ; End of stolen bytes, this is value of false OEP
0040C832  RETN                      ; jump to false OEP

Execute last RETN, press Ctrl+A to analyse module and scroll little up:

004013FB  POP EAX                                  ;  keygenme.00401F30
004013FC  POP EAX                                  ;  keygenme.00401F30
004013FD  POP EAX                                  ;  keygenme.00401F30
004013FE  POP EAX                                  ;  keygenme.00401F30
004013FF  POP EAX                                  ;  keygenme.00401F30
00401400  POP EAX                                  ;  keygenme.00401F30
00401401  POP EAX                                  ;  keygenme.00401F30
00401402  POP EAX                                  ;  keygenme.00401F30
00401403  POP EAX                                  ;  keygenme.00401F30
00401404  POP EAX                                  ;  keygenme.00401F30
00401405  POP EAX                                  ;  keygenme.00401F30
00401406  POP EAX                                  ;  keygenme.00401F30
00401407  POP EAX                                  ;  keygenme.00401F30
00401408  POP EAX                                  ;  keygenme.00401F30
00401409  POP EAX                                  ;  keygenme.00401F30
0040140A  MOV EAX,DWORD PTR FS:[0]
00401410  PUSH EAX
00401411  MOV DWORD PTR FS:[0],ESP
00401418  SUB ESP,58
0040141B  PUSH EBX
0040141C  PUSH ESI
0040141D  PUSH EDI
00401421  CALL DWORD PTR DS:[;  kernel32.GetVersion

> Those POP EAX are junk and there you need to place stolen bytes. Then dump file with LordPE.
When you try to dump with LordPE, it will tell you that it can't grab process memory. Right
click in LordPE on our process, select "active dump engine", than "intelliDump" and "select".
Now dump it. You will get message that not all bytes could be dumped and that hey will be
substituted with zeros. Ok, save dump and open ImpREC. Reall OEP is 004013FB so write 13FB and
fix dump. And that's all!

4. Final words

It was not hard to unpack this version. I'm planing to write tutorial about MSLRH v0.32a which is
harder, but all in time. In the archive you will find script for de-junking and one for unpacking
this protector. Also you will find protector itself, packed target and unpacked target, and this
tutorial too.

Sorry for grammar mistakes and other errors.

Big greets to all folks on BIW and we gona see you in the next tutorial. Bye!

[ haggar ]