ARTeam Tutorial

by Shub-Nigurrath

Visit: http://cracking.accessroot.com | http://forum.accessroot.com

Beginner tutorial #4: unpacking and patching, a more complex case v.1.1


Information A little tutorial which continues the beginner series started by Gabri3l with some more explanations on an a program packed with aspack, IAT details and the Olly selfextracting function.
Target Super Video Joiner 1.8.0
Available http://www.witcobber.com/  or
http:/www.intechhosting.com/~access/ARTeam/tools/superjoiner_180.exe
Tools OllyDbg 1.10 (HideDebug and OllyDump plugin), Import Reconstructor 1.6, Diablo2oo2 Universal Patcher
Protection AsPack 2.12 with crippled IAT, Serial Protection
level Beginner
Category patching
Author(s) Shub-Nigurrath Feb 2005
Requirements Windows XP, Firefox 1.0 and above for best viewing & printing


1. Introduction


This tutorial is a follow-up of the series of three wrote by Gabri3l, available on our tutorial's site. I will suppose you read them before, because some the things he already said are given as known. Especially what I'm referring to is the last tutorial, the third one, arguing about a program protected with AsPack 2.11.

In this tutorial I will (ab)use another little program protected with AsPack, but which cannot be unpacked with the same method Gabri3l told in his already mentioned third tutorial. The target is Super Video Joiner, a nice program useful to join different videos into an unique final one, also DivXs.

As we will see later on, this target has a different Import Address Table -IAT- format which will require a little more advanced use of Import Reconstructor. I will also show you how to tell Olly to automatically unpack self-extracting applications (such those protected with aspack, upx or similar but not with asprotect, armadillo etc). The patching of the unpacked application is indeed, once dumped, very easy and will be covered in a few steps.

Lastly I will concentrate on the creation of a smart patch which will, hopefully, be valid for future versions: Gabri3l shown you how to use Dup (Diablo2oo2 Universal Patcher) using the Offset Patch, I instead will show you how to use the Search and Replace tab...

So, fasten your seatbelts ;-)



2. Unpacking the program


Understanding the program compression used
As usual the very first step of any cracking session is to understand what we are facing to! The first thing to do is to open PEiD and to see what it reports.

By the way, PEiD is not always 100% correct and sometimes report wrong results. Its authors for this reason included the possibility to use an external signature database, an external file inside its installation folder, called userrdb.txt.

This file contains all the additional signatures not already included into PEiD. These signatures are usually posted in the forums around, even our one, and you might take time to collect some of them to improve your PEiD accuracy. PEiD installer install a sample userdfb.txt file with no meaningful things inside, so often you need to find a real one around.

 

Anyway our case is very simple and AsPack is recognised by PEiD easily. You should already know that AsPack is a program's packer, not a program's protector, so it's main target is to reduce the whole exe image file's size, rather than to protect it. So its main characteristic is to be "re-entrant", which means that when the unpacking code finishes to unpack the program into the memory, it doesn't leave any trace and the program appears in memory as nothing happened (see also Gabri3l tutorials). This lead to the method suggested by Gabri3l in his tutorial, which consists in placing an hardware breakpoint on Dword on the stack, just after the first PUSHAD.

On the other hand protectors like AsProtect (of the same author of AsPack), instead do several things to the original executable's code and are "not re-entrant"; also when the program is uncompressed in memory, ready to execute,  it's code has been widely modified (its IAT modified so as to not rebuild it easily, its instructions modified to not follow the program's flow easily and so on).

Let Ollydbg unpack the program for us
Ok we can now open OllyDbg and unpack the program. Follow on your own, as an exercise, the method exposed in the third Gabri3l's tutorial, just to see if you have understood it clearly. Done?? Ok, so go on reading this..

In this document I will follow instead an alternative way, which is already implemented into OllyDbg and which is not so much known/used.

Goto into the OllyDbg settings and set them as in the picture below:

OllyDbg has an automatic Self Extractors (SFX) feature, which does the a trick similar (more complex actually) to what Gabri3l explained you. This trick is valid as I told for all the "re-entrant" program's packers or generally for all the self extractor executable. Ollydbg is able to do all the work for you and to stop at the right place!

Once you set your settings as above you are ready to load your target into Ollydbg (in this case the program is videojoiner.exe). There's not need to close and reopen Olly, it's enough to relauch the program, e.g. using CTRL-F2 if it was already opened.

Usually Ollydbg would have stopped at the first instruction of the AsPack unpacker (see below), which was a PUSHAD

004F8001 > 60 PUSHAD
004F8002 E8 03000000 CALL videojoi.004F800A
004F8007 - E9 EB045D45 JMP 45AC84F7


This time instead Ollydbg behaves differently and starts to analyze deeply the program and especially starts to trace it (see the left bottom part of the interface, there is a message "Tracing SFX.."). Let Ollydbg works till the process stops and you land at what Ollydbg believe could be the final destination of the self extracting procedure.

As you can see Olly also reports in the status bar a statistic of the work done:

If you haven't still done it press a CTRL-A to reanalyze the program, and to see clearly that we are in the real OEP of the program.

A note about Ollydbg: Ollydbg stores away (into the UDD file) the original OEP just found, so the next time we will launch the same program the OEP will be immediately found and Ollydbg will report that the SFX entry point (aka the OEP) has been "learned":

As you can see the point where we stopped this time is the same we would have obtained doing the process manually! Well, a little bit of work skipped!

Dumping the Process and Fixing the IAT
Now we are ready to dump the program as usually. Try, before reading on, to do as Gabri3l told you in the third tutorial, and you will see that ImpRec is not be able to get any valid import from the running process; there's no way to obtain a fixed dump operating as Gabri3l told you. Why? Well, we will see it in a moment.

First of all dump the program using OllyDump, uncheck Rebuild Import and take note of the OEP. In this case is: AF0A0.

I called the dump file, with a name of fantasy, dump.exe.

Now open Import Reconstructor 1.6 and open the process you are debugging from the processes list. I'll do it brief hoping you are already accustomed to this program, so the picture reports everything needed, and the number are the steps to follow.

Once you press the "IAT Autosearch" button ImpRec pops up to you this dialog message:

As you should already know this is the address where ImpRec beleives the IAT is (the IAT has a fixed structure so it's easily recognized by ImpRec).
Go forward to step 3 and 4 and you should get the point: the IAT cannot be reconstructed in this way!

We need to instruct ImpRec to find on it's own the APIs in the memory space of the process in a different way!

 

See how..
 

Right click on the white space of the interface, and you should get this contextual menu:

Select the "Get API Calls" and this is the final dialog which appears to you:


Before pressing Ok, do you understand what the program is going to do? It is going to scan all the CALL [X] instructions in the memory that goes from 0000000 to FFFFFFF. We will do the same process manually for just one call, to understand the work that ImpRec will do when you will press able to press ok.
Now leave ImpRec as it is and return to read a little of what happens behind the scenes.

Understanding what ImpRec will do
Switch to OllyDbg which is still running in background, with the process stopped at the OEP.

This is the situation you should see now:

004AF0A0 /. 55 PUSH EBP ; Real entry point of SFX code
004AF0A1 |. 8BEC MOV EBP,ESP
004AF0A3 |. B9 0B000000 MOV ECX,0B
004AF0A8 |> 6A 00 /PUSH 0
004AF0AA |. 6A 00 |PUSH 0
004AF0AC |. 49 |DEC ECX
004AF0AD |.^ 75 F9 \JNZ SHORT videojoi.004AF0A8
004AF0AF |. 53 PUSH EBX
004AF0B0 |. 56 PUSH ESI
004AF0B1 |. B8 B8ED4A00 MOV EAX,videojoi.004AEDB8
004AF0B6 |. E8 6573F5FF CALL videojoi.00406420
004AF0BB |. 8B35 6C204B00 MOV ESI,DWORD PTR DS:[4B206C] ; videojoi.004B3C14
004AF0C1 |. 33C0 XOR EAX,EAX
004AF0C3 |. 55 PUSH EBP
004AF0C4 |. 68 28F54A00 PUSH videojoi.004AF528
004AF0C9 |. 64:FF30 PUSH DWORD PTR FS:[EAX]
004AF0CC |. 64:8920 MOV DWORD PTR FS:[EAX],ESP
004AF0CF |. 6A 00 PUSH 0 ; /Title = NULL
004AF0D1 |. 68 38F54A00 PUSH videojoi.004AF538 ; |Class = "Super Video Joiner"
004AF0D6 |. E8 B97BF5FF CALL videojoi.00406C94 ; \FindWindowA
004AF0DB |. 85C0 TEST EAX,EAX
004AF0DD |. 76 14 JBE SHORT videojoi.004AF0F3


Go to one of the system calls spread around the code and see how it is done. For example I will follow the first one, FindWindowA, called at 004AF0D6.

Look the call: it is like CALL videojoi.00406C94, where 00406C94 is the called address. At the called address this what we find:

00406C94 $- FF25 04474B00 JMP DWORD PTR DS:[4B4704] ; user32.FindWindowA

the JMP at 00406C94 jumps directly into the user32's FindWindowA starting address. By the way when you are at 00406C94 look around, there are a lot of similar JMPs to the real system calls...

So let try to summarize: we have a CALL to an address (004AF0D6), which contains a JMP to another address (004B4704), so we can see the first called address as a memory pointer to the real called address, which in assembler is as an indirect call.

Its syntax generally is:

CALL [address_containing_the_real_addr_to_call]

or rather, using ImpRec notation, CALL [X].. What the ImpRec is still asking to you? "Get CALL [X] Scheme"...

For example when a registry contains an address of a subroutine to call the ASM instruction will be CALL [EAX], if EAX is the register.


But let take a short break to dig a little this specific argument, I think that could be interesting to know some details about how the calls to imported functions are generated from the compiler and why. Let's examine what the call to an imported API looks like. There are two cases to consider: the efficient way and inefficient way. In the best case, a call to an imported API looks like this:

CALL DWORD PTR [0x00405030]

If you're not familiar with x86 assembly language, this is a call through a function pointer. Whatever DWORD-sized value is at 0x405030 is where the CALL instruction will send control. In the previous example, address 0x405030 lies within the IAT.
The less efficient call to an imported API looks like this:
 

CALL 0x0040100C
........
0x0040100C:
JMP       DWORD PTR [0x00405030]
In this situation, the CALL transfers control to a small stub. The stub is a JMP to the address whose value is at 0x405030. Again, remember that 0x405030 is an entry within the IAT. In a nutshell, the less efficient imported API call uses five bytes of additional code, and takes longer to execute because of the extra JMP.

You're probably wondering why the less efficient method would ever be used. There's a good explanation. Left to its own devices, the compiler can't distinguish between imported API calls and ordinary functions within the same module. As such, the compiler emits a CALL instruction of the form
 

CALL XXXXXXXX
where XXXXXXXX is an actual code address that will be filled in by the linker later. Note that this last CALL instruction isn't through a function pointer. Rather, it's an actual code address. To keep the cosmic karma in balance, the linker needs to have a chunk of code to substitute for XXXXXXXX. The simplest way to do this is to make the call point to a JMP stub, like you just saw.

Where does the JMP stub come from? Surprisingly, it comes from the import library for the imported function. If you were to examine an import library, and examine the code associated with the imported API name, you'd see that it's a JMP stub like the one just shown. What this means is that by default, in the absence of any intervention, imported API calls will use the less efficient form.

Logically, the next question to ask is how to get the optimized form. The answer comes in the form of a hint you give to the compiler. The __declspec(dllimport) function modifier tells the compiler that the function resides in another DLL and that the compiler should generate this instruction
 

CALL DWORD PTR [XXXXXXXX]
rather than this one:
 
CALL XXXXXXXX
In addition, the compiler emits information telling the linker to resolve the function pointer portion of the instruction to a symbol named __imp_functionname. For instance, if you were calling MyFunction, the symbol name would be __imp_MyFunction. Looking in an import library, you'll see that in addition to the regular symbol name, there's also a symbol with the __imp__ prefix on it. This __imp__ symbol resolves directly to the IAT entry, rather than to the JMP stub.

So what does this mean in your everyday life? If you're writing exported functions and providing a .H file for them, remember to use the __declspec(dllimport) modifier with the function:
 

__declspec(dllimport) void Foo(void);
If you look at the Windows system header files, you'll find that they use __declspec(dllimport) for the Windows APIs. It's not easy to see this, but if you search for the DECLSPEC_IMPORT macro defined in WINNT.H, and which is used in files such as WinBase.H, you'll see how __declspec(dllimport) is prepended to the system API declarations.


Terminating the dumping process
It's time now to finish our dumping procedure and to have a fixed working dump ready to be patched, isn't it?

When you press OK in the ImpRec's dialog box discussed above, the program starts to work, and the final result is something which looks like below:

Press the "Show Invalid" button and you should see that there are still a lot on invalid values: are all calls that ImpRec wasn't able to associate to any system call.
No problem, it's obvious and we should have expected it: not all the calls of the type CALL [X] are pointing to the system and some of them are internal to the program, so it's obvious that ImpRec is not able to recognize all of them.

To eliminate them simply right click on one of them and select "Cut Chunk(s)" (see the contextual menu shown few pictures above).

Press "Show Invalid" again and this time you shouldn't get any invalid entry.

The ImpRec log window reports "Congratulation! There is no more invalid pointers, now the question is: Will it work?:-)"

 

Press Fix Dump and we will answer soon to this question ;-)

Obviously the answer is yes, it works!
 



3. Patching the Program

Now we have a working dump and we are ready to happily patch the application.

First of all study the protection: there are three limitations to the program, it cannot join more than two files, when you exit there is a messagebox and you cannot enter any registration code you like. All these limitations have very clear Bad Boy messages, which are essential grips to start reversing with Olly.

From earlier beginner tutorials of this series and of ARTeam's tutorials collection, you should have learnt that a Bad Boy is a great place from where to find references to the code to patch, you should also know how to search referenced strings in OllyDbg.

Following this method I found these three points where to patch the application. I will report them briefly without commenting them too much, because are very simple indeed (just some jmps patching). I also anyway reported the referenced strings I used to find the right point.

Patch1

Original Code:
004A7123 |. /75 14 JNZ SHORT videojoi.004A7139
004A7125 |. |A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A712D |. |74 0A JE SHORT videojoi.004A7139
004A712F |. |A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 |. |75 12 JNZ SHORT videojoi.004A714B
004A7139 |> \BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

Patched Code:
004A7123 . 90 NOP
004A7124 . 90 NOP
004A7125 . A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A . 8338 00 CMP DWORD PTR DS:[EAX],0
004A712D . 90 NOP
004A712E . 90 NOP
004A712F . A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 . EB 12 JMP SHORT videojoi.004A714B
004A7139 . BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

Patch2

Original Code:
004A8CE2 |. /75 18 JNZ SHORT videojoi.004A8CFC
004A8CE4 |. |A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A8CE9 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A8CEC |. |74 0E JE SHORT videojoi.004A8CFC
004A8CEE |. |A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A8CF3 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A8CF6 |. |0F85 52020000 JNZ videojoi.004A8F4E
004A8CFC |> \8D45 F0 LEA EAX,[LOCAL.4]
004A8CFF |. BA 28954A00 MOV EDX,videojoi.004A9528 ; ASCII "This trial version only can join less than 2 video files at a time,this limit only can be eliminated when it is registered!"
004A8D04 |. E8 E3B4F5FF CALL videojoi.004041EC

Patched Code:
004A8CE2 . 90 NOP
004A8CE3 . 90 NOP
004A8CE4 . A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A8CE9 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A8CEC . 90 NOP
004A8CED . 90 NOP
004A8CEE . A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A8CF3 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A8CF6 . E9 53020000 JMP videojoi.004A8F4E
004A8CFB . 90 NOP
004A8CFC . 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
004A8CFF . BA 28954A00 MOV EDX,videojoi.004A9528 ; ASCII "This trial version only can join less than 2 video files at a time,this limit only can be eliminated when it is registered!"
004A8D04 . E8 E3B4F5FF CALL videojoi.004041EC

Patch3

Original Code:
004AB0D4 . /75 12 JNZ SHORT videojoi.004AB0E8
004AB0D6 . |833D FC3C4B00 00 CMP DWORD PTR DS:[4B3CFC],0
004AB0DD . |74 09 JE SHORT videojoi.004AB0E8
004AB0DF . |833D 003D4B00 00 CMP DWORD PTR DS:[4B3D00],0
004AB0E6 . |75 0A JNZ SHORT videojoi.004AB0F2
004AB0E8 > \B8 FCB04A00 MOV EAX,videojoi.004AB0FC ; ASCII "This is a unregistered Version, Don't forget to register it."
004AB0ED . E8 327EF8FF CALL videojoi.00432F24

Patched Code:
004AB0D4 . 90 NOP
004AB0D5 . 90 NOP
004AB0D6 . 833D FC3C4B00 00 CMP DWORD PTR DS:[4B3CFC],0
004AB0DD . 90 NOP
004AB0DE . 90 NOP
004AB0DF . 833D 003D4B00 00 CMP DWORD PTR DS:[4B3D00],0
004AB0E6 . EB 0A JMP SHORT videojoi.004AB0F2
004AB0E8 . B8 FCB04A00 MOV EAX,videojoi.004AB0FC ; ASCII "This is a unregistered Version, Don't forget to register it."
004AB0ED . E8 327EF8FF CALL videojoi.00432F24

You have done it! I hope you understood what I simply pasted here, (sorry, but it's useful to make the whole tutorial shorter). From Ollydbg save the applied patches to a program and call it for exampl dump.patched.exe, then run it.

It works fine with no limitations or annoying nags and allows to join more than two sequences..
 



4. Creating the Patcher

Also this time we will use Diablo2oo2 Universal Patcher to create the patcher, it has an unique feature to be able to patch packed programs and specifically supports AsPack. The final patch so it will be able to work on the original installed file without having to dump it and reducing the distribution weight.

Anyway this time we will use a different method to create a patcher, the "Search and Replace".

Search and Replace, general considerations
This patching method searches through the target executable file for a specific byte pattern and when if finds it applies the corresponding new pattern. This operation is done blindly for all the occurrences of the specified byte pattern. So it's potentially dangerous if not done on a well thought pattern.
On the other hand if this pattern is well choosen and well formed there is an high probability that the same pattern will be present in future versions of the same program: what often changes among different program's versions are the relative offsets inside the program, used in JMPs, CALLs and MOVs and so on, but the code remains essentially the same (this happens because most often we are changing the registration routines which very often doesn't change, what changes are the features which we leave untouched). Anyone reading this, who cracked two different versions of the same program should easily realize this concept!

This is DuP Search and Replace form:

You should start entering the original byte pattern in 1, the the new one in 2, press the Add button in 3 and Check occourrence of "Search Bytes" in 4. The finally you shold see your result(s) in 5.

By the way, generally speaking if the byte pattern is not unique DuP let you also choose the occourrence number to patch, using the Occourrence box.

Choosing the byte patterns
Before to preceed I need to clarify a thing: each ASM operation has first of all its own op-code (an hex number representing it for the CPU), and then its operands. Ollydbg separates them for your convenience, so an instruction like:

A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]

has an op-code equal to A1 (the MOV EAX, DWORD PTR DS:[]), and an operand equal to 081E4B00 (which is 4B1E08 in reverse order).

Given this, we are ready to go further, to understand the way to proceed. For the sake of shortness, I will report here again the Patch1 wrote in Section #3 of this tutorial. I will do the complete steps only for this patch and for the other I will only give to you the final results so you can train yourself.

Original Code:
004A7123 |. /75 14 JNZ SHORT videojoi.004A7139
004A7125 |. |A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A712D |. |74 0A JE SHORT videojoi.004A7139
004A712F |. |A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 |. |8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 |. |75 12 JNZ SHORT videojoi.004A714B
004A7139 |> \BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

which in bytes is as follows:

75,14,A1,08,1E,4B,00,83,38,00,74,0A,A1,9C,1E,4B,00,83,38,00,75,12,BA,00,72,4A,00

the yellow part denotes the operands.
Now ask to yourself, what could change across different versions of the program; the answer is: the offsets, the addresses of the strings and of the MOVs...

Well, so cut these things away from the above byte pattern and substitute them with a couple of *

75,**,A1,**,**,**,**,83,38,**,74,0A,A1,**,**,**,**,83,38,**,75,**,BA,**,**,**,**

Thinking to operands a little more I can see that we can leave in place some of them, like the first, equal to 14, which is an offset to a relative jmp destination (means 14 bytes forward) because I can suppose that if the code changes it's position in next releases it will do as a whole block so the relative offset of 14 bytes is small enough to be sure that even the destination bytes of the jmp will be there. The operand of the instruction at 004A712A which is 00, is also for sure a think that will be still here in future releases.

So this lead to our final search string:

Original byte sequence:
75,14,A1,**,**,**,**,83,38,00,74,0A,A1,**,**,**,**,83,38,**,75,12,BA,**,**,**,**

The concept is: do not include offsets too far away from the peice of code you are going to patch, the other operands can be left.

The patched code is the follow:
004A7123 . 90 NOP
004A7124 . 90 NOP
004A7125 . A1 081E4B00 MOV EAX,DWORD PTR DS:[4B1E08]
004A712A . 8338 00 CMP DWORD PTR DS:[EAX],0
004A712D . 90 NOP
004A712E . 90 NOP
004A712F . A1 9C1E4B00 MOV EAX,DWORD PTR DS:[4B1E9C]
004A7134 . 8338 00 CMP DWORD PTR DS:[EAX],0
004A7137 . EB 12 JMP SHORT videojoi.004A714B
004A7139 . BA 00724A00 MOV EDX,videojoi.004A7200 ; ASCII "Licence to: Unregister"

leads to this string instead:

Patched byte sequence:
90,90,A1,**,**,**,**,83,38,00,90,90,A1,**,**,**,**,83,38,**,EB,12,BA,**,**,**,**

Well, enter them into Dup

Note: if you right click on the edit field 1 or 2 DuP it will open a contextual menu which allows to paste the search pattern without having to manually insert it (CTRL-C won't work).

In 5 select the byte pattern you added and press the button in 4 to get this result:

The VA offset at which the pattern is found is 4A7123 which is correct!

 

 

To make the story shorter the byte patterns of the other two left patches are:

Patch2:

Original byte sequence:
75,18,A1,**,**,**,**,83,38,00,74,0E,A1,**,**,**,**,83,38,00,0F,85,**,**,**,**,8D,45,**,BA,**,**,**,**

Patched byte sequence:
90,90,A1,**,**,**,**,83,38,00,90,90,A1,**,**,**,**,83,38,00,E9,53,02,00,00,90,8D,45,**,BA,**,**,**,**
 

Patch3:

Original byte sequence:
75,**,83,3D,**,**,**,**,00,74,09,83,3D,**,**,**,**,00,75,0A,B8,**,**,**,**

Patched byte sequence:
90,90,83,3D,**,**,**,**,00,90,90,83,3D,**,**,**,**,00,EB,0A,B8,**,**,**,**

I think you might now rebuild how I got them.

Finally Diablo2oo2 Universal Patcher should look like below:

You are ready to create the patch, remember to tag the "Target is packed" checkbox, save the project and create the patch.

You are now ready to test it!

Of course (should) work.

 

5. Conclusions

Lesson Learnt

I realize each time that, if someone wants to get things basic, there's a lot to write and a lot of snapshots to take. So simpler tutorials are those which take more time, anyway I write them also hoping to help new RCE saplings grow.. ^__^

I hope you improved your skill with ollydbg and with what happens under the cover. I hope also you understood that even simple things such as AsPack could be a little more complicated of how a single tutorial drove you to suppose. I also hope you started to understand that RCEing most of the times means analyzing things under a different point of view and be open minded.

have phun!


 
6. Greetingz

[MAIN TEAM]
| Nilrem | Enforcer | Ferrari | Pompeyfan(ex-member) | MaDMAn_H3rCuL3s | EJ12N | Kruger |
Shub-Nigurrath | Jdog45 | R@adier | Teerayoot | ThunderPwr | Eggi | Bone Enterprise | Gabri3l |

*****************************

Exetools | Woodmann | VCT | TSRh | Sir JMI | | FEUERRADER | Britedream | cl0ud (Mephisto) | Zest | Hobgoblin | Nullz | Everyone I missed & you


(.|.)
 ).( (¯`·._.·[¯¨´*·~-.¸¸,.-~*´¨&8~) Ŝħůβ¬Ňïĝµŕřāŧħ ¨´*·~-.¸¸,.-~*´¨]·._.·´¯)
( v )
 \|/