Reversing 'Adware' by Modifying Window Display Properties

by Kayaker

Published by +Tsehp, November 2000.

Comment : A very useful essay to easily locate some general nagging functions.

Target: Frazzle V1.0
              http://bindweed.com/frazzle/frazzle.htm 640Kb
              http://www.zdnet.com

Tools Used: SoftIce, W32Dasm, Win32 Programmer's Reference, Hexeditor
- An API monitor (i.e. APISpy32)
- The API Viewer, apiload.exe, that comes with the Visual Studio package to determine Constant values for API parameter flags. Available as a standalone package somewhere ;)
- A Windows Class Identifier (WinShow, Windowse). I used WinFrog which is FrogPrint's version of WinShow which displays the Hwnd in Hex instead of decimal (available at http://frogsprint.hypermart.net/main/tool.pl?GO)

Preamble: Greetings Fravias! Adware. A nasty concept in "freeware" development that makes it even more satisfying to reverse. Frazzle, a challenging 5-star rated puzzle game, is a typical Adware program that displays its own advertisements in a banner header. If you are connected to the Internet and click on the banner, it downloads more ads via advert.dll and inundates your senses even further. I'm sorry but this type of behaviour is just begging to be reversed ;-)

General Structure and Code Flow:

The program is written in MS Visual C++ 5.0. Above the main "puzzle" part of the program proudly flies the Ad Banner, and next to it an About button. At the bottom is a row made up of the rest of the controls. After retrieving the Taskname in SoftIce with 'Task', type 'Hwnd [Taskname]' to get a view of all the Controls (windows) in the program and their relationship to each other. You can see from the table below that most of the Controls are Child windows of the main Parent window AfxFrameOrView42s. I've only included the Window Handle (Hwnd, which changes each time the program is run) and Class Name from the Hwnd command and added a Description.

Window Handle Class Name Description
0978(1)   AfxFrameOrView42s Parent window
  07D8(2) Button 'Help'
  0840(2) Button 'Custom'
  085C(2) Button 'Options'
  0854(2) Button 'Restart'
  0860(2) Button 'New'
  0844(2) ComboBox ComboBox
  0E84(2) Button 'About'
  0E7C(2) AfxWnd42s Puzzle window
  0820(2) Button unnamed button
  07FC(2) AfxWnd42s Ad Banner window
07AC(1) ListBox ListBox

Now that we have an idea of the individual components making up the program, what we're going to want to do is:

1. Eliminate the Ad Banner window.
2. Eliminate the About button because it happens to be next to the Ad Banner window and is in the way.
3. Move the main Puzzle window up and increase its size to cover the blank space left by 1. and 2.
4. Get rid of the Nag when you delete advert.dll from your system.

There are 3 API's that we need be concerned with - CreateWindowExA, ShowWindow and SetWindowPos.

The CreateWindowEx function creates an overlapped, pop-up, or child window with an extended style.

HWND CreateWindowEx(

DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu, or child-window identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data

Monitoring CreateWindowExA is used here more to keep tabs on what is going on and to have a reference to the Hwnds.

The ShowWindow function sets the specified window's show state.

BOOL ShowWindow(

HWND hWnd, // handle of window
int nCmdShow // show state of window

nCmdShow

Specifies how the window is to be shown. This parameter is ignored the first time an application calls ShowWindow, if the program that launched the application provides a STARTUPINFO structure. Otherwise, the first time ShowWindow is called, the value should be the value obtained by the WinMain function in its nCmdShow parameter. In subsequent calls, this parameter can be one of the following values:

SW_HIDE Hides the window and activates another window.

SW_SHOW Activates the window and displays it in its current size and position.

We'll change the ShowWindow nCmdShow parameter value from its default of SW_SHOW = 5 to SW_HIDE = 0 to eliminate the Ad Banner window.

The SetWindowPos function changes the size, position, and Z order of a child, pop-up, or top-level window. Child, pop-up, and top-level windows are ordered according to their appearance on the screen. The topmost window receives the highest rank and is the first window in the Z order.

BOOL SetWindowPos(

HWND hWnd, // handle of window
HWND hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
UINT uFlags // window-positioning flags

X - Specifies the new position of the left side of the window.
Y - Specifies the new position of the top of the window.
cx -
Specifies the new width of the window, in pixels.
cy - Specifies the new height of the window, in pixels.

All coordinates for child windows are client coordinates (relative to the upper-left corner of the parent window's client area).

uFlags
Specifies the window sizing and positioning flags. This parameter can be a combination of the following values:

SWP_NOACTIVATE Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).

SWP_SHOWWINDOW Displays the window.

We'll use the SetWindowPos uFlags parameter to get rid of the About button by changing it from SWP_SHOWWINDOW = 40 to SWP_NOACTIVATE = 10.

And to cover up the blank space left, we'll modify the SetWindowPos call of the Puzzle window AfxWnd42s by resetting its values for Y and cy.


OK, the next thing to do is to take a look at the API monitor output. I've included all the pertinent calls so you get an idea of the code flow. It may be a little confusing to read, but hey, that's the output ;). All the Calls are listed in the order in which they occur, and I've grouped them into 3 tables so they can (hopefully) be easier to read. I've also highlighted the Calls we'll be dealing with directly.

Each Call is listed as a pair of lines, the 1st is the Call itself and is composed of the address of the Call, the name of the Call, and then within the brackets, each of the stack parameter values before the call is made. The 2nd is the Return value (for CreateWindowExA the Return value is the Hwnd, for the others it's generally just a zero/nonzero flag).

A few things to note:

- If you check carefully, you see that all the CreateWindowExA calls for the Child windows are bracketed within the CreateWindowExA call for the Parent window. This is important to note so you don't get screwed up when tracing in SoftIce. The CreateWindowExA call for the main window AfxFrameOrView42s does not return until all the other CreateWindowExA calls are finished. I've indented the Child window calls to highlight this.

- The same applies for ShowWindow for AfxFrameOrView42s, each of the Child windows goes through a SetWindowPos call before ShowWindow returns.

- When you start the program for the 1st time, a file "Prefs.SAV" is produced. As well as having information on any customizing of puzzles you do, it also contains the default height and width of the parent window. If you resize the program it modifies these values (242 and 186) and remembers them next time. This is the function of CreateFileA and ReadFile. This has nothing to do with the Ad window, but I include it anyway.

0041D0AD:CreateFileA(LPSTR:011E087C:"Prefs.SAV", DWORD:80000000, DWORD:00000000, LPDATA:0069FC78, DWORD:00000003, DWORD:00000080, HANDLE:00000000)
0041D0B3:CreateFileA = 14
0041D10B:ReadFile(HANDLE:00000014, LPDATA:010D34CC, DWORD:00001000, LPDATA:0069FC9C, LPDATA:00000000)
0041D111:ReadFile = 1

 

0041A40B:CreateWindowExA(DWORD:00000200, LPSTR:004277E0:"AfxFrameOrView42s", LPSTR:0042D0F8:"Frazzle", DWORD:000F0000, DWORD:0000000A, DWORD:0000000A, DWORD:0000021A, DWORD:0000017C, HWND:00000000, HANDLE:00000000, HANDLE:00400000, LPDATA:00000000)

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:004277A8:"AfxWnd42s", LPSTR:00000000, DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:00000000, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = E7C ; Puzzle window

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:0042868C:"BUTTON", LPSTR:0042D118:"About", DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:0000006B, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = E84 ; About Button

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:0042868C:"BUTTON", LPSTR:0042D144:"New", DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:00000071, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 860

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:0042868C:"BUTTON", LPSTR:0042D13C:"Restart", DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:00000072, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 854

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:0042868C:"BUTTON", LPSTR:0042D134:"Options", DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:0000006F, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 85C

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:0042868C:"BUTTON", LPSTR:0042D12C:"Custom", DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:00000073, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 840

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:00428760:"COMBOBOX", LPSTR:00000000, DWORD:40000003, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:0000006C, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 844

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:0042868C:"BUTTON", LPSTR:0042D124:"Help", DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:0000006A, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 7D8

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:0042868C:"BUTTON", LPSTR:0043121C, DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:00000010, DWORD:00000010, HWND:00000978, HANDLE:0000006D, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 820

0041A40B: CreateWindowExA(DWORD:00000000, LPSTR:004277A8:"AfxWnd42s", LPSTR:00000000, DWORD:40000000, DWORD:00000000, DWORD:00000000, DWORD:000001D4, DWORD:0000003C, HWND:00000978, HANDLE:00000000, HANDLE:00400000, LPDATA:00000000)
0041A411: CreateWindowExA = 7FC ; Ad Banner


00418FFC: ShowWindow(HWND:000007FC, DWORD:00000005) ; Ad Banner
00419002: ShowWindow = 0

0041A411:CreateWindowExA = 978

 

00418FFC:ShowWindow(HWND:00000978, DWORD:0000000A)

Puzzle window:

00418FC6: SetWindowPos(HWND:00000E7C, HWND:00000000, DWORD:00000000, DWORD:0000003C, DWORD:0000020E, DWORD:000000FE, DWORD:00000040)
00418FCC: SetWindowPos = 1

About Button:

00418FC6: SetWindowPos(HWND:00000E84, HWND:00000000, DWORD:000001D4, DWORD:00000000, DWORD:0000003A, DWORD:0000003C, DWORD:00000040)
00418FCC: SetWindowPos = 1

00418FC6: SetWindowPos(HWND:00000844, HWND:00000000, DWORD:00000000, DWORD:0000013A, DWORD:00000058, DWORD:0000012C, DWORD:00000040)
00418FCC: SetWindowPos = 1

00418FC6: SetWindowPos(HWND:00000860, HWND:00000000, DWORD:00000058, DWORD:0000013A, DWORD:00000058, DWORD:0000001E, DWORD:00000040)
00418FCC: SetWindowPos = 1

00418FC6: SetWindowPos(HWND:00000854, HWND:00000000, DWORD:000000B0, DWORD:0000013A, DWORD:00000058, DWORD:0000001E, DWORD:00000040)
00418FCC: SetWindowPos = 1

00418FC6: SetWindowPos(HWND:0000085C, HWND:00000000, DWORD:00000108, DWORD:0000013A, DWORD:00000058, DWORD:0000001E, DWORD:00000040)
00418FCC: SetWindowPos = 1

00418FC6: SetWindowPos(HWND:00000840, HWND:00000000, DWORD:00000160, DWORD:0000013A, DWORD:00000058, DWORD:0000001E, DWORD:00000040)
00418FCC: SetWindowPos = 1

00418FC6: SetWindowPos(HWND:000007D8, HWND:00000000, DWORD:000001B8, DWORD:0000013A, DWORD:00000056, DWORD:0000001E, DWORD:00000040)
00418FCC: SetWindowPos = 1

00419002:ShowWindow = 0

OK, I hope the tables help you navigate through the output and understand how to isolate and home in on the components that need reversing. Now, back to what we're trying to do.


1. Eliminate the Ad Banner window:

We need to change the ShowWindow nCmdShow parameter value from its default of SW_SHOW = 5 to SW_HIDE = 0 to eliminate the Ad Banner window.

00418FFC: ShowWindow(HWND:000007FC, DWORD:00000005)

You see from the API monitor output that the Call is at 418FFC and is the 1st ShowWindow call encountered. So if you load the program with SoftIce loader32.exe and set a BPX 418FFC and press F5, SoftIce will break on that address.

:00418FF5 PUSH DWORD PTR [ESP+04]
:00418FF9 PUSH DWORD PTR [ECX+1C]
:00418FFC CALL [USER32!ShowWindow]

Type "dd ESP" while you are ON 418FFC to display the stack parameters of the Call and confirm you have the right call:

:dd esp
:0069F878 000007FC 00000005

Now return from the "call to the Call" by F10 stepping over the ShowWindow call until you reach a RET, or just press F12, and you'll RETurn to 407ECC. Notice the code above it and you see

:00407EC3 6A05 PUSH 05
:00407EC5 MOV ECX,ESI
:00407EC7 CALL 00418FEE ; to ShowWindow
:00407ECC MOV EAX,[ESI+1C]

The PUSH 05 statement PUSHes the value of 05 onto the stack of the Call at :00407EC7 and becomes DWORD PTR [ESP+04] in the ShowWindow Call, or the nCmdShow parameter value of SW_SHOW = 5. To change it to SW_HIDE = 0 and get rid of the Ad Banner window, we simply change

:00407EC3 6A05 PUSH 05
to
:00407EC3 6A00 PUSH 00

You can use W32Dasm to find out that address 407EC3 is at hex location 72C3 in the file and make the change in a hex editor.

Et Viola, no more Ad window! Just like that, careful use of an API monitor yields us a victory, no fuss no muss ;-)


2. Eliminate the About button:

Use the SetWindowPos uFlags parameter to get rid of the About button by changing it from SWP_SHOWWINDOW = 40 to SWP_NOACTIVATE = 10.

00418FC6: SetWindowPos(HWND:00000E84, HWND:00000000, DWORD:000001D4, DWORD:00000000, DWORD:0000003A, DWORD:0000003C, DWORD:00000040)

Notice in the API output listing that the About button is dealt with in the 2nd SetWindowPos call at 418FC6. If you set a BP on this address and allow SoftIce to break twice you reach:

:00418FB3 PUSH DWORD PTR [EBP+1C]
:00418FB6 PUSH DWORD PTR [EBP+18]
:00418FB9 PUSH DWORD PTR [EBP+14]
:00418FBC PUSH DWORD PTR [EBP+10]
:00418FBF PUSH DWORD PTR [EBP+0C]
:00418FC2 PUSH EAX
:00418FC3 PUSH DWORD PTR [ECX+1C]
:00418FC6 CALL [USER32!SetWindowPos]

And a "dd ESP" displays the stack

:0069FA54 00000E84 00000000 000001D4 00000000
:0069FA64 0000003A 0000003C 00000040

Now if you RETurn from the "call to the Call" as before you spy the following code. Note that this comes immediately after the 1st SetWindowPos call for the Puzzle window RET, which we'll deal with later.

:00401BE4 CALL 00418F9F ; SetWindowPos call for Puzzle window
:00401BE9 MOV EBX,[ESP+28]
:00401BED MOV EDX,[ESP+20]
:00401BF1 SUB EBX,EDX
:00401BF3 6A40 PUSH 40 ; SWP_SHOWWINDOW uFlag parameter
:00401BF5 SUB EBX,000001D4
:00401BFB PUSH 3C
:00401BFD PUSH EBX
:00401BFE LEA EBP,[ESI+000032F4]
:00401C04 PUSH 00
:00401C06 PUSH 000001D4
:00401C0B PUSH 00432AF0
:00401C10 MOV ECX,EBP
:00401C12 CALL 00418F9F ; SetWindowPos call for About button

SoftIce helps confirm that the PUSH 40 statement is where the uFlags parameter SWP_SHOWWINDOW is set to 40. We want to change it to SWP_NOACTIVATE = 10 by changing

:00401BF3 6A40 PUSH 40
at FF3h to
:00401BF3 6A10 PUSH 10

Done. Now we've got a nice big blank space where the Ad Banner and About button used to be. Let's make it more aesthetically pleasing...

 

3. Move the main Puzzle window up and increase its size to cover the blank space left by 1. and 2.:

We need to modify parameters 4 and 6 of SetWindowPos, vertical position Y and height cy.

00418FC6: SetWindowPos(HWND:00000E7C, HWND:00000000, DWORD:00000000, DWORD:0000003C, DWORD:0000020E, DWORD:000000FE, DWORD:00000040)

Proceeding as above, the stack for the SetWindowPos Call:

:0069FA54 00000E7C 00000000 00000000 0000003C
:0069FA64 0000020E 000000FE 00000040

Notice that of the 3rd and 4th stack parameters, only the 4th or the Y vertical position of the Puzzle window relative to the upper-left corner of the parent window is defined (3C). This is of course to allow for the height of the Ad Banner that lies above it, which is set in the 8th stack parameter of the Ad window CreateWindowExA call. The height of the About button is updated to 3C in its SetWindowPos call to allow it to fit into the space allocated.

Examing the code preceding the Call:

:00401BB1 CALL [USER32!GetClientRect]
:00401BB7 MOV EDI,[ESP+2C] ; 158h = total height allocated to fit in all the controls in the program's main window
:00401BBB MOV ECX,[ESP+24] ; 0
:00401BBF SUB EDI,ECX ; edi remains 158h
:00401BC1 MOV EAX,[ESP+28] ; 20E = width of main window
:00401BC5 SUB EDI,1E ; total height - height of bottom row of buttons = 13A remaining
:00401BC8 PUSH 40 ; SWP_SHOWWINDOW uFlag parameter
:00401BCA LEA ECX,[ESI+00003070]
:00401BD0 8D57C4 LEA EDX,[EDI-3C] ; [13A - height of Ad window] = FE remaining
:00401BD3 PUSH EDX ; height left over for Puzzle window = FE
:00401BD4 MOV EDX,[ESP+28] ; 0
:00401BD8 SUB EAX,EDX ; 20E - 0 = 20E
:00401BDA PUSH EAX ; width = 20E
:00401BDB 6A3C PUSH 3C ; vertical "y" offset of Puzzle window from upper left corner
:00401BDD PUSH 00 ; "x"
:00401BDF PUSH 00432AF0
:00401BE4 CALL 00418F9F ; SetWindowPos

There's 2 things we need to do here. One is to change the "y" position from

:00401BDB 6A3C PUSH 3C
to
:00401BDB 6A00 PUSH 00
at hex location FDB

And the other is to increase the height of the window to fill in that space. You can see that

:00401BD0 8D57C4 LEA EDX,[EDI-3C]
takes into account the height of now non-existent Ad window, so we can dispense with the 3C offset and change this to
:00401BD0 8D17 lea edx, dword ptr [edi]
:00401BD2 90 nop

at hex location FD0

That about does it except for

4. Get rid of the Nag when you delete advert.dll from your system:

Simple enough trace using MessageBoxA to find that FindFirstFileA is used to check if the file exists when you select some of the functions (some irrelevant code missing below)

* Referenced by a CALL at Addresses:
|:0040191B , :0040194B , :00401A24 , :0040247E , :00402498
|
:004025A0 A118124300 mov eax, dword ptr [00431218] ; a flag in the uninitialized section of .data in memory, always 0
:004025A5 81EC40010000 sub esp, 00000140
:004025AB 85C0 test eax, eax
:
:004025B1 740B je 004025BE ; I changed this to 7400 je 4025B3 to miss the FindFirstFileA call entirely
:004025B3 B001 mov al, 01
:
:004025BD C3 ret

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004025B1(C)
|
:004025BE 8D442408 lea eax, dword ptr [esp+08]
:004025C2 C7051812430001000000 mov dword ptr [00431218], 00000001
:004025CC 50 push eax

* Possible StringData Ref from Data Obj ->"advert.dll"
|
:004025CD 6884D14200 push 0042D184

* Reference To: KERNEL32.FindFirstFileA, Ord:0082h
|
:004025D2 FF1524674300 Call dword ptr [00436724]
:004025D8 83F8FF cmp eax, FFFFFFFF
:004025DB 7426 je 00402603
:

* Reference To: KERNEL32.FindClose, Ord:007Eh
:

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004025DB(C), :004025EE(C), :004025F6(C)
|
:00402603 6A10 push 00000010

* Possible StringData Ref from Data Obj ->"Error"
|
:00402605 687CD14200 push 0042D17C

* Possible StringData Ref from Data Obj ->"ADVERT.DLL is missing or corrupt!"
|
:0040260A 6858D14200 push 0042D158
:0040260F 8BCE mov ecx, esi
:00402611 E855DA0100 call 0042006B ; MessageBox


Postamble:

There you have it. Pretty straightforward if you can sort out the sequence of API calls. Nothing beats a good API monitor and the Win32 Programmer's Reference ;-) But is the program truly "reversed" by modifying its window display properties? Well yes and no. Visually it is of course, and no longer can it connect to its home page if you inadvertently click on the Ad window. But in the background there is still processing going on which is trying to alternately display a couple of its own advertisements (which are built-in bitmap Resources BTW), with calls to GetWindow, CallWindowProcA, DefWindowProcA, GetTopWindow etc. This seems to indicate that we can't get rid of the Ad Banner CreateWindowExA call entirely without a major code overhaul. Interestingly, if you move the Puzzle window (step 3) before removing the Ad window and About button, the ads still show through as they are redrawn. You can actually see the 3 windows competing with each other as they are refreshed.

So if you want to have a go at stomping out these in-your-face CPU-sucking billboards, there's plenty of sites specializing in 'Adware'. Anyway, if you have any questions or comments I'd love to hear from you, just don't send me any Ads...

Big Greetz and Hurrahs to the new improved RCE Regroupment Board. Long may you live and Reverse.

Cheers,

Kayaker
Nov. 2000