Reverse-Engineering: DLL Reversing - Modifying Shell32.dll functions by NeuRaL_NoiSE
Posted by: AntiCrack on Feb 04, 2002 - 09:38 AM
RCE - Code-Injection Modifying functions in the shell32.dll library

Target: MS Windows shell32.dll
Essay Level: Easy/Intermediate
Date: 06-10-'99
Tools used:
  • W32Dasm v8.93
  • SoftICE v3.24
  • HIEW v6.01

Hi everyone!

How many times have u thought "damn wouldn't it be nice if i could delete files from hard disk without having them pass through Recycle folder first...i could set the option 'move to recycle' (in the recycled preferences) off but then i wouldn't have a chance to choose wheter i want to erase the file or to move it to the trashcan...or i could press the shift while hitting del to erase the file from disk but then that poor neural_n dude wouldn't have a fair chance to write a tutorial..." ??? Well, if u have this problem (hmmm:), you're reading the right .txt :)

At this point, let me explain a coupla things and let me disclaim my responsabilities:

FIRST : If u act like i point out in this tute, don't consider me responsible for any loss of data that might occur on your hard disk (your "DELETE" command on explorer won't be as safe as the old times anymore! :)

SECOND : this is more a reversing tutorial than a cracking one...if u are reading this hoping to find out how to deprotect some game or app i must tell you that alas u won't find any of the such here. If u're looking for this kinda stuff, please refer to many of the fantastic web pages around (first of all try out some links at

THIRD : I have a little problem writing this tutorial. I am italian, and obviously my copy of Window$ is in italian... being shell32.dll a system dll, its language will be different from nation to nation. So i can't assure you that the VA's and the offsets i write in this tute will be the same on your computer (i don't really think anyway)...however you should be able to follow the procedure by printing the text and following with your disassembled copy on the screen, it's not such a difficult task. And i must point out two more IMPORTANT things:


This said......LET'S GO!!! u will have surely noticed from the intro, in this tute i'll describe you how to create a situation where, when u choose an option, Window$ explorer will physically ERASE the files from your hd, whithout having them pass through recycled folder. Would you like a 3-options dialog box, ONE=MOVE TO RECYCLED, TWO=ERASE FROM DISK and THREE=CANCEL OPERATION ? Right....i'm talking about combinating, in some mysterious way, the three things without having to play around with recycled preferences every single time, or hitting some shift key. Ok, 'nuff chitchat....

Some ppl think that it's explorer.exe that does the tricks (deleting stuff from hd, copying files and so on), but that's only partly true. The code we will modify resides in the SHELL32.DLL library...a dll (Dynamic Link Library) is a set of functions that can come handy for executable files (in our case explorer.exe), and which is loaded/unloaded everytime the executable wishes (when it needs its functions, when it doesn't need them anymore and so on). The dll is also unloaded when you close the last program which used it. In our case, shell32.dll will always be present cuz the first copy of explorer (which is launched at windows start as u all know) loads it into memory, and you can't close that copy of explorer, because that would mean closing the session. Shell32.dll is in the WINDOWSSYSTEM dir of your pc. No big deal, we'll treat the dll as a normal exe, so the tools we'll use will be the usual ones:

  • W32Dasm v8.93
  • SoftICE v3.24
  • HIEW v6.01

Open Shell32.dll with'll note the image base difference...not 00400000h anymore (as most of the classic .exe's) but 7FDB0000h. Good, now open Explorer.exe and create a temporary directory, filling it with useless files copied from other dirs. BE CAREFUL NOT TO OPERATE WITH DIRECTORIES ALREADY PRESENT ON YOUR HARD DISK, BECAUSE THE CHANCES OF LOSING YOUR DATA ARE VERY HIGH! take a random file from your dummy dir and choose DELETE from the FILE menu (or from the context menu that appears when u hit the right mouse button on the selected file). You'll have a nice dialog that will ask you if you want to delete the file...good, we found our hook to the code. At first look, the API to break on is DialogBoxParamA. So let's do it...choose "NO", enter SoftICE, and write "BPX DialogBoxParamA".

Select once again "delete" and sice will pop. Press F12 and you will see the dialog on your screen once again... select "YES" and sice will pop up again, and you will be at the line of code next to the Dialog Box call, which is this one:

* Reference To: USER32.DialogBoxParamA, Ord:0084h
:7FDE2505 FF15E83DE17F            Call dword ptr [7FE13DE8]
:7FDE250B 83F8FF                  cmp eax, FFFFFFFF        ; <--YOU'LL BE HERE

Take a look at EAX register, which, as you know, contains the return value of EVERY API function. Note that it's 6. Press F5, and your file will be moved to the recycled. Now choose DELETE once again. You'll note that, if you answer "NO", the return value in EAX after the call to DialogBoxParamA is 7. Repeat the operation, but this time press the ESCape key on your keyboard. The return value will be 2. At this point we can draw a little scheme:

                                           |                          |
          00000006                         |BUTTON "YES" ON THE DIALOG| MOVE FILE TO RECYCLED
                                           |                          |
          00000007                         | BUTTON "NO" ON THE DIALOG| CANCEL THE OPERATION 
                                           |                          |
          00000002                         | "ESC" KEY ON THE KEYBOARD| CANCEL THE OPERATION 

Hmm.....what bites here is a useless repetition....two return values perform the same would be much better if we could modify the function of the "NO" button on the dialog box...perhaps even redirecting it towards the PHYSICAL ELIMINATION of the file from the disk....thus, without huge changes to the code we'd have a much more interesting situation: "YES" would move the file to the recycled folder, "NO" would phyisically eliminate it from disk, and "ESC" would cancel the operation. Let's see what we can do :)

We'll trace on with F10 in sice, until we don't arrive here:

* Referenced by a JUMP at Addresses:
|:7FDE24ED(U), :7FDE2519(C), :7FDE2524(U)
:7FDE252B 83FE06                  cmp esi, 00000006         ; ESI CONTAINS THE RETURN VALUE
:7FDE252E 751E                    jne 7FDE254E              ; IF YOU DIDN'T CHOOSE "YES"
:7FDE2530 8B452C                  mov eax, dword ptr [ebp+2C]  
:7FDE2533 8B00                    mov eax, dword ptr [eax]  ; OBJECT DESCRIPTOR'S INITIAL BYTE
:7FDE2535 A810                    test al, 10               ; CHECK IF THE OBJECT IS A DIR    
:7FDE2537 7515                    jne 7FDE254E              ; IF SO (10=DIR, 20=FILE) JUMP 
:7FDE2539 A805                    test al, 05               ; IF IT'S A FILE....
:7FDE253B 7411                    je 7FDE254E               ; JUMP AS WELL :)    

Stop! We know enough to understand that the procedure will continue, if everything is OK, at address 7FDE254E...where we find this line of code:

:7FDE254E 8BC6                    mov eax, esi

The return value is put in eax again, very good...we're on the right path...the previous check was needed only to verify memory and handle problems...if there were problems, a dummy value was left into eax and next operations wouldn't have been executed. As you will see, shell32.dll is FULL of such can call it Good Old Micro$oft Redundant Code (TM) :)

Let's keep on tracing with SoftICE until we execute the "ret 028"...we'll find ourselves here:

:7FDE4093 E810E1FFFF              call 7FDE21A8         ; <-- WE COME BACK FROM THIS CALL
:7FDE4098 8BF8                    mov edi, eax
* Referenced by a JUMP at Address:
:7FDE409A 83FF06                  cmp edi, 00000006        ; AGAIN...CHECK IF WE PRESSED "YES"     
:7FDE409D 7540                    jne 7FDE40DF             ; OTHERWISE PROCESS ACCORDINGLY    
:7FDE409F 8B442434                mov eax, dword ptr [esp+34] 
:7FDE40A3 8B08                    mov ecx, dword ptr [eax] ; OBJ. DESCR. INITIAL BYTE IN ECX
:7FDE40A5 F6C110                  test cl, 10              ; HAVE WE GOT A FILE HERE??
:7FDE40A8 7435                    je 7FDE40DF              ; YES! JUMP!
:7FDE40AA 81FD00010000            cmp ebp, 00000100        ; OR A DIR?? (100=DIRECTORY)
:7FDE40B0 7408                    je 7FDE40BA
:7FDE40B2 81FD00030000            cmp ebp, 00000300        ; (300=FILE)
:7FDE40B8 7525                    jne 7FDE40DF        

Heh....more checks...(getting tired? Nah, there's still more to come :) our jne at 409D won't jump cuz we chose "YES", while we'll take the one at 40A8 for obvious reasons (we chose a file :) i must tell you one thing. What we are about to do is REALLY dangerous for your data. So I chose to cover, in this tutorial, ONLY the modifications of the code that perform SINGLE FILE elimination. If you want to delete MORE than one file at once, or a whole dir, you'll have to use shift or pass through the other words, the file will be ERASED from disk by pressing "NO" ONLY when you are trying to eliminate ONE single file. However, if you are thinking about modifying the code relative to multiple stuff elimination, i'll give you a little advice: the dialog box is different (at least here in Italy :D) when you want to delete more files at, a bpx on DialogBoxParamA will make sice pop in another code zone, and u'll find easy to proceed in the same way (or in a similar one) as we're doing here. Another little advice: when you delete more than one file at once, you can consider nopping out the "mov eax,2" that will be performed either when you press "NO" or when you press "ESC" nopping the mov you'll still have a differentiation between the two return values, and thus you will be able to process them accordingly in the dialog procedure. Only problem here is that i don't think there'll be enough space to insert your own code there, as we will have one less value (my god, where does Bill recruit his programmers ?! :) and the "7" value won't be processed. So, you might have to increase the size of the last section of the pe header and append your code there, but hey, that's quite another story :D - although i'm thinking about writing a tutorial for that as well, but i need some motivation...write me and tell me whether i should or not :))

Whatever, let's come back to our reversing session :)

The jumps point us to the next part of the code where our return value will be analized, in the case we chose a file (in other words, what we're interested in:) starts at 7FDE40DF:

:7FDE40DF 83FF02                  cmp edi, 00000002
:7FDE40E2 741A                    je 7FDE40FE
:7FDE40E4 83FF07                  cmp edi, 00000007
:7FDE40E7 7415                    je 7FDE40FE
:7FDE40E9 837C241C00              cmp dword ptr [esp+1C], 00000000
:7FDE40EE 740E                    je 7FDE40FE
:7FDE40F0 FF742424                push [esp+24]
:7FDE40F4 56                      push esi
:7FDE40F5 FF742434                push [esp+34]
:7FDE40F9 E845EEFFFF              call 7FDE2F43
* Referenced by a JUMP at Addresses:
|:7FDE40E2(C), :7FDE40E7(C), :7FDE40EE(C)
:7FDE40FE 8BC7                    mov eax, edi
:7FDE4100 5D                      pop ebp
:7FDE4101 5F                      pop edi
:7FDE4102 5E                      pop esi
:7FDE4103 5B                      pop ebx
:7FDE4104 83C418                  add esp, 00000018
:7FDE4107 C22400                  ret 0024

Three more checks...if we hit "ESC" or chose "NO", the program will exit from the call (with the ret 024 at 4107), keeping that value in the register, otherwise it executes another part of the code, in order to gain the necessary infos for the file elimination (with the call at 40F9).
Let's keep on tracing with SoftICE, and we'll reach the caller....

:7FDE4952 E8A3F6FFFF              call 7FDE3FFA    ; <--WE COME FROM THIS CALL     
:7FDE4957 83F802                  cmp eax, 00000002 ; DID U PRESS "ESC" ?
:7FDE495A 0F847F080000            je 7FDE51DF        
:7FDE4960 83F806                  cmp eax, 00000006 ; DID U CHOOSE "YES" ?
:7FDE4963 743F                    je 7FDE49A4
:7FDE4965 83F807                  cmp eax, 00000007 ; DID U CHOOSE "NO" ?
:7FDE4968 0F8486070000            je 7FDE50F4
* Referenced by a JUMP at Addresses:
|:7FDE48D1(U), :7FDE50AF(U)
:7FDE496E 8945FC                  mov dword ptr [ebp-04], eax ; SORRY, BUT WE HAVE SOMETHING
                                                              ; WRONG HERE..IF EAX DOESN'T HOLD
                                                              ; ONE OF THE PREVIOUS 3 VALUES, 
                                                              ; WE HAVE AN INVALID HANDLE!
                                                              ; LET'S PRINT AN ERROR MESSAGE!

Wohho! Here's the interesting code...the game gets more fun :D
Luck is by our side...we have three branches for three potential the problem is redirecting the je relative to the "NO" button to a different piece of code, which will ELIMINATE the file from the disk...we won't have to waste time with pe sections raw and virtual sizes, and that's a good advantage!

For now, let's keep on tracing, just to get an idea of how things work...our run will deviate at 4963 cuz in our case we chose "YES"...and we'll find ourselves here:

:7FDE49A4 8B4DF4                  mov ecx, dword ptr [ebp-0C]
:7FDE49A7 8B01                    mov eax, dword ptr [ecx]
:7FDE49A9 0B45F8                  or eax, dword ptr [ebp-08]
:7FDE49AC 3D01020000              cmp eax, 00000201
:7FDE49B1 7725                    ja 7FDE49D8 ; WE TAKE THIS ONE IF WE'RE NOT PROCESSING A DIR
:7FDE49B3 0F8402020000            je 7FDE4BBB
:7FDE49B9 3D01010000              cmp eax, 00000101
:7FDE49BE 0F848E000000            je 7FDE4A52
:7FDE49C4 3D02010000              cmp eax, 00000102
:7FDE49C9 0F84FA000000            je 7FDE4AC9
:7FDE49CF 3D03010000              cmp eax, 00000103
:7FDE49D4 7447                    je 7FDE4A1D
:7FDE49D6 EB3E                    jmp 7FDE4A16

Don't be scared by the number of the jumps, if everything is ok you'll take the JA at 49B1 (u won't take it if u're trying to delete a folder, but as soon as it comes to deleting the first file of such folder, you'll take it), that will bring you here:

:7FDE49D8 3D02020000              cmp eax, 00000202
:7FDE49DD 743E                    je 7FDE4A1D
:7FDE49DF 3D03020000              cmp eax, 00000203
:7FDE49E4 0F84D1010000            je 7FDE4BBB
:7FDE49EA 3D01030000              cmp eax, 00000301
:7FDE49EF 0F84F0030000            je 7FDE4DE5
:7FDE49F5 3D02030000              cmp eax, 00000302
:7FDE49FA 0F843C020000            je 7FDE4C3C
:7FDE4A00 3D03030000              cmp eax, 00000303
:7FDE4A05 0F8444030000            je 7FDE4D4F    ; JUMP THAT WILL BE TAKEN IF EVERYTHING IS OK
:7FDE4A0B 3D04030000              cmp eax, 00000304
:7FDE4A10 0F8499030000            je 7FDE4DAF

Hmmm....we can make a safe guess that these are only more checks on the characteristics of the file...let's leave them alone...the jump we'll always take is the one at 4A05. Here's where it brings:

:7FDE4D4F 8B4730                  mov eax, dword ptr [edi+30]
:7FDE4D52 85C0                    test eax, eax
:7FDE4D54 741C                    je 7FDE4D72
:7FDE4D56 53                      push ebx
:7FDE4D57 8D4DFC                  lea ecx, dword ptr [ebp-04]
:7FDE4D5A 6A00                    push 00000000
:7FDE4D5C 8D95E4FDFFFF            lea edx, dword ptr [ebp+FFFFFDE4]
:7FDE4D62 50                      push eax
:7FDE4D63 51                      push ecx
:7FDE4D64 52                      push edx
:7FDE4D65 E815690000              call 7FDEB67F        ; "RECYCLED ON/OFF" CHECK CALL
:7FDE4D6A 85C0                    test eax, eax        ; EAX IS *ZERO* IF THE RECYCLED IS *OFF*
:7FDE4D6C 0F85ABFCFFFF            jne 7FDE4A1D         ; YOU GOT RECYCLED "ON", JUMP AND MOVE
                                                       ; THE FILE TO IT
* Referenced by a JUMP at Address:
:7FDE4D72 8D85E4FDFFFF            lea eax, dword ptr [ebp+FFFFFDE4] ; OTHERWISE PROCEED WITH
:7FDE4D78 50                      push eax                          ; THE PHYSICAL ELIMINATION
* Reference To: SHELL32.Ordinal:00A4                                ; OF THE FILE!
:7FDE4D79 E8C7C3FFFF              call 7FDE1145
:7FDE4D7E 85C0                    test eax, eax
:7FDE4D80 7409                    je 7FDE4D8B
:7FDE4D82 C745FC00000000          mov [ebp-04], 00000000
:7FDE4D89 EB09                    jmp 7FDE4D94

Heh....looks like we found it :)
Everything depends on that "option check" call at 4D65...if the return value from the call (in eax) is ZERO, the file will be physically removed, otherwise it will be put in the recycled folder. You can look up what that call does by tracing into it...i personally wrote down every difference i found between the exectution with the "move to recyled" option (in the recycled preferences) switched ON and switched OFF....if you are interested, the code zone where the destiny of eax is decided is the one of the three jumps at B660, B666 and B66C. The xor will zero out eax and will give us back the zero value, avoiding the execution of quite some code in the call (which is needed to prepare the operation of moving the file to the recycled).

So, now we know that, in order to PHYSICALLY ELIMINATE the file, our program will have to perform the code that starts at 4D72...hey but what do i see here?? a jump that brings there?? hmm, let's put a bpx on this jump (7FDE4D54)...strange, it never jumps...neither with recycled "OFF", nor with recycled "ON"...ah! i almost forgot about our Good Old Redundand Code (TM) from Micro$oft :)))

No joke now, i must i say that this useless jump (will it jump once per millenium? ;) made me think about one thing... if there's a jump that avoids the whole previous piece of code (the one with those checks), this makes me sure about the meaning of the call at 4D65, and of its uselessness (i don't know if that's correct in english :P) for our purpose... we don't have to worry about sticking in a flag and making the code check it AFTER the "option check" call... we can access the piece of code that erases the file from disk even AVOIDING the whole's demonstrated by the fact that Microbloat programmers put a jump in the code that does exactly what we'll do now...AVOIDING the checks... it's just a matter of logic and intuition (but let's call it ZEN that makes me sound l33ter ;)).... The solution at this point is really easy...we'll modify the jump relative to the "NO" button in a way that it executes DIRECTLY the code at 7FDE4D72, instead of its useless functions! So, the first step is MAKING A BACKUP COPY OF YOUR SHELL32.DLL FILE, calling it shell32.bak for instance. Then, we'll also copy it to a new file that we'll physically patch (we can't patch shell32.dll cuz being a system dll it won't be discarded from memory while windows is running). Now we have 3 files, shell32.dll, shell32.bak and, let's say,

Logic (no zen this time;) would suggest us to write down the two offsets (one for the jump relative to the "NO" button, and one of the point where we want it to jump to), open HIEW and patch it as in every other case....instead the procedure we must follow is different. If you try this approach, when you'll restart Windows with the new shell32.dll, by pressing "NO" on the dialog you'll have the bitter delusion of a GPF....why?? the explanation is really easy: HIEW has a little problem when patching far inserts wrong offset differences, or at least it misinterpretates (woh, strange word actually:) the given offset.... so when we patch shell32.dll with HIEW, the offset where we tell it to jump to is 34172h, but alas, at the execution of the jump in the "real life :)", the dll will jump to the code *VIRTUAL ADDRESS* 34172h, NOT to the *OFFSET* 34172h (which is relative to virtual address 7FDE4D72)....hmmm nasty problem you think?? nah, a little logic is enough to solve it... you should NEVER forget about the power of SoftICE... enter it and insert CODE ON (if you don't already have it in the init string)...this option will allow you to see the bytes relative to every single code line you're tracing through. Now put a bpx on 7FDE4968 (the je relative to the "NO" button) break in the right code selector bpx on the usual DialogBoxParamA, then F12 to get to shell32.dll code. Once you put the bpx, press "NO" on the dialog box, and you'll find yourselves stuck on the line with the je where we placed the breakpoint. If you continue with execution, you'll cause a GPF, so what you do now is pressing the "A" key (Assemble instruction), so that sice will give you a nice prompt where you can insert the assembly instruction that you wish, and that will replace the current one. Just write "JE 7FDE4D72", and you'll note a change in the bytes at the left of the instruction. Take a piece of paper and write down those bytes. On my shell32.dll version the bytes are the following ones: 0F8404040000.

Once u did this, close everything and open your temporary file with HIEW...go to offset 33D68h (F5), press F3 and insert the SAME EXACT byte sequence that SoftICE gave you before.

Save the file, restart in DOS mode, go to windowssystem and copy SHELL32.NEW to SHELL32.DLL (that now is overwriteable, because Windows is not in execution). Now restart explorer and try "delete" on a dummy file...if you press "ESC", the file will stay right there....if you press "YES", it will be moved to the recycled...and if you press "NO", the file will be removed from the disk! Yeah! We made it! :D

Last words

With this tutorial i wanted to show how easy can it be to do some nice reversing, and i really hope that someone found all this useful for learning purposes. If you have any problems following the tute, or you just want to make me know something (addons are PARTICULARLY welcome), my email is

This said, i wave to all the friends from #cracking4newbies, #crack-it and RingZ3r0 (welcome to the group, ALoR! :)... special greetings go to my dear friends BrahzVooz and ytc (thanks for hosting the tute, fella :))

till next time......!

-NeuRaL_NoiSE 1999