How to extend notepad's functionality by adding code to caves - by defiler.


Published by Tsehp.September 2000.



You may ask yourself "Why the hell notepad again?".

Well, the reason is, that notepad is a good and small target.



Tools i used this time:


(x) SoftIce v4.05

(x) Hiew 6.16



Some lame words:


This essay is based on an old idea i had a year ago. I realized this idea,

but i didn't know anything of the PE-format at all, so i just overwrote some

other code. This time I will be using caves, no code gets overwritten. ;)


The idea:


We will make notepad to 'encrypt' its buffer before it'll be saved to disc.



The essay:


First, we have to find a cave, some space between 2 sections.

This can be done very fast by loading notepad.exe into hiew ;)

Switch mode from "Text" to "Decode" by pressing F4 then F3 (I assume you know

how to use hiew so i won't explain any further "how-to-rape-hiew").

Press F8 to show up the PE-Header, then F6 for the Object-Table. You'll see all

the sections listed, beginning with the code-, then the data-section. The relevant

properties of the objects are "PhysSize" and "Offset"; they'll show the size of unused

bytes between the sections (called 'caves').


By looking at the ObjectTable you see:


PhysSize of .text : 4000

Offset of .text       : 1000


by adding them together      : 5000


This value is the offset of the end of the .text section



Now have a look at the Offset of the .data section (PhysSize ain't important):


Offset of .data       : 5000


You see, Both offsets match... hmmm but maybe there are some unused bytes anyway ?

Let's see.. select the first section (.text) and press enter.

We landed at 401000, it's the beginning of the code (.text) section (NOT the EnytrPoint!!)

As you may remember, its physical size was 4000. Add this value to 401000 and we have

the end of the .text section (405000). Jump to this address by pressing F5, then enter

".405000" (don't forget the dot).

So we are directly "between" the .text and the .data section. If you scroll up a bit,

you'll see rather lot of 0-bytes (I guess they're unused, as the characteristics for

this section is 60000020).

You scroll up until you see the last non-crap instruction of the .text section.

You should be at 404E96 (there's a jmp GetFileTitleA), 404E9C may be the first byte we

could use. Now press Ctrl-D to get into our beloved ring0 debugger, just to calculate the

number of bytes we can use - type "? 405000 - 404E9C". Sice gives us:

164h = 356, a lot of bytes, at least for our purpose.

OK, we got almost all necessary stuff to start adding some code:

404E9C, the RVA of the unused bytes, where we'll insert our code and 356 bytes to use.

Just one thing missing: The address of the routine, that reads in notepad's textbuffer.

After reading in the textbuffer we have to jump immediately to our routine to encrypt it.

And this is how we can get it: Create an empty textfile, run notepad.exe, open the empty

file and write some text. Press Ctrl-D and type "bpx CreateFileA", i guess it uses this

API to save the file. Get back to Windows again, select File/Save.

We are inside Kernel32!CreateFileA, after pressing F11 (g @ss:esp) we are back in



0167:004031df  ff15c0634000        call      [KERNEL32!CreateFileA]

0167:004031e5  eb2a                jmp       00403211          ;here we are!

0167:004031e7  6a02                push      02

             ...            ...       ....

i left out some code here, cause we directly jump to 403211

we'll trace some code here...


0167:0040320f  33f6                xor       esi,esi

0167:00403211  a340564000          mov       [00405640],eax    ;save handle

0167:00403216  83f8ff              cmp       eax,-01           ;error ?

0167:00403219  7524                jnz       0040323f          ;no? then go on at 40323F

0167:0040321b  85ff                test      edi,edi

0167:0040321d  7419                jz        00403238

             ...            ...        ...

we don't need the errorhandler...


here we get, if no error occured...

0167:0040323f  33ff                xor       edi,edi

0167:00403241  a104504000          mov       eax,[00405004]

0167:00403246  57                  push      edi

0167:00403247  57                  push      edi

0167:00403248  68c8000000          push      000000c8

0167:0040324d  50                  push      eax

0167:0040324e  ff15d8644000        call      [USER32!SendMessageA]


hmmm.. sendmessage, maybe it uses WM_GETTEXT ?

let's look up SendMessageA parametres in win32.hlp:


LRESULT SendMessage(


    HWND hWnd, // handle of destination window

    UINT Msg,  // message to send

    WPARAM wParam,      // first message parameter

    LPARAM lParam       // second message parameter



We need UINT Msg, it is the 3rd parametre pushed onto stack (remember LIFO!).

In our case it is 000000C8, so we go visiting sice and typing "wmsg C8".

Sice gives us: 00C8  EM_FMTLINES


shit, we were wrong.

ok, we go on tracing...



0167:00403254  57                  push      edi

0167:00403255  8945f8              mov       [ebp-08],eax

0167:00403258  57                  push      edi

0167:00403259  a104504000          mov       eax,[00405004]

0167:0040325e  6a0e                push      0e

0167:00403260  50                  push      eax

0167:00403261  ff15d8644000        call      [USER32!SendMessageA]


Another SendMessageA, this time with UINT Msg == 0E. Back in sice we type

"wmsg 0e" == WM_GETTEXTLENGTH, this one is good! ...but we don't want it :P

go on..


0167:00403267  8d5801              lea       ebx,[eax+01]

0167:0040326a  6a42                push      42

0167:0040326c  53                  push      ebx

0167:0040326d  8945fc              mov       [ebp-04],eax

0167:00403270  a144564000          mov       eax,[00405644]

0167:00403275  50                  push      eax

0167:00403276  ff15a4634000        call      [KERNEL32!LocalReAlloc]


hmmm... LocalReAlloc, look at the API reference:


"The LocalReAlloc function changes the size or the attributes of

a specified local memory object. The size can increase or decrease."


Ahhhhh! It enlarges/decreases memory after getting the size of our textbuffer =)


0167:0040327c  a344564000          mov       [00405644],eax    ;save handle

0167:00403281  3bc7                cmp       eax,edi           ;some testing for errors

0167:00403283  7520                jnz       004032a5          ;if no error -> 4032A5

      ....        ...      ...      ...



0167:004032a5  a144564000          mov       eax,[00405644]

0167:004032aa  50                  push      eax

0167:004032ab  ff15a0634000        call      [KERNEL32!LocalLock]


ahhh, LocalLock, I know what it does:


"The LocalLock function locks a local memory object and

returns a pointer to the first byte of the object's memory block."


We do slowly but at least we DO com closer to our routine... =)


0167:004032b1  50                  push      eax

0167:004032b2  8bf8                mov       edi,eax

0167:004032b4  53                  push      ebx

0167:004032b5  a104504000          mov       eax,[00405004]

0167:004032ba  6a0d                push      0d

0167:004032bc  50                  push      eax

0167:004032bd  ff15d8644000        call      [USER32!SendMessageA]


Again a sendmessage. Get into sice, type "wmsg 0d":




great! look it up in the API reference:


"An application sends a WM_GETTEXT message to copy the text

that corresponds to a window into a buffer provided by the caller."



wParam = (WPARAM) cchTextMax;   // number of characters to copy

lParam = (LPARAM) lpszText;     // address of buffer for text


Return Values:

The return value is the number of characters copied.


hmmm, we need the length AND a pointer to a buffer as return values...

have a look at the API reference (once again ;), then at address 4032b1.

Eax is the pointer to the buffer, at line 4032b2 it is copied to edi.

And it won't be changed as i figured out! After SendMessageA we have

edi==pointer to buffer and eax==return value of SendMessageA==length of buffer.

Followed by some code, that will write this buffer to a file:


0167:004032c3  ff75fc              push      dword ptr [ebp-04]

0167:004032c6  57                  push      edi

0167:004032c7  ff3540564000        push      dword ptr [00405640]

0167:004032cd  ff1564634000        call      [KERNEL32!_hwrite]

0167:004032d3  3b45fc              cmp       eax,[ebp-04]

0167:004032d6  7442                jz        0040331a we got to insert a jump to our code somewhere. i think we'll take the

line at 4032C7, gonna insert the jump to our code (remember, our location was 404E9C).

Of course, we got to restore the overwritten bytes inside our code again.

Our jump needs 5 bytes, so the "push dword ptr [00405640]" instruction has to be

resotored again.

Press F5 (GOTO) then type ".4032C7", then F3 "EDIT" and finally F2 to switch to ASM.

Enter "jmp 4E9C" press return, then "nop" and return, then escape.

Then press F9 and our jump is done. Now we'll code the "encryption".

We are at 4032CD now, that's where we got to jump back to.

Jump to the cave, where we'll add this code by pressing F5 and typing ".404E9C".

Now remember, edi was a pointer to the textbuffer and eax the size of the buffer in bytes.

So we'll make an easy 'encryption', you may write your own, mine is rather cheap, a simple

XOR plus a ROL instruction ;)



      pushad                        ;save registers

      xchg eax, ecx                ;ecx=size of buffer


      xor b,[edi],64              ;remember: edi is a pointer to our buffer,

      rol b,[edi],cl              ;some lame encryption

      nop                     ;some nops for further changes








      inc edi                     ;increment pointer to buffer (next char)

      loop lamecrypt               ;loop until ecx=0 -> end of buffer

      popad                   ;restore registers

      push d,[00405640]            ;remember? we have to restore the overwritten

                              ;push instruction from 4032C7 !!

      jmp 32CD              ;we got to jump back to the code at 4032CD


OK, we are still at 404E9C. Press F3, then F2 and enter the code above.

The label "lamecrypt" is located at 404E9E, so you won't type "loop lamecrypt", you'll

type "loop 4E9C". Finally press F9 and we have finished the encryption-code.

We have finished now, at least part 1, i need to learn for a maths test now,

maybe i'll update this essay tomorrow.

Included are my modified version and my orginial version of notepad.


Greetings fly out to:


no specific order.

ultraschall, cardenal mendoza, kaparo, flwright, ratso, haldir, lightdruid, ganjaman,

telcofix, whataduck, tornado, hutch, masta, notty, knotty, kwazy webbit, secret, iczelion,

bisoux, MisterE, mr nop, neural noise, lazarus, peegee, random, daze, the anti xryst,

acid burn, azzyrian, decline, blind angel, keyboard junky, reverend x, DnNuke, Gizmo,

viny, volatility, alpine, promothee, sat0r, fli7e, targ0n, Ridlexx, novatrix, TomTom,

WarezPup, Weazel, visionz, kaai, MagicRaph, sepulcrum, MVD, sn00pee, sinn0r and all

who contribute to the reversing spirit ;)

Group/chan greets:

all blizzard members, everyone in #win32asm, #cracking, #eliteFravias, #reversing4newbies,

#immortaldescendants, #digital.factory, #hellforge, and #learn2crack.


I would be pleased to get some feedback, catch me on irc or mail to: