Reversing.generalsby Ignatz / stoicForce PreludeGreetings! Waypoints into the lightor just TOC if you prefer0) Conventions0.1) Commenting The Code 0.2) Translate The Code 1) Functions 1.1) Calling Conventions 1.2) Local Variables - Parameters 1.3) Return Value 2) Loops 2.1) Counter 2.2) Exiting Condition 2.3) Type Of Loop 3) Control Structures 3.1) If Then Else 3.2) Case 4) Global Variables 4.1) Initialization 5) Generating High Level Source 6) Further work 7) Conclusion ConventionsAs you start to reverse, you will soon see, that it is essential to
stick to some general conventions. like the way you name a vaiable or you
comment a loop. this should always look the same, due to sticking to a
convention. mov [00534160], eax
mov [00534160_SerialFlag], eax ; like this you wonīt lose vital information
i now want to present some more thoughts on this topic. Commenting The CodeIt is very important that you use clear definitions and donīt mind if you write some extra words. youīll love yourself for that afterwards, and youīll hate yourself if you can not follow your steps just because you didnīt comment well enough. It will also make some code parts clearer for you, because you have to think about what this piece of code does if you want to comment it seriously. as a general rule: comment the code as if someone else, who never reversed before, had to read, sorry my bad, -understand- it. commenting is about understanding the code not reading the code. Translate The CodeThis is the most important part if you really want to reverse the code
- changing the asm commands into pseuo-high level language source. if you
do a good job here, it will be peanuts to generate the reversed code in
your prefered language. itīs important to realize the difference in
translating the asm-source and commenting it. if you just comment the
code, the outcoming sourcecode is not defined at all. for example, there
are at least 3 differet ways to realize a loop. but if you read trough the
asm-code you will see if itīs a while, a repeat or a for loop. letīs look
at an example: xor exc, exc :00440000 inc ecx ; comment: counter of loop is increased . . cmp ecx, 05 ; comment: counter is compared jbe 00440000 ; repeat this while the counter of ; loop is 4 or less continue loop ----- ; reversed 1 while (i < 5) { still to come } ----- ;reversed 2 for (i=0; i<5; i++) { still to come } ----- ;revesed 3 i = 0 repeat i++; still to come until (i >= 5 ) the comments have nothing to do with the code you finally find
suitable. they just make you and others understand whatīs going on. The
reversed style does not make anyone understand the code better, unless he
knows how to reverse C code better then reversing asm for example. it is
just another form of the same thing. like translating form latin to
english. in this case it is not absolutely clear what kind of loop it is.
it looks like a repeat until, but itīs not really important either, unless
you want to make an exact copy of the original source. FunctionsA program consists of many different functions. thus, it would be wise
to reverse one function after the other. this allows us to stay focused on
one part of the code. another benefit is, that the structure of the code
will nearly reveal by itself. if you have reversed a couple of functios,
you will see a structure coming out of the dark very quickly. this
structure will contain the returnvalues, parameters, stackadjustments,
other commands and further more. Letīs see what we need to describe a
function. Calling ConventionsOne of the most important things to find out is, how the program passes
parameters to functions and how the program handles the stack adjustments.
there are two major conventions. the C and the pascal convention. if you
donīt know how the stack is used during calls, then refer to my tutorial
"Our friend stack and his cousin call". i will only repeat the basics.
parameters are passed through the stack. this means that the stack has to
be restored after the function is completed. if itīs not restored, the
caller will have a corrupt stack, with which it canīt work with. So the
calling convention consists of two parts: procedure test1(Par1, Par2, Par3: integer); asm: push par3 ; push in reversed order push par2 push par1 call test1 ; call function add esp 0C ; restore stack value ) The Pascal calling convention procedure test1(Par1, Par2, Par3: integer); asm: push par1 ; push in same order push par2 push par3 call test1 ; call function ... ; no add esp,0C ; this was already done within the test1 call Local Variables - ParametersWhen you identified how the parameters are passed to the function, you can easily use this knowledge to name the parameters in the function. two lines of code can be found at the beginning of nearly every function. (1.2.I) push ebp ; save the original base pointer mov ebp, esp ; set basepointer to the stack (parameters!) this is a very efficient way to access the parameters without having to
worry about the stack anymore. every parameter can now be accessed
realtive to ebp. (1.2.II) sub esp, 00000018 ; make room on stack ; for local variables even here, the stack is reserved, but not used with the push and pop
instructions. this part of the memory is directly accessed relative to ebp
just as the parameters are. the only difference is, that itīs not ebp+8
but ebp-8 for example. note that ebp-8 is the third local variable
(assuming all variables are WORD values). now you can imagine, where a
stackoverflow comes from. Return ValueIf the call returns a value, it is always returned in eax or in one of
the input parameters. you will often reckon if it is an input parameter,
by checking if an address or a value is provided to the call. if itīs a
value it is definitly not a parameter you need to care about afterwards.
if it is an address, to a string for example, then you should take a look
at the next lines of code to figure out wether it is used again or not. in
most cases you will see that itīs used again. (thatīs more efficient, than
wasting memory) i will now show you what a pascal programmer has in front
of his eyes when he creates a function. this should make the concept even
clearer. function MyAdd(x, y :integer) : integer; // declaration var erg: integer; // local var begin erg := x + y; // do the things add := erg; // return value end; This is a normal function. it just adds two integers. there are two parameters and a local variable that is used to temporarily store the result, which is then returned by the function. the assembler has to care, that the two parameters are removed from the stack after the function is done and it has to reserve enough memoryspace for the local variable...might look like this: ; *Pascal* push ebx ; par1 push ecx ; par2 call MyAdd ; call the function ; in this example, the function restores the stack (pascal) mov ..., eax ; returnvalue in eax ; *C* push ecx ; par2 push ebx ; par1 call MyAdd ; call the function add esp, 8 ; restore the stack mov ..., eax ; returnvalue in eax ;------------------------------- ; *MyAdd* ;------------------------------- push ebp ; save baspointer mov ebp, esp ; get a grip on the parameters sub esp, 4 ; make room for the local var ; adding mov ebx, [ebp+0C] ; get first parameter mov ecx, [ebp+8] ; get second parameter add ebx, ecx ; add mov [ebp-4], ebx ; move result to local var mov eax, [ebp-4] ; return local var ; returnvalue is eax leave ; this instruction does the folowing: ; mov esp, ebp ; pop ebp add esp, 8 ; ONLY for pascal! ret here you should see the differences between pascal and c convention. it
is also obvious how the assembler works with parameters and local
variables. procedure MyAdd2(var res:integer; x, y :integer) // declaration begin res := x + y; // do the things end; // no explicit returnvalue the point is, that the parameter res will be overwritten by the procedure with the result. this means, you have to give a variable parameter to the procedure which can be overwritten; it cannot be a constant or a number. let us see what this would look like in assembler: ;* i will only do the pascal style since you already ;* saw everything important regarding the ;* c - pascal differences in the last example lea ebx, res ; because itīs a var you have to pass the address! push ebx ; put address as 1st param on stack push edx ; edx contains x push ecx ; ecx contains y call MyAdd2 ; call the function mov ..., res ; since res was overwritten by MyAdd2 ; it now has the result ;------------------------------- ; *MyAdd2* ;------------------------------- push ebp ; save baspointer mov ebp, esp ; get a grip on the parameters ; no local variables ; adding mov eax, [ebp+10] ; get first parameter mov ecx, [ebp+0C] ; get second parameter add eax, ecx ; add mov ebx, [ebp+08] ; get the address into ebx mov [ebx], eax ; move result to variable-parameter (erg) leave add esp, 0C ; adjust stack ret in this example, you can see the difference between an explicit returnvalue and a variable-parameter. i hope you got the point, so we can continue to the next section. LoopsIdentifying loops can be a difficult job. especially if the are big and encapsulated. this makes it even harder. but through my years of reversing i figured that a jump backwards is very often a jump done by a loop. for a normal "if then else" statement, you would skip code, by jumping forward, but with loops you have to return in the code which means you have to jump back. that should be clear now. this means, after all itīs not so hard to identify loops. when you identified a loop you have to reverse it of course. let us now have a closer look at the loopīs main parts. Counterif you already made some contact with asm-programming you will have noticed, the loops normally use the ecx register as the loop counter. in case that ecx is not the counter, just look at the jump-statements of the loop. there must be a condition checked before (like test eax, eax) the jump. use your brain to figure that out. brains is the best tool ever developed. Exiting Conditionwhen you identified the counter you also see where the counter is checked. then all you have to do is look at the statement before the loop-jump and voila. Type Of LoopThis is not as important as you might think. you can transfer every
type of loop into another one. the only really important thing to notice
is, if the loop is done at least once or if not. As an example a while
loop might not be entered due to the fact, that the condition at the
beginning is not met. a repeat until loop will always be executed for once
at least, because there is no check at the beginning. Control StrcucturesThese are especially important for crackers. This is obvious. The program checks if you are regged or not. this makes heavy use of control structures. thus understanding these is essential to every serious Fravia. generally they can be divided into if-then-elseif-else and case statements. for control structures it is not so important to identify them and reckon that there is something, but it is essential to understand the changes this statement means to the programexecution. only with that knowlege you can interpret and judge a control statement correctly. i will only point out some examples here. i think this shows best what i mean. If Then ElseThis is a piece of code taken out of Opera 3.62. The purpose of the code is to set the displayed program name in the program bar to either "Opera 3.62 (Unregistered version)" for unregistered users, or to "Opera 3.62" for regged users. it got insane reversing this one. i believed that there was a statement like this: if (regged) progname = "Opera 3.62" else progname = "Opera 3.62 (Unregistered version)" but what it does is the following: cmp byte ptr [PrgDisplayName_00531640], bl ; do the following only if PrgDisplayName="" jne 0045FE49 mov esi, 00000080 ; esi = 80 (maxCount) push esi ; maxCount (maximum chars to append) push edi ; append at edi = PrgDisplayName String Resource ID=20092: "Opera 3.62" | push 00004E7C ; str to append mov ecx, ebp call 00460BD8 - AppendStr(ToApp,EndOfOrig,Count) push edi call 00501E10 - int strLen(str S) ; calculates lenght of the prgDisplayName string pop ecx sub esi, eax ; calcuate maxCount mov dword ptr [005315F4], eax push esi ; maxCount lea eax, dword ptr [eax+PrgDisplayName_00531640] ; eax = end of DisplayName push eax ; EndOfOrig String Resource ID=21428:" (Unregistered version)" | push 000053B4 ; str ToAppend mov ecx, ebp call 00460BD8 - AppendStr(ToApp,EndOfOrig,Count) * Referenced by (C)onditional Jump at Addresses: |:0045FDD3(C), :0045FE12(C) | cmp dword ptr [ebp+flg_Regged_000004EC], ebx;=0 ; check regFlag mov eax, dword ptr [005315F4] ; length of Opera 3.62 string je 0045FE5E ; if regFlag = 0 then set end of PrgDisplayName ; right behind "3.62", this means ; discrad the "(Unregistered version)" part mov byte ptr [eax+PrgDisplayName_00531640], bl ; mov 00 after the Opera 3.62! ; "thats why the first chracter of manually ; entered Names vanished because the lenght ; function was 0" jmp 0045FE65 As you can see, the program goes a different way. it does not differ between regged and unregged the way i thought. it always puts the not regged part in place. this really confused me until i saw how this really works. the program overwrites the string a third time if you are regged. then it sets the end of the string zero, so that the not regged part is ignored. look at it in pseudo code. s = "Opera 3.62\0"; a = " (Unregistered version)\0"; append_this_to_at(a, s, endOf(s)); if regged then s[10]="\0"; end; CaseThis also is some code from Opera 3.62. It decides which help page to show in the browser. I came across this code by accident, but it is a very useful example for a switch/case statement. Before you look at it i want to point out, that there are two different ways in writing a switch statement. one is to write a series of jump sequences. this would equal a "if then elsif elsif elsif...end" and there is the type Opera 3.62 uses here. it is faster than the previously mentioned method. here, the program calculates a jumpmark [4*eax+004932A3] to the corresponding code, instead of going through every line. this only works, because the code has the same length for every branch taken - 4 bytes. normally this is not the case, so you have to deal with a series of "cmp jne" statements which refer to the "if then elsif elsif elsif...end". lets look at the code now. :00493070 mov eax, dword ptr [ebp+10]; eax = Par1 :00493073 add eax, FFFFB1DD ; Par1 = Par1 - 20003d "look at stringrefs" :00493078 cmp eax, 0000008A ; if eax smaller than 138d jump toindex.html :0049307D ja 0049305E :0049307F movzx eax, byte ptr [eax+00493357] :00493086 jmp dword ptr [4*eax+004932A3] ; switch statement, calculate jumpmark * Data Obj ->"keys.htm" | :0049308D push 0051DD58 ; a jumpmark :00493092 jmp 00493063 * Data Obj ->"prefmenu.htm#print" | :00493094 push 005256B0 ; another jumpmark :00493099 jmp 00493063 * Data Obj ->"dialogs.htm#direct" | :0049309B push 0052569C ; ... :004930A0 jmp 00493063 * Data Obj ->"prefmenu.htm#sethome" | :004930A2 push 00525684 :004930A7 jmp 00493063 * Data Obj ->"dialogs.htm#fileuplf" | :004930A9 push 0052566C :004930AE jmp 00493063 * Data Obj ->"dialogs.htm#hotlist" | :004930B0 push 00525658 :004930B5 jmp 00493063 * Data Obj ->"dialogs.htm#locked" | :004930B7 push 00525644 :004930BC jmp 00493063 ; and so on ... Global VariablesIn most programs there are variables and constants that have to be accessible everytime. these cannot be local variables, because these are discarded after the owning function terminates. Thus they are only accessible by the funciton itself. the variables that can be accessed all the time are called global variables (in contrast to local). as a Fravia you should keep an eye out on those, because they contain flags like registrationflags, demoflags, trialdate, ... and other data like programname, parameters, ... . identifying global variables can be a hard job. i figured two ways Opera accessed its variables. one way was direct addressing. this means, if a variable is at :00543380 the program accesses it by this number. direct mode: mov eax, dword prt [00543380] other than that, it might use relative addressing mode. you can see an example of this in the next section. with this mode, the program accesses the variable relative to a baseaddress. this base is stored in a register. if you see something like this itīs a bit harder to identify the global variable. still it is possible. you just have to search for the offset value. donīt get confused everything will be clear in a second. just look at the example. mov eax, [esi + 4EC]
| |
base offset
all you have to do now is search for the offset value. if you can find it serveral times in the same context, then you found a global variable. like i found the regflag here: ; first appearence :004CB0AF mov eax, dword ptr [esi+flg_Regged_000004EC] :004CB0B5 cmp eax, ebx :004CB0B7 lea edi, dword ptr [esi+flg_Regged_000004EC] ; second appearence :004D9543 mov dword ptr [esi+flg_Regged_000004EC], eax ; check that :004D9549 pop ebx :004D954A je 004D9556 ; third appearence :004D963C mov eax, dword ptr [ecx+flg_Regged_000004EC] ; return with regFlag :004D9642 ret ; there are still many more occurences of this addressing but i think you got the point IntializationThese variables have to be initialized. you can see how ths looks in this example. when you see a part of code that looks similar, you know what you got. * Referenced by a (U)nconditional or (C)onditional Jump at Address: ; /*INIT SECTION */ |:0045BD04(U) | :0045BD12 mov dword ptr [esi+regName_00000138], ebx :0045BD18 mov dword ptr [esi+0000013C], ebx ; the variables are addressed via esi+X :0045BD1E mov dword ptr [esi+00000144], ebx ; this means relative addressing :0045BD24 mov dword ptr [esi+00000148], ebx :0045BD2A mov dword ptr [esi+0000014C], ebx :0045BD30 mov dword ptr [esi+00000150], ebx :0045BD36 mov dword ptr [esi+00000154], ebx :0045BD3C mov dword ptr [esi+00000158], ebx :0045BD42 mov dword ptr [esi+0000015C], ebx :0045BD48 mov dword ptr [esi+00000160], ebx :0045BD4E mov dword ptr [esi+00000164], ebx :0045BD54 mov dword ptr [esi+0000038C], ebx :0045BD5A mov dword ptr [esi+00000184], ebx :0045BD60 mov dword ptr [esi+00000188], ebx :0045BD66 mov word ptr [esi+00000212], 0008 :0045BD6F mov dword ptr [esi+00000218], ebx :0045BD75 mov dword ptr [esi+0000021C], ebx :0045BD7B mov dword ptr [esi+00000224], ebx :0045BD81 mov dword ptr [esi+00000220], ebx ~something deleted~ :0045BE9F mov dword ptr [esi+00000358], ebx :0045BEA5 mov dword ptr [esi+0000036C], ebx :0045BEAB mov dword ptr [esi+00000380], edi :0045BEB1 mov dword ptr [esi+0000037C], edi :0045BEB7 mov dword ptr [esi+00000378], edi :0045BEBD mov dword ptr [esi+00000374], edi :0045BEC3 mov dword ptr [esi+00000370], edi :0045BEC9 mov dword ptr [esi+00000384], edi :0045BECF mov dword ptr [esi+00000388], edi :0045BED5 mov dword ptr [esi+000003C4], edi :0045BEDB mov word ptr [esi+000003B4], bx :0045BEE2 mov dword ptr [esi+000003EC], ebx :0045BEE8 mov dword ptr [esi+000003F4], ebx :0045BEEE mov dword ptr [esi+000003F8], ebx :0045BEF4 mov dword ptr [esi+000003F0], edi :0045BEFA mov word ptr [esi+000004D8], bx :0045BF01 mov dword ptr [esi+000003DC], ebx :0045BF07 mov dword ptr [esi+000002A8], ebx :0045BF0D mov dword ptr [esi+000002AC], ebx :0045BF13 mov dword ptr [esi+00000250], ebx :0045BF19 mov dword ptr [esi+00000254], ebx :0045BF1F mov dword ptr [esi+00000258], ebx :0045BF25 mov dword ptr [esi+0000025C], ebx :0045BF2B push 00000720 :0045BF30 mov dword ptr [esi+flg_Regged_000004EC], ebx ; INIT here is not much to say. you can recognize very easily how each variable is set. most of them are flags but some of them are addresses to strings or other data. which is which - this to find out is your work. Generating High Level SourceThe last step in reverse engeneering is to recreate the high level
sourcecode. how to do this you might ask. there are two general
approaches. one is to strictly follow the target program and also follow
its instructions, sometimes even without exactly knowing what they do, to
find out later when reading the recreated source. -or- understanding the
code and writing your own™ source, without having reversed everything
since you were able to guess what the code does. the first approach
focuses on reconstructing the original source files, thus you have to
write it in the same language as it was originally written. the second
approach only wants to create source that does the same as the original
(g.e. it doesnīt matter if you show a nag with a dialogbox or a messagebox
the user will know he failed to reg anyway but the reversed program is not
exacly the same), hence it can be written in any language. the drawbacks
are of course, that you will never know if your program does the same
since you only look for functionality - a lot of testing has to be done in
this case. but it is much faster than looking trough every line of asm
source. the plus on the first method is that you can sometimes continue
without understanding the meaning of the code. it might get clear later
on. as always it depends on you which approach (also mix em up) you
want. :004BCF49 push ebp ; save base pointer :004BCF4A mov ebp, esp ; set basepointer for the function :004BCF4C sub esp, 00000548 ; make room for stack :004BCF52 push ebx ; save ebx :004BCF53 mov ebx, ecx ; ebx = ecx :004BCF55 cmp dword ptr [ebx+00000714], 00000000 ; if [ebx+714] == 0 :004BCF5C je 004BCF66 ; contine with function :004BCF5E push 00000001 ; eax = 1 :004BCF60 pop eax ; . :004BCF61 jmp 004BCFEF ; leave with with eax = 1 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004BCF5C(C) /* continue with ?WriteRegFile(?) */ | :004BCF66 push esi ; push source :004BCF67 push edi ; push destination :004BCF68 mov ecx, ebx ; ecx = ebx :004BCF6A call 004BC81A :004BCF6F mov ecx, 0000012F ; ecx = 0x12F (303d); 303*4 = 1212 (0x4BC) :004BCF74 mov esi, ebx ; sourceaddress of RegInfo :004BCF76 lea edi, dword ptr [ebp+FFFFFAB8] ; destination address of RegInfo :004BCF7C lea eax, dword ptr [ebp+FFFFFAB8] ; . :004BCF82 repz ; while not finshed :004BCF83 movsd ; move the RegInfo :004BCF84 push eax ; pointer to RegFileBuffer :004BCF85 mov ecx, ebx ; :004BCF87 call 004BCF14 - Decrypt(chr *ToDecrypt) ; encrypt the RegInfo * Reference To: KERNEL32.SetFileAttributesA, Ord:0268h | :004BCF8C mov esi, dword ptr [005121A4] ; put address of SetFileAttributes into esi :004BCF92 push 00000080 ; attributes to set :004BCF97 lea edi, dword ptr [ebx+CRegFileSize_000004BC]; edi = addr of filename :004BCF9D push edi ; address of filename :004BCF9E call esi ; Make File writable :004BCFA0 push 00001010 ; fuMode (action and attribs) :004BCFA5 lea eax, dword ptr [ebp+CRegInfBuf_FFFFFF74]; eax = address of buffer :004BCFAB push eax ; address of buffer :004BCFAC push edi ; addres of Filename * Reference To: KERNEL32.OpenFile, Ord:01E8h | :004BCFAD Call dword ptr [00512228] :004BCFB3 mov ebx, eax ; ebx = hfile OpenFile(fName,[opts]) (FileHandle) :004BCFB5 cmp ebx, FFFFFFFF ; if open succeeds :004BCFB8 jne 004BCFBE ; then continue with ebx = eax :004BCFBA xor ebx, ebx ; else handle = 0 :004BCFBC jmp 004BCFE6 ; skip writing part. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004BCFB8(C) | :004BCFBE push CRegFileSize_000004BC ; number of Bytes to write :004BCFC3 lea eax, dword ptr [ebp+FFFFFAB8] ; eax = address of Encrypted RegInfo :004BCFC9 push eax ; Pointer to buffer holding encrypted reginfo :004BCFCA push ebx ; filehandle * Reference To: KERNEL32._lwrite, Ord:02F7h ; is used to write Regfile ! | :004BCFCB Call dword ptr [005121CC] :004BCFD1 xor ecx, ecx ; set ecx = 0 because of setne cl later :004BCFD3 cmp eax, FFFFFFFF ; check for errors :004BCFD6 setne cl ; set if an error occoured :004BCFD9 push ebx :004BCFDA mov dword ptr [ebp-04], ecx ; save result _lwrite * Reference To: KERNEL32._lclose, Ord:02F2h | :004BCFDD Call dword ptr [005121C8] ; close file :004BCFE3 mov ebx, dword ptr [ebp-04] ; ebx = result of _lwrite * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004BCFBC(U) | :004BCFE6 push 00000021 ; make file WriteProtected :004BCFE8 push edi ; pointer to filename :004BCFE9 call esi ; SetFileAttributes :004BCFEB pop edi ; resotre values :004BCFEC mov eax, ebx :004BCFEE pop esi * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004BCF61(U) | :004BCFEF pop ebx :004BCFF0 leave :004BCFF1 ret ; bye After reading the notes it is clear what the program does. I will not attempt to generate C++ source that does the same. might look like this #define CRegFileSize 1212 // global Var gRegFileName /* Name: WriteRegInfoToFile * Purpose: Writes the encrypted Reginfo into the file OUser350.dat * and sets its fileattributes to writeonly * Returnvale: Either 1 if function fails, or it returns the result of * the _lwrite function which is temporarily stored in res. * Remarks: Have to find out about the first flag and the first * function. Very strait forward implementation. */ int WriteRegIfnoToFile(void) { // variables char *CryptRegInfo, // Holds the encrypted Registrationinformation *RegInfo, // Holds the original Registrationinformation *RegFileBuffer ; // Filebuffer for the RegFile handle hFile; // Handle to Regfile int res; // result of function if !unknownflag_[ebx+714] { exit 1; } else { (void) unknownfunction_004BC81A(uPar1, uPar2); (void) StrCpyN(CryptRegInfo, RegInfo, CRegFileSize); (void) encrypt(CryptRegInfo); (void) SetFileAttributes(gRegFileName, FILE_ATTRIBUTES_NORMAL); if (hFile = OpenFile(gRegFileName, RegFileBuffer, 10)) { res = _lwrite(hFile, CryptRegInfo, CRegFileSize); _lclose(hFile); } (void) SetFileAttributes(gRegFileName, FILE_ATTRIBUTES_READONLY); return res; } } Further workThis should be the biggest section, since there is still loads and loads of work to do. i will only point out some headwords: dll reversing, object oriented reversing, ocx-vxd reversing, language specific reversing, packed and encypted programs, commercial protection schemes... . what i want to enforce now is windows reversing. identifying and reversing the WindowProcs, MenuHandlers, Messages, and so on and so on. Maybe you will find another tutorial on this and other topics. but letīs see what time will bring. A lot of these things can also be handled by you. i would like so see some tutorials tools and other stuff. Now we know where to start you so letīs get movinī. ConclusionTo me, reversing is like solving a puzzle. you start out at one point
put peice after peice together until youīre stuck. then you start another
colony somewhere else and slowly the little colonies start growing
together. you will be able to see the big picture clearer and clearer with
every piece you add to the whole. after a while you can already predict
what itīs going to be like and then, in the end you marvel at your genius
work, show it to others and enjoy it, tell stories about how you did it
and canīt wait to start all over again with another one, because of the
fascinating dynamics and the great fun. never forget that, whatever you
are doing enjoy your work be proud of it and make it something special.
last wordsPlease send your comments and all to me -thanks. | |
Đ 1999-2010 by the stoicForce™ |