Log in

View Full Version : Changing notepad's background color


homersux
July 9th, 2004, 16:39
I have an idea how to do this, basically i need to patch the wndproc
and add a WM_CTLCOLOR code segment in notepad's code cave (? where the code segment
ends but not yet at the end of the memory page). Then after the
WM_CTLCOLOR code segment is executed, it jmps back to the original
notepad's wndproc.

My question is, Is there an easy way to change notepad's background
color? the default WHITE_BRUSH is annoying.

evlncrn8
July 9th, 2004, 17:02
yep the WM_CTLCOLOR , intercept it, and return the handle of a brush to draw the background that should allow you to quite easily change the background color, the system has a few basic brushes already defined like the WHITE_BRUSH one, which can be obtained from a GetSysColorBrush call, and if the color you want to use isnt available, then you can make one , and just return with eax = the handle of the brush u want the background to be you may have other things to do such as calling a SetBkColor etc on the window as well so the text also assumes the same background color, shouldnt be too hard

bilbo
July 14th, 2004, 01:59
Or do it dynamically.

You need some code-injection mechanism, and then you can call
Code:
SetClassLong(notepad_wnd, GCL_HBRBACKGROUND, (LONG)newbrush)
from Notepad address space

Have a look at h..p://www.codeproject.com/dll/subhook.asp

Regards, bilbo

homersux
July 14th, 2004, 10:15
Well, the first approach doesn't work. It happens that the message
WM_CTLCOLOREDIT (0x113) is never passed to notepad windproc at 0100248f.
And even if it was passed to the windproc, the message jmp table executes
something that does not relate to color management at all.

code injection is very easy, i have a generic code injection scheme setup. i will try
the sendwindowlong approach shortly.

Another question: can anyone make obtain a non-NULL ptr1 (cannot change base address in 2nd VirtualAlloc call)

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <windwsx.h>

int main(){
LPVOID * ptr, *ptr1;
ptr = (LPVOID)VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_READWRITE)
ptr1 = (LPVOID)VirtualAlloc((LPVOID)((LPBYTE)ptr+0x1000), 0x1000,
MEM_COMMIT, PAGE_READWRITE)
printf("ptr = %08X, ptr1=%08X\n", ptr, ptr1);

}

bilbo
July 14th, 2004, 11:17
Hi, homersux,

the second approach doesn't work either, I have tried it today without success. Sorry if I gave you a wrong advice!

In fact (I tried with a yellow brush) the notepad class will come with a yellow background (you can see it when you resize the window) but it is immediately hidden by the child Edit window.
By the way, changing the background on the child window with the same technique does not work.

I am trying to subclass on the fly the notepad window, but without success at the moment.

-----------------------------------------------------------------------


Your second question is another good question!
Why the second LocalAlloc() fails? I cannot answer!
In fact, if you do
Code:

{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((LPBYTE)ptr+0x1000, &mbi, sizeof(mbi));
printf("base %x,allocbase %x,allocprotect %x,size %x,state %x,protect %x,type %x\n", mbi.BaseAddress, mbi.AllocationBase, mbi.AllocationProtect, mbi.RegionSize, mbi.State, mbi.Protect, mbi.Type);
}

you will see that memory at ptr+0x1000 is MEM_FREE!
But this is another thread...

Regards, bilbo

homersux
July 14th, 2004, 11:35
I have answer to the 2nd problem at least on windows 2000. It's a lenthy analysis of ntoskrnl!MmAllocateVirtualMemory. Basically the kernel will not allow the 2nd call to proceed no matter what. It has to be a on a 64K boundary with MEM_RESERVE|MEM_COMMIT option to succeed. It's very interesting.

The notepad thing is really interesting, I have learnt quite a few things about gdi32 in the process.

bilbo
July 15th, 2004, 09:48
Hi, homersux,
I finally managed to obtain some results on your questions...

1. NOTEPAD BACKGROUND
The following code, pasted in a file testlib.c, will build a dll TESTLIB.DLL

Code:

// coded by bilbo, 15jul04 - compiled with M$: cl -W3 -LD testlib.c

#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "user32"
#pragma comment(lib, "gdi32"

#define BKG_COLOR RGB(0xFF,0xFF,0) // yellow

// globals
HBRUSH brush;
WNDPROC oldproc;

/*
* new window procedure for the Notepad class
*/
LRESULT CALLBACK
WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CTLCOLOREDIT) {
// change background of current line to bypass default white
SetBkColor((HDC)wParam, BKG_COLOR);
// change color of all remaining lines
return (LRESULT)brush;
}

return CallWindowProc(oldproc, hwnd, uMsg, wParam, lParam);
}

/*
* exported function for LOADDLL.EXE
*/
void __declspec(dllexport)
TestFunction(void)
{
HWND wnd;

// search for Notepad class
wnd = FindWindow("notepad", 0);
if (!wnd) {
MessageBox(0, "Notepad not found", 0, 0);
return;
}

brush = CreateSolidBrush(BKG_COLOR);
if (!brush) {
MessageBox(0, "Cannot create new brush", 0, 0);
return;
}
// subclass it
if (!(oldproc = (WNDPROC)SetWindowLong(wnd, GWL_WNDPROC, (LONG)WndProc))) {
MessageBox(0, "Cannot set new wndproc", 0, 0);
return;
}

// force immediate repaint
InvalidateRect(wnd, 0, 1);
}


Inject TESTLIB.DLL inside NOTEPAD with the command
"loaddll /l <pid> testlib.dll(full path) TestFunction"

You can find LOADDLL.EXE with sources at
http://www.codeguru.com/Cpp/W-P/dll/article.php/c105

2. VIRTUALALLOC ODDITIES
I've reverse-engineered NtAllocateVirtualMemory() in ntoskrnl.exe.
What you say
Quote:
It has to be on a 64K boundary with MEM_RESERVE|MEM_COMMIT option to succeed
is not exact.

In order to succeed, VirtualAlloc() with a prefixed address must find the address inside the VadRoot binary tree of the process. This means that a previous VirtualAlloc() with a 0 address must have been done with MEM_RESERVE flags. Unfortunately, every such VirtualAlloc() will occupy 10000h bytes, independently on the Size parameter.
So, the only way I found to obtain two pointers with a difference of 1000h bytes between them is the following: MEM_RESERVE a quantum of memory with VirtualAlloc(0, ...), then MEM_COMMIT two times with VirtualAlloc(not_zero, ...)
The following code will show you what I am saying:

Code:

// coded by bilbo - 15jul04

#include <windows.h>
#include <stdio.h>

void
main(void)
{
LPBYTE ptr;

// len 1 or 10000 is the same, but we use 10000 for further commit
ptr = VirtualAlloc(0, 0x10000, MEM_RESERVE, PAGE_READWRITE);

ptr = VirtualAlloc(ptr/*not 0*/, 0x1000, MEM_COMMIT, PAGE_READWRITE);
printf("first ptr = %#x\n", ptr);

ptr = VirtualAlloc(ptr+0x1000, 0x1000, MEM_COMMIT, PAGE_READWRITE);
printf("second ptr = %#x\n", ptr);
}


The fun is just started!
bilbo

Silver
July 15th, 2004, 11:31
Don't you need to release the brush object (Select null into the GDI object) then call DeleteObject() on the brush as well?

Just doing this off the top of my head, but I'm fairly sure you'll get a GDI object leak if you don't...

homersux
July 15th, 2004, 12:16
The findings from hooking notepad wndproc was extremely interesting Note I don't use the lame CBT global hook. I use remotethread dll->loading additional dlls to avoid relocation problems etc. I find the following results interesting:

1) Don't preserve stack before making calls to actual WINAPI WndProc, the hooked program will work ok
until you move the mouse out of the window region. Apprently inside TranslateMessageEx, it's trying to do something smart about the stack and corrupts the original wndproc subroutine entry (4 bytes)

2) Preserve stack (a couple assembly tricks here) and don't preserve return result from original wndproc,
you can type and move mouse out of window region, everything seems ok. But you cannot minimize, move the window -

3) Preserve stack and preserve return result from original WndProc, everything works and behaves normal. This is the only way to go when hooking WndProc.

homersux
July 15th, 2004, 12:27
Bilbo, thanks for your replies. I will try the answer from your first experiment shortly.

Now onto the VirtualAlloc thing, we all know that once you reserve a huge chunk of memory then
subsequently you can obtain a ptr 0x1000 bytes after the first ptr. I didn't make it clear that in my
test program, one cannot change the amount of memory the first call allocates, i.e. the 0x1000 bytes but not 0x10000 bytes. Here is the result from my studies of the NtAllocateVirtualMemmory,

Internals of VirtualAlloc win32 API

Win32 API VirtualAlloc allocates a multiple page size memory space
from the system. The function takes 4 arguments and returns a pointer
to the allocated memory space:

ptr = VirtualAlloc(address, size, mmFlag, pgFlag)
address: 0 or fixed address (rounded up to 64kb)
size: rounded up to page size
mmFlag: MEM_COMMIT, MEM_RESERVE
pgFlag: PAGE_READ, PAGE_WRITE, PAGE_READWRITE ..

The function calls VirtualAllocEx to do the job, pseudo code for
VirtualAlloc call VirtualAllocEx(-1, address, size, mmFlag, pgFlag

VirtalAllocEx has one more argument, the process handle, -1 means the
calling process itself. VirtualAllocEx calls IVirtualAlloc. pseudo
code for VirtualAllocEx:

setup stack frame
setup SEH
if(address){
ecx = [7c5cf048] // global variable lowMemBoundary = 4MB
if(address < [ecx+13c]) // [ecx+13c] = 0x00010000
goto out_bad_below_4MB
// The 3rd argument is always 0 when called from ring3 application?
eax = IVirtualAllocEx(-1, &address, 0, &size, mmFlag, pgFlag)
label_1
remove SEH
return EAX

IVirtualAlloc:
mov eax, 10 <-- syscall index for ntoskrnl!NtAllocateVirtualMemory
edx = esp+4 <-- the return address: label_1 in VirtualAllocEx
int 2e

NtAllocateVirtualMemory:
setup kernel SEH
if(user land esp > 7fff0000) goto called_from_kernel
copy stack parameters from user land (EDI) to kernel land (ESI)
called_from_kernel:
mov ebx, 80499e48 (win2ksp4)
call ebx
if(eax != 0) eax = 0 <------ Interesting
else eax = AllocatedAddress
remove kernel SEH
SYSEXIT

This subroutine called through ebx is the real labor code that
allocates the memory. This subroutine is definitely written in higher
level language, probably C due to the presense of stack frame and
stupid compiler optimizations such as mov [ebp-20], eax; mov eax,
[ebp-20] sequence. This subroutine takes the same argument list as
IVirtualAlloc.

The pseudo code follows (when MEM_COMMIT and address are set):

setup stack frame
setup SEH
sub esp, 124 ; lots of local variables

local_stack_ebp-18 = esp
if(arg3 >= 15) goto called_from_kernel
local_mmFlag_ebp-128 = mmFlag & 200000
local_mmFlag_ebp-128 = mmFlag & 200000
if (mmFlag | MEM_RESERVE) goto attempt_to_reserve_page

if(addr > 7fff0000) goto out_bad_MMTooHigh
if(addr > eax=(7ffeffff+ffff0000)=7ffdffff) goto out_bad_MMTooHigh
if(eax - addr < 64kb ) goto out_bad_NotEnoughMem
if(size == 0) goto out_bad_invalidSize
if(pHandle = arg0 != -1) goto allocate_in_another_process
setup thread related parameters
call ExAcquireFastMutex
call NtAcquireFastMutexUnsafe
if(addr == 0) goto free_base_address_allocate
fixed_base_address_allocate
verify pgFlag and mmFlag
align size to 4k boundary
push address >> 0c
push (address + size -1) >> 0c
push ptr to memory_block_linkedlist

call sub_8044eb8a <--interesting subroutine that walks through the LL

if(eax == 0) goto out_bad_STATUS_conflicting_address
prepare to allocate memory with the information in eax
free_base_address_allocate:
allocate the memory at address
... // abbrieviated
out_bad:
mov eax, ERROR_CODE ; C0000018 STATUS CONFLICTING_ADDRESS
ret

sub_8044eb8aaddress >> 0c, (address+size-1) >> 0c, MM_block_LL_head)
mov eax, [esp+0c] ; head ptr to the MM_BLOCK_LL
l_1:
test eax, eax
jz out_bad
mov ecx, [esp+4] ; address >> 0c
cmp ecx, [eax+4]
jbe l_2
mov eax, [eax+10]
jmp l_1
l_2:
mov ecx, [esp+8] ; (address + size -1) >> 0c, upper bound
cmp ecx, [eax]
jae out_good
mov eax, [eax+0c]
jmp l_1
out_bad:
xor eax, eax
out_good:
ret 0c ; 3 arguments

the structure of the MM_BLOCK is:
struct MM_BLOCK_LINKEDLIST{
DWORD linear_address >> 0c;
DWORD lower_bound_LA >> 0c;
unknown; 0
DWORD upper_bound_LA >> 0c;
DWORD next_block; <-- always points to a higher address block
}
The head of MM_BLOCK linked list is derived by:
mov eax, FS:[124] ; FS=30, ring0 TIB
mov eax, [eax+44]
mov eax, [eax+194]
mov MM_BLOCK_LL_HEAD, eax

Using the MEM_COMMIT argument and fixed address, the last MM_BLOCK is
21f0, 21f0, 0, 21f0, 0 (hypothetically in my test case), thus
sub_8044eb8a always ends up returning 0 in eax. This subsequently
causing AllocateVirtualMemory to return C0000018 in EAX and eventually
0 in EAX to IVirtualAlloc.

A simple alternation of the mmFlag argument will change the execution
path (MEM_RESERVE|MEM_COMMIT) to

attempt_to_reserve_page:
804AFC03 (IoOpenDeviceRegistryKey+0284)
mov eax, [ebp-8c] ; address
lea edx, [eax+edi-1] ; address+size-1
or di, 0fff ; align size to page boundary
and ax, 0000 ; aligh address to 64k boundary <--- MOST INTERESTING
mov [ebp-20], eax
...
push MM_BLOCK_HEAD
push address+size-1 >> 0c
mov eax, [ebp-20]
shr eax, 0c
push eax ; 02f0 instead of 02f1 due to or ax, 0000
call sub_8044eb8a(02f0, 02f1, MM_BLOCK_HEAD)
if(eax != 0) goto out_bad_STATUS_CONFLICTING_... <-------- AHHHH!!!
else proceed...

Do you see the difference here compared with the MEM_COMMIT case,
here, the kernel bails out when eax!=0 but it bails out when eax=0 in
MEM_COMMIT case. Why is that? Well, again check sub_8044EB8A, when the
code walks through the MM_BLOCK_LL and reaches the end, the jbe and
jae instruction will return 02f0 in eax thus it is never zero. What
does this tell you? When you do MEM_RESERVE|MEM_COMMIT, your address
has to be on a 64k boundary! There is no such restriction for
MEM_COMMIT, however, the kernel would still bail out regardless if
your fixed address is on a 64k boundary or not. Your call to
VirtualAlloc must be MEM_RESERVE|MEM_COMMIT to succeed if you use a
fixed address. If you start out with address 0 though, you can use
MEM_COMMIT alone and it will work. This sound a little complicated,
try to play with sample code and see what the results are.

homersux
July 15th, 2004, 12:46
Great, the notepad's color finally changed, lol Here is my hookdll source:

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include "injinfo.h"

//typedef void (*LPFUNC)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
typedef DWORD (*LPFUNC)();

// The worker dll and function that do some real work
#define THREADHELP_DLL "ThreadHelpDll.dll"
#define THREADHELP_FUNC "HookFunc"

// It's unlikely that a process will run 2 threads containing this structure
// But if the situation arises, we'll have to convert this to per thread data.
// Since thread is always executed in the context of the hosting process,
// this data is not shared among processes unless a shared rw segment is specified.
struct t_inj_info * l_info;
LPFUNC orig_func;
DWORD i, stackPtr, ORetAddr, retval;
HDC hdc;
HBRUSH brush;


LRESULT CALLBACK
new_func(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){

// removed the stack messing for clarity purpose.
if(message == WM_CTLCOLOREDIT){
hdc = (HDC)wParam;
SetTextColor(hdc, RGB(255,255,0));
SetBkColor(hdc, RGB(255,0,0));
//GetSysColorBrush(COLOR_3DHILIGHT);
}else{

orig_func = (LPFUNC)(l_info->target_func_ptr);

// Do real work here. We have set up a frame for powerful code injection.

for(i = 0; i < 5; i ++)
((LPBYTE)(l_info->target_func_ptr))[I] = l_info->old_op[I];
__asm { cpuid }
//orig_func(hwnd, message, wParam, lParam);
retval=orig_func();
for(i = 0; i < 5; i ++)
((LPBYTE)(l_info->target_func_ptr))[I] = l_info->op[I];
}
return retval
}

BOOL MakeBrush(){

char * wndname = "PopPad1";
HWND hwnd = FindWindow(wndname, 0);
brush = CreateSolidBrush(RGB(255,0,0));
if(hwnd == 0 || brush == 0){
MessageBox(0, "Error during DLL HOOKING", 0, 0);
return FALSE;
}

//SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH));
InvalidateRect(hwnd, 0, 1);
return TRUE;
}



__declspec(dllexport) BOOL HookFunc(struct t_inj_info * example){

BOOL retval;
BYTE op[5];
DWORD oldpageattr, i, offset, opbytes;
char * mutex_name = "editctl_mutex";
HANDLE worker_mutex;

l_info = example;

//worker_mutex = CreateMutex(NULL, TRUE, mutex_name);
worker_mutex = OpenMutex(SYNCHRONIZE, FALSE, mutex_name);
if(worker_mutex == 0) return FALSE;

retval = VirtualProtect( (LPVOID)(((DWORD)(example->target_func_ptr))&0xFFFF1000),
0x1000, PAGE_EXECUTE_WRITECOPY, (PDWORD)&oldpageattr);
if(retval == FALSE) return FALSE;

offset = ((LPBYTE)(DWORD)new_func - (LPBYTE)(DWORD)(example->target_func_ptr))-5;
op[0] = 0xe9; // far jump
for(i = 0; i < 4; i ++)
op[i+1] = (BYTE)( (offset >> (8*i)) & 0xff );
opbytes = 5;

for(i = 0; i < 5; i ++){
example->old_op[I] = ((LPBYTE)(example->target_func_ptr))[I];
example->op[I] = op[I];
((LPBYTE)(example->target_func_ptr))[I] = op[I];
}
_asm { cpuid }

example->hookapi_addr = (DWORD)new_func;
if(!MakeBrush()) goto out;
//example->hookapi_addr = (DWORD)worker_mutex;
//ReleaseMutex(worker_mutex);
// We'll wait for the main controler to tell us to terminate: InjectThread app in this case
// After sleeping for 60 seconds, the control application is guranteed to have obtained
// the released mutex and reset its state (through the magic multi-tasking, Sleep yields CPU).
// Wait till control application release it.
//Sleep(1000);
WaitForSingleObject(worker_mutex, INFINITE);
CloseHandle(worker_mutex);
out:
// Clean up our track
for(i = 0; i < 5; i ++)
((LPBYTE)(example->target_func_ptr))[I] = example->old_op[I];
return TRUE;
}

bilbo
July 16th, 2004, 09:10
to Silver
Quote:
Don't you need to release the brush object (Select null into the GDI object) then call DeleteObject() on the brush as well?

well, Silver, thanks for the warning, you are right under a theoretical point of view.
But, just because curious, I dumped the GDI table during and after Notepad execution. As soon as the Notepad is killed, the handle is invalidated, so there is no need to DeleteObject(). I was surprised too. This happens on Wiondows XP. I don't know about previous M$ systems.
Look!
during subclassed Notepad execution...
Code:
0641.E14C8BA0 068C 0 E410 7800A8 10 (BRUSH_TYPE)

0641 is the table slot, E14C8BA0 is the pointer to kernel object, 68C is the process id, E410 is the high part of the handle (the low part is the slot, so the handle you obtain from CreateSolidBrush is E4100641), 7800A8 is the pointer in process memory, 10 is the type

after Notepad is exited
Quote:
0641. 64F 0000 0 E510 0 0

64F is no more a kernel pointer but a link to the next free slot, E510 maybe a hint for the next handle to be created, 0 the pid, 0 the type.

to homersux
Your answer is interesting but I did not have yet the time to try it; and I work on another platform.
Just a suggestion. Why don't you grab ntoskrnl symbols??? It would be a lot easier for you to analyze M$ kernel "sources"!

Best regards, bilbo

Silver
July 16th, 2004, 11:01
That's very interesting... I'll remember that. Maybe it's a hangover from the NT4 days that they've fixed in (maybe) 2000 and XP... I sure remember being able to run out of GDI handles/objects in 9x and NT... Thanks for posting that update bilbo!

dELTA
July 16th, 2004, 12:02
Yeah, GDI/User resource handling in Windows 9x sucks, they are even allocated from a fixed pool, so if your program has a too complex GUI, you can run out of them even without having a leak... This was fixed too in 2000/XP though.

homersux
July 16th, 2004, 21:05
Silver, on 2000/XP, any process owned kernel objects will be deleted once
the object is no longer referenced (count == 0). The kernel can refer
back to the owner through a field in kernel object's structure ( normally
the 2nd dword).

So there is no need to release resources explicitly in a user application that
really doesn't use a lot of resource. But if your application is a resource hog,
you want to release resource (including kernel objects) immediately after
its lifetime is over so your application can proceed.

Peres
July 18th, 2004, 17:00
I have taken a different - more classic - approach. I patched notepad and gave it a new menu item allowing the user to select the background color from the common 'color picker' window. I can upload it together with some sort of document if someone is interested. Please notice that my notepad is localized in italian...

Peres

bilbo
July 19th, 2004, 06:59
Surely could be interesting...

Quote:
Please notice that my notepad is localized in italian...


I'm afraid you have also to take into account the platform, not just the localization...

Regards, bilbo

homersux
July 19th, 2004, 20:00
I definitely would like to read about what you've done, Peres. Good work!

Peres
July 20th, 2004, 12:47
I didn't know if I could enclose my patched version of notepad for copyright issues... I uploaded it in the end. After all, it is a better product now. You can see this new feature clicking on 'Sfondo...' from menu 'Formato'.

Warnings:

I patched italian notepad for win2000.
Number base (hex or dec) isn't always specified; however it is always easy to recollect from the context.
Offsets are meant to be RVAs, except when explicitly specified.

---------------------------------------------------------------------------
THE TASK
---------------------------------------------------------------------------

I am going to add the following piece of code to notepad's own window procedure; I show C code since detailed asm code would be too long to list here. I think the code is pretty straightforward. It uses the color common dialog from comdlg32 in order to allow the user to pick his/her favourite color, then it updates the gdi brush when asked to.

if ( uMsg == WM_COMMAND && HIWORD(wParam) == IDM_SETBGCOLOR ) {

memset( &CC, 0, sizeof(CC) );
CC.lpStructSize = sizeof(CC);
CC.hwndOwner = hwndNotepad;
CC.lpCustomColors = &CustomColors;
CC.rgbResult = rgbBrush;
CC.Flags = CC_RGBINIT;

if ( !ChooseColorW( &CC ) ) {
rgbBrush = CC.rgbResult; // saves new rgb brush color
bBrushChanged = TRUE; // flags new brush

InvalidateRect( hWnd, NULL, TRUE ); // forces repaint
}

return 0;

} else
if ( uMsg == WM_CTLCOLOREDIT && (HWND)(lParam) == hwndEdit ) {

if ( bBrushChanged == TRUE ) {
if ( hBrush ) DeleteObject( hBrush ); // deletes brush
hBrush = CreateSolidBrush( rgbBrush );// new brush
}

SetBkColor( (HDC) wParam, rgbBrush ) ;// sets text background color (mandatory)
bBrushChanged = FALSE;
return hBrush;
}

The main quirk here is the fact the snippet invokes three api functions which notepad doesn't import, namely ChooseColorW (from comdlg32.dll), CreateSolidBrush and SetBkColor (from gdi32.dll).

The tasks are the following:
1. add new imports to notepad
2. modify its window procedure

---------------------------------------------------------------------------
GATHERING REQUIREMENTS
---------------------------------------------------------------------------

Room required by data structures used in the snippet above:

CHOOSECOLORW CC structure 24h bytes
COLORREF[16] CustomColor array 40h bytes
BOOL b_BrushChanged 4h bytes
COLORREF rgb_Brush 4h bytes
HBRUSH hBrush 4h bytes
---------
total 70h bytes

The snippet needs some functions not currently imported by Notepad:

FUNCTION DLL
CreateSolidBrush gdi32
SetBkColor gdi32
ChooseColorW comdlg32

We can make a rough estimate of the room we need for the new imports:

New functions names and ordinals 32h bytes
New import table entries (2) 50h bytes
IAT table (3 entries) Ch bytes
names arrays pointers (3 entries) Ch bytes
---------
9Ah bytes

Let's say 256 bytes (100h) will be surely enough.

Finally, making a good estimate for the space needed by the code is difficult, but I think 512 bytes (200h) will suffice.

In the end, we will need no more than 880 bytes (370h).

---------------------------------------------------------------------------
FINDING CAVES
---------------------------------------------------------------------------

Notepad.exe section table from PE header is summarized in the table below; the CAVE column highlights the space which is wasted for memory alignment - until now.

RVA ALIGN VIRT CAVE NAME
SIZE SIZE
1000 7000 65CA 0A36 .text
8000 2000 1944 06BC .data
A000 6000 6000 0000 .rsrc

Alignment between text and data section provides us with a lot of free space, about three times our rough estimate above. We can grow the code section and make it reach its maximum size (7000h), just add A00h bytes before offset 6C00h (where data section starts in the image file). We also need to modify the following fields in the section table of the PE header:

* text section virtual size (7000h)
* data and rsrc raw offsets to reflect the change (+A00h)

---------------------------------------------------------------------------
ADDING IMPORTS
---------------------------------------------------------------------------

A quick search for import data makes clear that .text section contains the import section beyond the actual executable code: as it is apparent from the following table, every relevant information about import is kept between RVAs 1000 and 8000.

OFFSET RVA SIZE
Import table 5C50 6650 00B4 (8 entries)
Name pointers arrays 5D04 6704 02FC
Names 6000 6A00 0BC9
Bound import table 0600 1000 02FC

Here is the actual situation of import table entries in Notepad.

NAMES THUNKS DLL
NAME ## RVA RVA RVA
advapi32 7 6704 1000 6C4C
gdi32 23 6724 1020 70BC
kernel32 48 6784 1080 6F56
mscvrt 19 6848 1144 6BBA
shell32 4 6898 1194 6AC6
user32 70 68AC 11A8 755C
winspool 3 69C8 12C4 75BC
comdlg32 9 69D8 12D4 6A8A

Here is the actual layout of import data stuff:

START END
RVA RVA
1000 1300 IAT table
1300 131C Debug data
.
.
.... 6650 Executable code
6650 6704 Import table
6704 6A00 Names pointers array
6A00 75C9 Names

Both IAT table and names pointers array must grow 12 bytes to accomodate the 3 new entries above. As you can see, there is currently no available room to comfortably enlarge them. There is a great number of paths we can tread from now on. I am taking the hardcore approach, for it has been a long time since I last delved into import table intricacies and I would like to recall.

The best way seems to add a couple of extra entries to the import table, for comdlg32 and gdi32 respectively. The import table already contains data for them both, but we are not messing with something that already works well. You can add any number of references to one library since the loader is smart enough, so why bothering.
Since we need 28 bytes (hex) to accomodate two new entries and we have got no room we shall move the import table away, say after the actual names at 75D0. The room left will immediately be handy, in fact we are putting names, names pointers and thunks there. Choosing a layout is a matter of personal taste, and here is mine:

RVA NEW CONTENT
6650 names pointers for extra comdgl32 (666C)
6654 zero
6658-665C names pointers for extra gdi32 (667B)
6660 zero

666C ordinal and name for 'ChooseColorW'
667B ordinal and name for 'SetBkColor'
6688 ordinal and name for 'CreateSolidBrush'

66A4 bound address for comdlg32 function
66A8 zero
66AC bound addresses for gdi32 functions
66B4 zero

We must also grow the import table adding two entries referring to the new content above; the only information missing are the pointers to DLL names, but we can recycle the old ones.
Of course, the PE header must be updated as well. We must change the import table address (to 75D0), its size (to B4+28), and finally the IAT size since we added 3 bound entries (to 2FC+4+4+8+4 accounting for separators)

Here is the situation after the modifications:

NAMES THUNKS DLL
NAME ## RVA RVA RVA
advapi32 7 6704 1000 6C4C
gdi32 23 6724 1020 70BC
kernel32 48 6784 1080 6F56
mscvrt 19 6848 1144 6BBA
shell32 4 6898 1194 6AC6
user32 70 68AC 11A8 755C
winspool 3 69C8 12C4 75BC
comdlg32 9 69D8 12D4 6A8A
comdlg32 1 6650 66A4 6A8A
gdi32 2 6658 66AC 70BC

Notepad has 3 more imports now.

---------------------------------------------------------------------------
ADDING FUNCTIONALITY
---------------------------------------------------------------------------

I lack an effective tool for assembling short snippet of code, because I can't get SnippetCreator by Iczelion to work properly. I wrote the assembly code corresponding to the C routine above using symbolic names for globals and routine, then searched the image file for the real VAs of what I needed:

HWND hwndNotepad: HWND of notepad main window
HWND hwndEdit: HWND of notepad's multiline text box
Both handles can be easily found searching for RegisterClassW, then following the code until both windows are created dinamically and their handles stored.

DeleteObject: address of bound import
InvalidateRect: address of bound import
We can get the VAs for the imports by looking at the IAT in any disassembler.

Actual window procedure address is passed as a parameter to RegisterClassW... it is 100248Fh in my notepad.

Also, I needed to choose some VAs for my data:

CHOOSECOLORW CC 1007800h
COLORREF[16] CustomColors 1007824h
COLORREF rgbBrush 1007864h (initialized to 0FFFFFFh)
BOOL bBrushChanged 1007868h
HBRUSH hBrush 100786Ch

Substituting the VAs in the code is commonly done with regular expressions. The assembled code can be pasted at any offset: I chose 7000h. The snippet forwards control to the original window procedure if necessary using a push/ret combination. Last but not least, the call of RegisterClassW must be changed since notepad's new window procedure now start at VA 1007A00h.

It's a long and perhaps fragmented document... I feel a little sorry for my English.

Peres

homersux
July 20th, 2004, 15:14
hi, Peres, it's quality stuff. I shall be excercising your approach with my English version of notepad on w2k soon. This has been my original idea on how to do it but at that time I lack the knowledge. My api hijacking method works prety well of course but I'd like to get back onto my original idea (the way you have done it) and try hand craft PE files.

Can you explain a little bit how you added the menu item (resource editing?)

Peres
July 21st, 2004, 01:43
It's a simple matter - compared to the rest - so I forgot to explain... ResHacker is perfect for things like that. Just open notepad, and take a look at its menu resource tree. It's a very simple resource indeed, in fact it contains a single menu. Now look to the right: the grammar of rc scripts is very simple. I added the following stuff:

MENUITEM SEPARATOR
MENUITEM "S&fondo...", 9000

The ampersand makes the 'f' an accelerator key for the menu item. The only issue might be choosing an appropriate menu item id. Just look at the rest of the script and pick an unused number (less than 65536 since resource id's are 16 bit numbers - I suppose). I chose 9000 (2328h): it's the constant I named IDM_SETBGCOLOR in my C code.

Peres

P.S. have you noticed that my notepad doesn't retain the background color you choose amongst sessions? You could add some registry code for that. (I am a bit tired of messing with masm...).

Will
July 23rd, 2004, 15:47
Wasn't there a reverseme along that theme? There was a basic window with a menu with a few color choices, and you had to add the functionality for the menu items to work.

Peres
July 23rd, 2004, 16:05
I don't know of any reverseme like that. The fact is I'm pretty new... I saw a thread about changing notepad's background color, and since I never tried the ameliorating game I gave it a try.

Will
July 25th, 2004, 11:39
Well I found it:
hxxp://www.reversemes.de/reversemes.html
(It's called 'Fun With Guis' or something like that.)


It's not exactly how I remembered it though. You have to add the menu yourself, as well as the functionality. It might be worth taking a look at.


cheers,
will

homersux
July 31st, 2004, 22:12
After a little bit coding, I bring you two tools that I think are useful for this project and projects like this one. RelocateGUI and InjectFile.

The approach I have used is to write c/asm mixed code and use as much option
as the compiler(vs6) gives me to ease the task. You have noticed the in the
source code how I packed all the global variables together in a 'gvars' data
segment and the code is in 'code' segment. These directives allow me to
easily relocate the codepatch machine code. After this dll is compiled, you
can load it into lordPE or any other pe viewer to examine its sections and
IAT etc. Then I use RelocateGUI to relocate all the global variable reference
in codepatch to an arbitrary base (the New Relocation Base textfield), in my
case, I used 0x01007600 (because I will be injecting code to 0x6d00 into
notepad.exe and I want the data to be visible at 0x6c00. Both 0x6c00 and 0x6d00
are raw offsets, and they are translated to 0x01007600 and 0x01007700
as virtual addresses). After the code is relocated, use injectFile to
put this binary image into notepad.exe at raw
offset 0x6d00 (in the Offset field in injectFile). injectFile will overwrite
the data in notepad.exe at said offset, it's a replace operation not insert.
Afterwards, I make a copy of notepad.exe, I load one in lordpe and another one
in hiew for modification. This is the step 5, I need to manually replace
the thunk values referenced in codepatch, RelocateGUI is smart enough only
to replace the global variables but not the iat thunk values. Another neat
thing that would totally automate the process would be to hardcode those iat
values inside the 'gvars' data segment and propotype all the functions and
call through gvars thunk, then it'd be not necessary to manually. This is
left as an exercise to the readers. Note that in the code I popped 4 times before I jump to the original 0x0100248F wndProc, that was from observation
of the compiled codepatch assembly.

code
<pre>
#include <windows.h>
#define IDM_SETBGCOLOR 9300
typedef DWORD (*pfn_WndProc)(HWND, UINT, WPARAM, LPARAM);

//#pragma comment ( linker, "/BASE:0x01000000" // Align to notepad's image base
#pragma data_seg("gvars"
#define GVARS __declspec(allocate("gvars")
GVARS CHOOSECOLORW CC;
GVARS HWND hwndNotepad = 0;
GVARS HBRUSH hBrush = 0;
GVARS COLORREF rgbBrush = 0, CustomColors[16];
GVARS BOOL bBrushChanged = FALSE, initHwnd = FALSE;
GVARS pfn_WndProc pWndProc = (pfn_WndProc)0x100248F; // Notepad's wndproc address
GVARS BYTE notepadTitle[8] = "Notepad";
#pragma data_seg()

#pragma code_seg("code"
__declspec(dllexport) DWORD CodePatch(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){

if ( ! initHwnd){
hwndNotepad = FindWindowA(notepadTitle, 0);
if( hwndNotepad != 0)
initHwnd = TRUE;
}

if ( uMsg == WM_COMMAND && LOWORD(wParam) == IDM_SETBGCOLOR ) {

memset( &CC, 0, sizeof(CC) );
CC.lStructSize = sizeof(CC);
CC.hwndOwner = hwndNotepad;
CC.lpCustColors = CustomColors;
CC.rgbResult = rgbBrush;
CC.Flags = CC_RGBINIT;

if ( ChooseColorW( &CC ) ) {
rgbBrush = CC.rgbResult; // saves new rgb brush color
bBrushChanged = TRUE; // flags new brush

InvalidateRect( hWnd, NULL, TRUE ); // forces repaint
}

return 0;

} else if ( uMsg == WM_CTLCOLOREDIT) { // && (HWND)(lParam) == hwndEdit ) {

if ( bBrushChanged == TRUE ) {
if ( hBrush ) DeleteObject( hBrush ); // deletes brush
hBrush = CreateSolidBrush( rgbBrush );// new brush
SetBkColor( (HDC) wParam, rgbBrush ) ;// sets text background color (mandatory)
bBrushChanged = FALSE;
}
return (DWORD)hBrush;
} else
__asm {
pop edi
pop esi
pop ebx
pop ebp
jmp pWndProc
}

return 0;
}
#pragma code_seg()
</pre>

I have uploaded the two tools and the dlls (extrememly useful) in this post. Hope you found them useful for your own projects.

Peres
August 1st, 2004, 03:56
I will give a try to your tools. I believe my handcraft approach is still less convoluted, though.

Peres

P.S.: hey! You still didn't save the background color in the registry...