Saturday, December 5, 2009

Other Protection Tricks

Here are some other protection tricks.



API Hook Detection



An API hook is an exchange of an API function for some other code. For example, if your program calls the API function MessageBoxA, and some other program has placed an API hook on this function, your program will first perform a routine to replace this particular API function. Only once that routine has completed can the program call the original API function, though it doesn't have to. Thus, the program secures the API function and it may, for example, check the values by which the function was called, as well as the return values. API hooks may endanger the security of your programs, so I'll show you how to protect against them.



This trick takes advantage of the two functions VirtualQuery and GetModuleHandleA. The former is used to find the current address of the user of the API function that you are testing, which in this case is MessageBoxA. Next, the program finds the address of the real API function's user by means of GetModuleHandleA. (If the address is MessageBoxA, the user is the USER32.DLL library.) The program then compares the values it finds, and if they are different, there is an API hook on the function.



.386

.MODEL FLAT/STDCALL

locals

jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC

Extrn VirtualQuery : PROC

.data message1 message3 message2 delayESP previous

Memory_structure:

pvBaseAddress pvAllocBase dwAllocProt dwRegionSize dwState dwProtect dwType

db "Detection of an API hook on the MessageBoxA function",0

db "API hook found",0

db "API hook not found",0

dd 0 ;the ESP register saves here

dd 0 ;saves the address of the previous SEH here

;saves the return values of the API function

;VirtualQuery here

dd 0 dd 0 dd 0 dd 0 dd 0 dd 0 dd 0

User db "USER32.DLL",0 ;enter the source DLL for the tested API

;function here

.code

Start:

;Sets the SEH in case of an error

mov [delayESP], esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

mov eax, dword ptr [Detection+1]

;searches for the IAT. This will put the offset ;of the jump to the API function MessageBoxA ;from the call MessageBoxA instruction into the ;eax call MessageBoxA= (E8xxxxxxxx). You will ;actually read the xxxxxxxx value.

add eax, offset Detection+5+2

;adds the offset of the beginning of the jump
;table +5=call MessageBoxA= (E8xxxxxxxx)+2= jmp
;first_API_function (FF25xxxxxxxx)
mov eax, [eax] ;reads the offset of the jump jmp

;first_API_function (FF25xxxxxxxx); you will
;actually read the xxxxxxxx value.
mov eax, [eax] ;the program will eventually read the final real

;address of the API function routine, ;MessageBoxA, inthe memory from the Import

;table

call VirtualQuery, eax, offset Memory_structure, 4*7

;calls the API function VirtualQuery with the ;following parameters:

;eax = the address of the MessageBoxA routine in ;the memory offset

;Memory_structure = the buffer offset for the ;return values

;4*7 = the buffer size in dword (words). The ;program will use this call to find the address ;of the API function MessageBoxA user. The ;address will be in the buffer at the ;pvAllocBase offset. call GetModuleHandleA, offset User

;finds the DLL address that is supposed to use

;the API function (USER32.DLL)

push eax ;saves the return value

;Sets the previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

pop eax test eax,eax jz continue

cmp eax, [pvAllocBase]

;restores the return value

;the program will jump if there was an error ;and eax=0

;compares the addresses to see if the right ;module is really the user jnz jump ;if the addresses aren't equal, the program will ;jump because it has found an API hook of the ;particular function.





error:

call MessageBoxA,0, offset message3, offset message1,0 call ExitProcess, -1

;sets a new SEH service in case of an error.

mov esp, [delayESP] push offset continue

ret

Detection:

;this label may be anywhere that the called API ;function MessageBoxA can be.

call MessageBoxA

ends

end Start







This trick isn't limited to searching for an API hook on the MessageBoxA function; it is easily modified for any API function. When using this method, it is important to correctly set the library that uses the particular function.



The program can test to see if there is an API hook before calling any API function, but if you test too much, you may make the program terribly slow. The testing routine can be modified to get the information about the imported functions directly from the Import table of the file's PE header.



This trick works only in Windows 9x.

Anti-ProcDump Trick



This trick tries to prevent ProcDump from dumping a program. It's a good idea to consider using this trick in your application because ProcDump is currently the most frequently used decompressor, able to decompress almost all known compression and encoding programs (see Figure 9.1). If you use this trick, it will be impossible for a cracker to use ProcDump to decode your program.

You must use different routines for Windows 9x and Windows NT, because the process is different for each operating system. ANAKiN was the first person to use this method of fighting ProcDump in PE-SHiELD.



.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC .data

message1 db "Anti-ProcDump trick",0

message2 db "Increased the file size in its PE header",0

delayESP dd 0 ;the ESP register saves here.

previous dd 0 ;the ESP register will save the address of the

;previous SEH service here.

.code

Start:

;Sets SEH in case of an error

mov [delayESP], esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

mov eax, fs:[30h] ;reads the Windows version

test eax,eax ;tests the Windows version

js found_win9x ;the program will jump if Windows 9x is running

found_winNT: ;the procedure for Windows NT
mov eax,[eax+0ch]
mov eax,[eax+0ch]

add dword ptr [eax+20h], 3000h ;increases the program's size

jmp end

;jump, finished

found_win9x:



push 0

call GetModuleHandleA test edx,edx jns end

cmp dword ptr [edx+08], -1 jne end

mov edx, [edx+4]

add dword ptr [edx+50h],3000

;the procedure for Windows 9x ;finds the module handle

;the program jumps if there was an error ;tests for the value -1

;if the value is not -1, there was an error, and ;the program jumps

;finds the address of the file's PE header ;increases the size in such a way that it will ;change the item SizeOfImage in the PE header

end:

;Sets the previous SEH service



push dword ptr [previous]

call SetUnhandledExceptionFilter



continue:





error:

call MessageBoxA,0, offset message2, offset message1,0 call ExitProcess, -1

mov esp, [delayESP] push offset continue ret





;sets a new SEH service in case of an error.

ends

end Start







Unfortunately, it isn't enough to add this trick into an application that has been encoded or compressed by a program that may be decompressed by ProcDump because, once added, the code won't run at all. In such a case, this trick must be placed directly into the decoding routine.



There are only two ways to place the code into the decoding routine: Change the code of the compression program, or create your own compression program. The first method is easier, because you can find the code for compression programs on the Internet, at which point it isn't hard to modify the code and compile it. The best way, of course, is to program your own compression program, but that's a lot more difficult.





Switching a Running Program from Ring3 to Ring0



Most programs run in ring3, where you cannot make certain calls, like reading debug registers or performing VxD calls. Some programs, however, run directly in ring0, such as VxD files in Windows 9x, and Sys files in Windows NT, 2000 and XP.



I'm aware of three ways to switch a normal program running in ring3 into ring0, but only in Windows 9x. Windows NT, 2000, and XP systems were secured against these methods because of the prevalence of viruses that take advantage of them. (Older Windows NT versions did allow this switch, but after it was misused a few times, the possibility was removed from the system.)



As such, it is very important to test for the type of operating system before you use this call, or the program will end with an error in Windows NT, 2000 and XP.



Switching into Ring0 Using the LDT (Locale Descriptor Table)



This is the oldest method of switching into ring0. It isn't used much anymore because the other two methods are better. This method of switching over into ring0 using the LDT was often used by viruses, but not often used with normal applications. Here's an example, anyway, because you may encounter it.

.386

.MODEL FLAT,STDCALL

locals

jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC

.data

message1

message2

message3

delayESP

previous

gdt_ call_

o_gate

db "An example of switching into Ring0 using the LDT",0 db "An error occurred",0

db "Ring0 was successfully activated",0

;the ESP register saves here

;the address of the previous SEH service will ;be saved here.

;a segment for RING0

dw 0

.code

Start:

;Sets SEH in case of an error

mov [delayESP], esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

mov eax, offset ring0

mov [o_gate],ax

shr eax,16

mov [o_gate+6],ax

xor eax, eax

sgdt fword ptr gdt_

mov ebx, dword ptr [gdt_+2]

sldt ax

add ebx,eax

mov al, [ebx+4]

mov ah [ebx+7]

shl eax,16

mov ax,[ebx+2]

add eax,8 mov edi,eax

mov esi,offset o_gate

movsd movsd

call fword ptr [call_]

xor eax, eax

sub edi,8

stosd

stosd

;puts the offset of your service for RING0 into ;the eax.

;sets the address of your new RING0 service into ;the "callgate"



;saves the GDT

;gets the GDT base address

;gets the descriptor address



;gets the LDT address in the eax register. ;if you add into the eax, you will get the ;descriptor callgate address

;sets a locationin the callgate where you will ;start making changes

;puts the address of your "callgate" into the ;esi register

;and moves it into the real callgate ;which will prepare a jump into ring0 ;jumps your service into RING0

;nulls your changes in the callgate

;Sets the previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter

jmp jump

;jumps if the switch into RING0 was successful



continue:

call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1



call MessageBoxA,0, offset message3,\ offset message1,0

call ExitProcess, -1

error: ;sets a new SEH service incase of an error.

mov esp, [delayESP] push offset continue ret

;Your new RING0 service ring0:

mov eax, dr7 ;this instruction works only in RING0

retf ;returns to RING3

ends

end Start

Switching into Ring0 Using the IDT (EliCZ's Method)



The following method is called the IDT (Interrupt Descriptor Table) method, and was used for the first time by the wonderful Czech system programmer EliCZ. It was then fully used a few days later by the well-known CIH virus. This method was really revolutionary at the time, and thus the virus spread very widely and caused an awful lot of damage.



Of course, you are good guys, so you will use this method for something good. Still, I wanted to point out how good things can be misused.



.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC Interrupt equ 5 ;the interrupt number. The numbers 1 or 3 will ;make debugging more difficult.

.data

message1

message2

message3

delayESP

previous

db "An example of switching into RING0 by using the IDT (EliCZ's method)",0 db "An error occurred",0

db "Ring0 was successfully activated",0

dd 0 ;the ESP register saves the address of the

dd 0 ;previous SEH service here

.CODE Start:



;Sets SEH in case of an error

mov [delayESP], esp push offset error

call SetUnhandledExceptionFilter mov [previous], eax

push edx sidt [esp-2] pop edx

add edx,(Interrupt*8)+4

mov ebx,[edx]

mov bx,word ptr [edx-4]

lea edi,InterruptHandler

mov [edx-4],di

ror edi,16

mov [edx+2],di

push ds

push es

int Interrupt

pop es pop ds

mov [edx-4],bx

;reads the IDT into the stack

;reads the vector of the required interrupt

;reads the address of the old service of the ;required interrupt



;sets a new interrupt service

;saves the register for security

;jumps into RING0 (the newly defined INT 5h ;service)

;restores registers

;sets the original interrupt service (INT 5h)

ror ebx,16 mov [edx+2],bx

;Sets the previous SEH service

push dword ptr [previous]

call SetUnhandledExceptionFilter



jmp jump ;jumps if the switch into RING0 was successful

continue:

call MessageBoxA,0, offset message2,\ offset message1,0 call ExitProcess, -1

jump:

call MessageBoxA,0, offset message3,\ offset message1,0 call ExitProcess, -1

error: ;sets a new SEH service in case of an error

mov esp, [delayESP] push offset continue ret

;Your new INT 5h service (runs in Ring0) InterruptHandler:

mov eax,dr7 ;this instruction is functional only in RING0

iretd ;jumps back into RING3

ends

end Start







The IDT method is the one most frequently used for switching over into ring0. It has been used in many common applications that need to use ring0 for their special tasks, especially compression and encoding programs that want to prevent debugging. I have used this method in all the examples where I have taken advantage of ring0 because it's the easiest to understand.



When using this method, the program first reads the IDT using the SIDT instruction, after which it reads the address of the beginning of the interrupt vectors. Next, the program calculates the interrupt vector address to be used — Int 5h in this case.



The new service address is saved to its vector in its 16-bit DOS form — the offset first, and then the segment part of the address. Once the Int 5h interrupt is called in the following part of the code, the operating system calls the new service, which runs in ring0 like all interrupt services. Here is where you can take advantage of all the possibilities of ring0.



The service must be finished by the IRET instruction. The operating system switches the program back into ring3, and the program continues following the Int 5h instruction. Finally, you set the original Int 5h interrupt service to prevent problems.



Unfortunately, because this method needs to change the interrupt vectors and call the interrupt later, some programs, like FrogsICE, can discover it.



Switching into Ring0 Using the SEH (The Owl's Method)



The last, and probably the best, method for switching into ring0 is to use the SEH. I haven't seen this method used in any program yet, which is surprising because it has clear advantages over the preceding two methods.



The advantage of this method is that it's difficult to discover. While you can use other methods to call the SEH service and the Int 3h instruction, this solution is perfect when used with something like the previous example.



At the beginning of the program, you set a new SEH service that you then call with the Int 3h instruction, which causes an exception. The service tries to determine which exception caused it. Instruction Int 3h always causes the exception EXCEPTION_BREAKPOINT, so we can test it with our SEH service.



The program finds the address of the Context array. The Context array checks the register values in the CS and SS of the program before causing the exception, and it saves them in the Context field in the ECX and EDX registers right after the switch into ring0. (This is important for the eventual return into ring3.) The program also sets new values for the CS and SS registers in the Context field: 28h for CS and for 30h for SS, to ensure that the program will run in ring0 after the return. It also sets the flag for the CLI in the EFLAGS register, which prevents any interrupt calls.



While the program is running in ring0, it returns all the values necessary for running in ring3 to the registers. Finally, it sets an address from which the program will run, and it switches back into ring3 by means of the IRETD function.



Because this method is almost completely unknown, even programs like FrogsICE have problems detecting it.



.386

.MODEL FLAT,STDCALL

locals jumps

UNICODE=0

include w32.inc

Extrn SetUnhandledExceptionFilter : PROC

.data message1 message2

db "An example of switching into Ring0 by means of the SEH (The Owl's method)",0 db "RING0 was successfully activated",0

.code

Start:

push offset xhandler

push dword ptr fs:[eax]

mov dword ptr fs:[eax], esp

pushfd

mov eax,esp

int 3h

xor eax,eax ;nulls the eax register because the new SEH

;service is set

;address for your new SEH service (xhandler) ;saves the address of the original SEH service ;sets the new SEH service ;saves the EFLAGS

;saves the ESP register (stack address) ;causes an error and will also call your new SEH ;service (xhandler)

;From here the program runs in RING0



mov ebx,dr7


;tests to see if you are really in RING0, ;restores the original register values for

push edx


; GS

push edx


; FS

push edx


; ES

push edx


; DS

push edx


; SS

push eax


; ESP

push dword ptr [eax]


; EFLAGS

push ecx


; CS

push offset ring3


; EIP = address where it will jump back

;into RING3

iretd


;jump back into RING3

;From here the program runs again in RING3

ring3:

popfd

pop dword ptr fs:[0] add esp,4

call MessageBoxA,0, offset offset message1,0 ;restores EFLAGS

;sets the original SEH service

;clears the stack

message2,\

call ExitProcess, -1

;ends the program



;Your new SEH service (xhandler)



xhandler:

push ebp

mov ebp,esp

push ebx

push ecx

mov ebx,[ebp+8]

cmp dword ptr [ebx], 80000003h jne end

mov eax,[ebp+10h]

movzx ecx, word ptr [eax+0bch] mov [eax+0ach],ecx

mov dword ptr [eax+0bch],28h



movzx ecx, word ptr [eax+0c8h] mov [eax+0a8h],ecx

mov dword ptr [eax+0c8h],30h

or dword ptr [eax+0c0h],0200h mov eax,0

;sets the stack

;and finds its address



;reads the address containing the information ;about the error, which is the exception ;location or address.

;tests to see if EXCEPTION_BREAKPOINT was the

;error caused by INT 3h ;if not the program jumps

;reads the address of the beginning of the ;context

;reads Seg.Cs from the Context

;saves .ECX into the Context (this value will ;be in the ECX after switching into RING0) ;saves Seg.Cs into the Context (this value will ;be in the CS register). This will secure switch ;over into RING0. ;reads Seg.SS from the Context

;saves .EDX into the Context (this value will be ;in the EDX after switching into the RING3) ;saves Seg.SS into the Context (this value will ;be in the SS register). This will secure a ;switch over into RING0.

;sets CLI into the Context.EFLAGS (this value ;will be in the EFLAGS register) ;nulls EAX

end:

pop ecx pop ebx mov esp,ebp pop ebp retn

;clears the stack





;jumps back into the program, but the example ;program will continue running in RING0

ends

end Start









Anti-Disassembling Macros



To make debugging and disassembling more difficult, you can use short codes that repeat often and that are inserted between the program instructions. It is really unpleasant to debug code that contains something like this, and some disassemblers don't like these short codes. For example, WinDasm will often end with an error.



IDA, on the other hand, is much better off, and if you can check the translation there, you can also disassemble most of the anti-debugging macros, and the program will be much easier to understand. Fortunately, there are several methods for making disassembly in IDA much more tiresome.



The Simplest Method



push offset jump ret



After the RET instruction, the program will jump to the jump label. When properly used, it may make the code hard to understand. For example:





jump:





ret

;...this can include any code, but it mustn't change the stack or, if it does, it must clear it at the end of the code. ;jumps to the jump2 label

jump2:

;...this can include any code, but it mustn't change the stack or, if it does, it must clear it at the end of the code.







This is a very simple method that will surprise hardly anyone. To make the code harder to read, use a more complicated way to calculate the address saved by the PUSH instructions, and save it as register values rather than as offsets.



A Similar Method



Here is a slightly more complicated example.



mov eax, offset jump push eax

;...this can include any code, but it mustn't change the stack or, if it does, it must clear it.

pop eax jmp eax

jump:

;...any code,

Making It Even Better



.data

address_ dd offset jump for_a_jump

.code

mov eax, offset address_for_a_jump push eax



pop eax jump [eax]

jump: ;...this can include any code, but it mustn't change the stack or, if it does, it must clear it.



;...any code.



Fantasy Is Unlimited



.data address_

for_a_jump dd offset jump

.code

mov eax, offset address_for_a_jump push eax

;...any code, but it mustn't

change the stack or, if it does, it must clear it.

pop eax call [eax]

jump:

;... any code.

Jumping into the Middle of Instructions and Making the Code Harder to Understand



You should put as little code as possible between these anti-disassembling macros to prevent the cracker from fully understanding them, because this makes their job more time consuming. Naturally, adding these macros makes the code itself longer, which may slow down the application. Therefore, think carefully about where you put the disassembling macros, and insert them only into those parts of the code where they are important.

Подпись: You can find more macros on the CD enclosed with this book. I didn't list them here because you will understand them best during debugging itself.

Note





Address

00000000



0000000B 0000000C 00000011 00000013

00000016

Instruction Code





60

E803000000

D2EB

0B58EB 014840

Instruction





pushad

call 00000014

shr bl,cl

or ebx, [eax-15h] add [eax+40h], ecx

Explanation

;... any code (in this case 8 bytes long)

;saves registers because of changes ;jumps to this address ;this code is wrong







;after the jump the code will change:

00000014 00000015

00000017



00000018



00000019 0000001B 58



EB01 48



40



EB01

35FFE0E761



pop eax



jmp 00000018 dec eax



inc eax



jmp 0000001C xor eax, 61E7E0FFh



;puts the return value into the eax ;(necessary for cleaning the stack)



;this instruction will jump over the ;dec eax instruction ;increases the return value by 1 so the ;return address will be 00000012



;this code is wrong



;after the jump the code will change:



0000001C FFE0 jmp eax ;jump to 00000012



after the jump the code will change:

00000012 EB0B jmp 0000001F ;another jump

after the jump the code will change:

0000001F 61 popad



. ;insert any code here

. ;repeat

Detecting Attempts to Decompress Programs Prior to Decoding



Most programs compiled by Visual C++, Delphi, and similar compilers have an entry point at the 401000 address. If the entry point is somewhere else, it's easy enough to find using a debugger or another program for finding important information about the EXE files (like ProcDump).



Once a compression or encoding program for executable files has been used, this entry point will be changed. Your application then only has to test whether the program's entry point is different from the one that was there before the compressor was used. If the original entry point is still there (usually 401000), a decompression attempt has been made. The best solution in that case is to end the program incorrectly, or have it perform an error that may confuse the attacker because he cannot be sure that he decompressed the application correctly.





Testing a File's Checksum with the API Function

MapFileAndCheckSumA



The API function MapFileAndCheckSumA offers a very simple way to test a file's checksum. It is part of the IMAGEHLP.DLL library that is part of the Windows installation.



MapFileAndCheckSumA calculates the current checksum of a file like this:



push offset new_checksum push offset old_checksum push offset file_name call MapFileAndCheckSumA







Once the routine has been performed, the new_checksum address will contain the current checksum of the file. The old_checksum address will contain the original checksum, which is located in the file's PE header. The file_name address is the name of the file for which the checksum was calculated.



You could detect changes in the file by comparing the values of the old_checksum and the new_checksum, but this method would be correct only if the attacker didn't restore the checksum in the PE header. It is much better to locate the correct checksum somewhere else (such as in the Windows Registry or in the file), and then to compare this value with the value in the new_checksum.





Changes in Characteristics for the .code Section of the PE File



If the characteristics of the .code section of the PE file have been changed to the value C0000040, the program will be fully functional and will run without problems. Nevertheless, if the program is disassembled with WinDasm, the resulting code will not be correct. Also, when such a program has been loaded into memory using the Symbol Loader for SoftICE, it will not stop at its beginning but will continue running. Most compression and encoding programs use this trick.



If a cracker wants to remove this trick, he has to change the PE header, and you can detect his attempts using a CRC calculation. You can also directly test the characteristics in the file's PE header for changes.





Finding Monitoring Programs



Crackers often use various monitoring programs to help them determine which files the application works with, which API functions it uses, where it saves values in registers, and where it reads them. It is very important to protect against the use of monitoring programs because they allow a cracker to easily find information that should remain hidden.



There are several ways to find out if such a program is active in memory. One way is to search for a program window by its name. Another method is to use the API call CreateFileA to determine whether a VxD (a Sys file in Windows NT and 2000) for a particular program is active. While not all of these programs use their own VxDs, many use VxDs to get into ring0 because they want to use its possibilities. (There are other methods, such as searching for a process; you just have to experiment a bit.)



You can use the following code to search for the window name of a monitoring program.

.data

WindowName db "Enter the name of the window here",0 .code

xor eax,eax ;nulls eax

call FindWindowExA, eax,eax, eax, offset WindowName

;searches for the window

test eax,eax

jnz jump_program ;jumps if it has found the window of the

;monitoring program







To search for an active VxD or Sys driver, you can use the same routine you would use to search for SoftICE drivers, as discussed in Chapter 7 in the "Detecting SoftICE by Opening Its Drivers and Calling the CreateFileA API Function (SICE, NTICE)" section and others. Naturally, you can use any other function (such as _lopen, _lcreat, or CreateFileW) instead of the API function CreateFileA, as shown in Chapter 7.



.data

name db "\\.\driver name",0 ;driver to be searched for

.code

call CreateFileA, OFFSET name,\

FILE_FLAG_WRITE_THROUGH, FILE_SHARE_READ,\

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,\
NULL

cmp eax, -1 ;tests to see if the driver is active

jnz found ;if so, the program will jump







The following table lists the most famous monitoring programs and ways of detecting them. Of course, this list doesn't contain all the programs crackers use. These are the most frequently used programs. It is therefore suggested you follow new monitoring applications and then react fast.

Monitoring Program ADUMP v1.0

ATM

C.u.Dilla rl final Debug Print Viewer







DllView



ExeSpy98 File Monitor

Window Name

db "Advanced Dumper v1.0",0



db "C.u.Dilla rl final",0 db "Debug Print viewer",0



db "\\.\DBGV",0 (Windows NT and 2000) db "DllView - System Internals: http://www.sysinternals.com",0

db "ExeSpy98",0

db "File Monitor - System Internals: http://www.sysinternals.com",0

Name



db "\\.\ATM",0



db "\\.\DBGDD",0 (Windows 9x)



db "\\.\FXVXD",0



OpenTrap OpenList Port Monitor





ProcDump



Registry Monitor

db "\\.\FILEVXD",0 (Windows 9x) db "\\.\FILEM",0 (Windows NT) db "\\.\FUNCTRAP",0

db "OpenList",0

db "Port Monitor - System Internals:

http://www.sysinternals.com",0

db "\\.\PORTMVXD",0 (Windows 9x)

db "\\.\PORTM",0 (Windows NT)

db "ProcDump32 (C) 1998, 1999, 2000

G-RoM, Lorian & Stone",0 or for the

Bhrama Server which is a part of the ProcDump

db "ProcDump32 - Dumper Server",0

db "Registry Monitor - System Internals:

http://www.sysinternals.com",0

db "\\.\REGVXD",0 (Windows 9x)

db "\\.\REGSYS",0 (Windows NT)

Registry Monitor 2000



Registry Monitor 98 ResSpy 98 Setup Monitor 98 Setup Monitor 2000



SMU Winspector Spy & Capture Microsoft Spy++ TCP View VxD Monitor Win-Expose-I/O



Win-Expose-Registry



db "Regmon98",0

db "Resspy",0

db "SetupMonitor98",0





db "SMU Winspector",0 db "Spy Window",0

db "Microsoft Spy++ -Window 1",0

db "TCPView",0 db "VxD Monitor",0

db "\\.\FHNTVDD",0

(Windows NT)

db "\\.\FHVXD",0 db "\\.\FHVXD",0

db "\\.\FHVXD",0

db "\\.\FHNTVDD",0

(Windows NT)





db "\\.\VXDMON",0

db

"\\.\WXIMONTR",0

db

"\\.\WXRMONTR",0

A Trick for Punishing a Cracker



Windows offers several possibilities for punishing crackers or users who want to illegally use your application. If your application discovers an illegal attempt to use it, or an attack against it, it can perform several unassuming changes without warning the user.



The user could, for example, be punished with several changes to the Windows Registry. For example, here's how to block RegEdit from accessing the Windows Registry:



[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\system] "DisableRegistryTools"=dword:00000001



Unblocking is possible only after replacing the 0s with 1s. It is a good idea to combine this trick with the following one, which causes a window with your message to appear after starting Windows.



[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Winlogon]

"LegalNoticeCaption"="Illegal usage attempt."

"LegalNoticeText"= "A user is attempting illegal software usage at this location."





With access blocked to the registry, the user will be unable to remove the message.



There are also other methods of punishment. For example, a program that has discovered an attempt at illegal use doesn't have to work correctly. It may, for example, "freeze" after some time, save files incorrectly, or delete itself. It's up to the application's creator to choose the punishment.



An application may also surprise a cracker by deleting the debugger from his hard drive, though not as soon as it finds it, of course. It should first ask nicely for its removal from the memory, but if following tests discover the debugger in memory again, it is clear that this is an attack.



Chapter 10: Important Structures in Windows



There are important structures in Windows, without which the operating system cannot function. Standard file types (such as BMB, EXE, and DOC) are such structures, but there are also special structures in memory that contain important information about processes, threads, and so on. These structures can be very useful and sometimes absolutely necessary.



Context Structure



Context structure is a very important Windows structure, and every running thread contains it. You can find critically important information about every program in the context structure; information that may help you discover a debugger or better manage the program.



Two API functions are used for managing the context structure. GetThreadContext reads the context structure of a thread. Calling GetThreadContext in C looks like this:



B00L GetThreadContext (

HANDLE hThread, //handle to the thread with the context

LPCONTEXT lpContext //address where the context structure will be saved

);





The other function, SetThreadContext, writes the new context structure for the thread. Naturally, you can also use it for your own purposes.

B00L SetThreadContext (

HANDLE hThread, CONST CONTEXT * lpContext

);

//handle to the thread with the context //address where the context structure is located //which will be set for the thread





The SEH service also allows you to read and change information in the context structure, and I have used it several times in anti-debugging tricks. SEH's advantage lies in the fact that you needn't call any API functions.



The context structure looks like this:



include winnt.h

#if !defined (RC_INVOKED)

#define CONTEXT_i386 0x00010000 //i386 and i486 structures

#define CONTEXT_I486 XX00010000 //contain the same context entry

#define CONTEXT_CONTROL (CONTEXT_i386|0x00000001L)

//if this flag has been set the context will contain values of the SS:SP, CS:IP, FLAGS, BP
//registers

#define CONTEXT_INTEGER (CONTEXT_i386|0x00000002L)

//if this flag has been set the context will contain values of the AX, BX, CX, DX, SI, DI registers

#define CONTEXT_SEGMENTS (CONTEXT_i386|0x00000004L)

//if this flag has been set the context will contain values of the DS, ES, FS, GS registers

#define CONTEXT_FLOATING_POINT (CONTEXT_i386|0x00000008L)

//if this flag has been set, the context will contain the 387 status

#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386|0x00000010L)

//if this flag has been set, the context will contain values of the DR 0-3, 6, 7 debug registers

#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER |\ CONTEXT_SEGMENTS)

//Be careful with these settings because they will not set the context flags for the

//CONTEXT_FLOATING_POINT and CONTEXT_DEBUG_REGISTERS #endif


//A structure for the 387 state

typedef FLOATING_SAVE_AREA * PFLOATING_SAVE_AREA;

typedef struct _CONTEXT {

DWORD ContextFlags;



//offset 00

//offset saves the flags here





//387 STATUS

//

flag for the CONTEXT_FLOATING_POINT has

//This section will be returned/set only when the context //been set.

// offset 1Ch

FLOATING_SAVE_AREA FloatSave; //SEGMENT REGISTRY

//

//This section will be returned/set only when the context flag for CONTEXT_SEGMENTS has been set.

DWORD DWORD DWORD DWORD

SegGs; SegFs; SegEs; SegDs;

//offset 8Ch

//offset 90h

//offset 94h

//offset 98h



//BASIC REGISTERS

//

Edi; Esi; Ebx; Edx; Ecx; Eax;

//This section will be returned/set only when the context

DWORD DWORD DWORD DWORD DWORD DWORD flag for CONTEXT_INTEGER has been set.

//offset 9Ch

//offset A0h

//offset A4h

//offset A8h

//offset ACh

//offset B0h



//CONTROL REGISTERS

//

//This section will be returned/set only when the context set.



flag for the CONTEXT_CONTROL has been



DWORD


Ebp;


//offset


B4h

DWORD


Eip;


//offset


B8h

DWORD


SegCs;


//offset


BCh MUST BE SANITIZED

DWORD


EFlags;


//offset


C0h MUST BE SANITIZED

DWORD


Esp;


//offset


C4h

DWORD


SegSs;


//offset


C8h

} CONTEXT;

typedef CONTEXT *PCONTEXT;



The context structure is ubiquitous, and it is vital when working with Windows NT and 2000, where access

to certain critical information is very difficult and sometimes nearly impossible. The situation is rather different in Windows 9x, where you can switch your program over to ring0 and get the highest authorization, yet even there the context structure has its place, and it can be used in many ways.



Windows NT Executable Files (PE Files)



Windows NT executables, also referred to as Portable Executable (PE) files because they are not architecture-specific, are probably one of the most important and most frequently used Windows file formats. The PE file is the native file format for Win32, and all 32-bit Windows executable files (such as EXE and VxD files) contain this structure.



It is definitely good to know the PE header structure. You can use it when creating tricks against various memory dumpers. When a development team writing protection decides to create their own encoding program for executable files, they will not be able to avoid it.



The PE file structure looks like this:



DOS MZ header

DOS stub

PE header

Section table

Section 1

Section 2

Section n

All PE files must start with a classic DOS MZ header to ensure that when the PE file is launched in DOS, the DOS stub (an MS-DOS executable, stored next to the MZ header) will be launched, too. This header is a short routine that typically displays the well-known message: "This program cannot be run in DOS mode," or it may display additional information, too, if necessary.



The PE header, which follows the DOS stub, is of great interest to us. Its structure is called by the IMAGE_NT_HEADER, and it contains many essential fields that are used by the PE loader. When a program is launched in an operating system that knows the program's structure, the PE file loader will read important information from the header and will prepare the file for launching. Thus, the PE loader can find the starting offset of the PE header from the DOS MZ header, skip the DOS stub, and go directly to the PE header (which is the real file header).



The PE header determines where a particular section begins and where it ends. Sections are blocks of data with common attributes that follow the PE header and that contain the PE file's real content.



The section table is an array of structures with information about each section in the PE file.



You can find the beginning of the PE header in the 3Ch offset of the DOS MZ header. It's important to know its location when setting up a jump over the DOS stub in order to correctly load the PE file.



This is a PE header's structure:

00 SIGNATURE BYTES

CPU TYPE

n

OBJECTS



08 TIME/DATE STAMP

16 RESERVED

24 RESERVED | LMAJOR | LMINOR

RESERVED

NT HDR SIZE

RESERVED



FLAGS

32


RESERVED


RESERVED

40


ENTRYPOINT RVA


RESERVED

48


RESERVED


IMAGE BASE

56


OBJECT ALIGN


FILE ALIGN

64


OS MAJOR | OS MINOR | USER MAJOR | USER MINOR

72


SUBSYS MAJOR | SUBSYS MINOR


RESERVED

80


IMAGE SIZE


HEADER SIZE

88


FILE CHECKSUM


SUBSYSTEM | DLL FLAGS

96


STACK RESERVE SIZE


STACK COMMIT SIZE

104


HEAP RESERVE SIZE


HEAP COMMIT SIZE

112


RESERVED


#INTERESTING RVA/SIZES

120


EXPORT TABLE RVA


TOTAL EXPORT DATA SIZE

128


IMPORT TABLE RVA


TOTAL IMPORT DATA SIZE

136


RESOURCE TABLE RVA


TOTAL RESOURCE DATA SIZE

144


EXCEPTION TABLE RVA


TOTAL EXCEPTION DATA SIZE

152


SECURITY TABLE RVA


TOTAL SECURITY DATA SIZE

160


FIXUP TABLE RVA


TOTAL FIXUP DATA SIZE

168


DEBUG TABLE RVA


TOTAL DEBUG DIRECTORIES

176


IMAGE DESCRIPTION RVA


TOTAL DESCRIPTION SIZE

184


MACHINE SPECIFIC RVA


MACHINE SPECIFIC SIZE

192


THREAD LOCAL STORAGE RVA


TOTAL TLS SIZE

Offset 00 - SIGNATURE BYTES - size DWORD

A mark that shows whether the file is an executable. It changes with the type of the executable file into the following bytes:



"P", "E", 0, 0

"M", "Z", 0, 0

"N", "E", 0, 0

"L", "E", 0, 0

Offset 04 - CPU TYPE - size WORD

Specifies the minimum processor necessary to launch the file.



Value Explanation



0000h Unknown 014Ch 80386 014Dh 80486 014Eh 80586

0162h MIPS MARK I (R2000, R3000)

0163h MIPS MARK II (R6000) 0166h MIPS MARK III (R4000)

Offset 06 - n OBJECTS - size WORD

Specifies the number of entries (objects) in the object table.

Offset 08 - TIME/DATE STAMP - size DWORD

Stores the date and time when the file was created or modified by the linker.

Offset 12 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 16 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 20 - NT HDR SIZE - size WORD

Determines the size of the PE header in bytes.

Offset 22 - FLAGS - size WORD

Flag bits for the image. The possible values are as follows:



Value Explanation





0000h Program image.

0002h Image is executable. If this byte isn't set, errors were detected at link time or the image

0002h cannot be loaded.

0200h Indicates that if the image can't be loaded at the image base, then do not load it.

2000h Set if this is a library image (DLL).

Offset 24 - RESERVED - size WORD

A reserved area of the PE header.

Offset 26 - LMAJOR - size BYTE

Specifies where the main part of the linker version is located.

Offset 27 - LMINOR - size BYTE

Specifies where the second part of the linker version is located.

Offset 28 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 32 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 36 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 40 - ENTRYPOINT RVA - size DWORD

This address is relative to the image base. It is a starting address for program images and for libraries (DLLs). It is an initializing and ending address for library images.

Offset 44 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 48 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 52 - IMAGE BASE - size DWORD

The virtual base of the image. This will be the virtual address of the first byte of the file (DOS MZ header). It must be divisible by 64 and it is usually 400000h for EXE files.

Offset 56 - OBJECT ALIGN - size DWORD

Determines the alignment of the object and is the same for all objects. In practice, this means that if this value is, for example, 1000h, then each section will have to start with a multiple of 1000h bytes. If the first section (object) is at 401000h and its size is 10h bytes, then the following section must start at 402000h, even if the remaining space will not be used.

Offset 60 - FILE ALIGN - size DWORD

Determines the alignment of pages in the file and is the same for all pages. In practice, this means that if this value is, for example, 200h, then each page will have to start with a multiple of 200h bytes. If the first page is at the 200h file offset, and its size is 10h bytes, then the following section must start at 400h, even if the remaining space will not be used.

Offset 64h - OS MAJOR and OS MINOR - size DWORD

The type of operating system necessary for launching the file.

Offset 68 - USER MAJOR and USER MINOR - size DWORD

You can find the user's number here.

Offset 72 - SUBSYS MAJOR and SUBSYS MINOR - size DWORD

The number of the subsystem. If the PE file was created for Win32, there should be version 4.0 because otherwise the dialog box will not be displayed in 3D.

Offset 76 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 80 - IMAGE SIZE - size DWORD

The virtual size (in bytes) of the image is set here. This offset is a part of all headers and sections. For the value to be correct, it must be divisible by the object align setting.

Offset 84 - HEADER SIZE - size DWORD

The total header size, which is the sum of the DOS MZ header, PE header, and object table sizes.

Offset 88 - FILE CHECKSUM - size DWORD

Checksum of the entire file.

Offset 92 - SUBSYSTEM - size WORD

• The value that determines which NT subsystem is necessary to run the file. Value Explanation



0000h Unknown 0001h Native 0002h Windows GUI

0003h Windows character 0005h OS/2 character 0007h POSIX character

Offset 94 - DLL FLAGS - size WORD

Indicates special loader requirements. This flag has the following bit values. All other bits are reserved and should be set to zero.



Value Explanation



0001h Per-process library initialization

0002h Per-process library termination

0004h Per-thread library initialization

0008h Per-thread library termination

Offset 96 - STACK RESERVE SIZE - size DWORD

The stack size needed for the file. The memory is reserved, but only the STACK COMMIT SIZE has been used. The following stack page is the guarded page. If an application uses the guarded page, the guarded page becomes a normal page, and the next page becomes a guarded page. This continues until the reserve size is reached.

Offset 100 - STACK COMMIT SIZE - size DWORD

Determines how much reserved memory will be used for the stack.

Offset 104 - HEAP RESERVE SIZE - size DWORD

Size of the local heap reserved.

Offset 108 - HEAP COMMIT SIZE - size DWORD

The amount of the local heap that is actually used.

Offset 112 - RESERVED - size DWORD

A reserved area of the PE header.

Offset 116 - # INTERESTING RVA/SIZES - size DWORD

Size of the RVA/SIZE file that follows.

Offset 120 - EXPORT TABLE RVA - size DWORD

Relative virtual address (RVA) of the export table. This address is relative to the image base.

Offset 124 - TOTAL EXPORT DATA SIZE - size DWORD

Size of the export table.

Offset 128 - IMPORT TABLE RVA - size DWORD

RVA of the import table. This address is relative to the image base.

Offset 132 - TOTAL IMPORT DATA SIZE - size DWORD

Size of the import table.

Offset 136 - RESOURCE TABLE RVA - size DWORD

Relative virtual address of the resource table. This address is relative to the image base.

Offset 140 - TOTAL RESOURCE DATA SIZE - size DWORD

Size of the resource table.

Offset 144 - EXCEPTION TABLE RVA - size DWORD

Relative virtual address of the exception table. This address is relative to the image base.

Offset 148 - TOTAL EXCEPTION DATA SIZE - size DWORD

Size of the exception table.

Offset 152 - SECURITY TABLE RVA - size DWORD

Relative virtual address of the security table. This address is relative to the image base.

Offset 156 - TOTAL SECURITY DATA SIZE - size DWORD

Size of the security table.

Offset 160 - FIXUP TABLE RVA - size DWORD

Relative virtual address of the fix-up table. This address is relative to the image base.

Offset 164 - TOTAL FIXUP DATA SIZE - size DWORD

Size of the fix-up table.

Offset 168 - DEBUG TABLE RVA - size DWORD

Relative virtual address of the debug table. This address is relative to the image base.

Offset 172 - TOTAL DEBUG DIRECTORIES - size DWORD

Size of the debug table.

Offset 176 - IMAGE DESCRIPTION RVA - size DWORD

Relative virtual address of the description string specified in the module definition file.

Offset 180 - TOTAL DESCRIPTION SIZE - size DWORD

Size of the reference data.

Offset 184 - MACHINE SPECIFIC RVA - size DWORD

Relative virtual address of a machine-specific value. This address is relative to the image base.

Offset 188 - MACHINE SPECIFIC SIZE - size DWORD

Size of the machine-specific value.

Offset 192 - THREAD LOCAL STORAGE RVA - size DWORD

Relative virtual address of the thread local storage. This address is relative to the image base.

Offset 196 - TOTAL THREAD LOCAL STORAGE - size DWORD

Size of the thread local storage.



Object Table



The object table follows the PE header. The number of fields that it contains is determined by the NumberOfSections field in the PE header. The structure of the object table is called the

IMAGE_SECTION_HEADER.



• Offset 0 - IMAGE SIZE OF SHORT NAME - size 8 bytes



The name of the section in ASCII. Because the name doesn't end in 0, all 8 bytes will be used. Typical names are .data or .code. The name of the section doesn't mean that it must contain only code though; it may easily contain both code and data.

Offset 8 - PHYSICAL ADDRESS - size DWORD



The file address. This item isn't much used. There are linkers that set an address here, but it is usually 0.

Offset 12 - VIRTUAL SIZE - size DWORD



The size of the contents when loaded into memory (in bytes). This item isn't much used. There are linkers that set an address here, but it is usually 0.

Offset 16 - VIRTUAL ADDRESS - size DWORD



Address of the first byte of the data section when loaded into memory, relative to the image base.

Offset 20 - SIZE OF RAW DATA - size DWORD



Size of the initialized data on disk, rounded to the next multiple of the FileAlignment.

Offset 24 - POINTER TO RAW DATA - size DWORD



An offset to the beginning of the section from the start of the file. If it is 0, then the section doesn't contain any data.

Offset 28 - POINTER TO RELOCATIONS - size DWORD



Only object files use this item, or it occurs only under special circumstances.

Offset 32 - POINTER TO LINENUMBERS - size DWORD



Only object files use this item, or it occurs only under special circumstances.

Offset 36 - NUMBER OF RELOCATIONS - size DWORD



Only object files use this item, or it occurs only under special circumstances.

Offset 38 - NUMBER OF LINENUMBERS - size DWORD



Only object files use this item, or it occurs only under special circumstances.

Offset 40 - CHARACTERISTICS - size DWORD



Flags that determine whether the section is data or code and whether it can be written to or read from.



Bit 5 (IMAGE_SCN_CNT_CODE) Set if the section contains executable code.

Bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA) Set if the section contains data that will become real values (initialized) before the file has been launched.

Bit 7 (IMAGE_SCN_CNT_UNINITIALIZED_DATA) Set if the section contains uninitialized data, and all of the data will be initialized as 0-byte values before the file is launched. This is usually the BSS section.

Bit 9 (IMAGE_SCN_LNK_INFO) Set if the section doesn't contain image data but rather commentary, description, or other documentation. This information is part of the object file and it may, for example, contain information for the linker telling it which libraries will be necessary.

Bit 11 (IMAGE_SCN_LNK_REMOVE) Set if the section will be removed from the object file after linking. It is often used with bit 9.

Bit 12 (IMAGE_SCN_LNK_COMDAT) Set if the section contains the common block data (COMDAT).

Bit 15 (IMAGE_SCN_MEM_FARDATA) Set if there is distant data. (I haven't determined the exact meaning of this bit.)

Bit 17 (IMAGE_SCN_MEM_PURGEABLE) I don't know the meaning of this bit. Bit 18 (IMAGE_SCN_MEM_LOCKED) I don't know the meaning of this bit.

Bit 19 (IMAGE_SCN_MEM_PRELOAD) I don't know the meaning of this bit. Bits I don't know the meanings of these bits.

20-23

Bit 24 (IMAGE_SCN_LNK_NRELOC_OVFL) Set if the section contains extended relocations.

Bit 25 (IMAGE_SCN_MEM_DISCARDABLE) Set if the data sections will not be necessary after the file is launched (such as the starting routine of a driver that will be launched only once).

Bit 26 (IMAGE_SCN_MEM_NOT_CACHED) Set when the data sections will not be preserved.

Bit 27 (IMAGE_SCN_MEM_NOT_PAGED) Set when the data sections will not be paged. This is relevant to drivers.

Bit 28 (IMAGE_SCN_MEM_SHARED) Set when the data sections can be shared. Bit 29 (IMAGE_SCN_MEM_EXECUTE) Set if the section can be executed as code. Bit 30 (IMAGE_SCN_MEM_READ) Set if the section can be read. Bit 31 (IMAGE_SCN_MEM_WRITE) Set if the section can be written to.

Section Types



The various section types include code, data, BBS, and exported and imported symbols. The characteristics of each section are described below.



Code Section



This section usually contains only the program code, and each file contains only one code section, though that doesn't have to be the rule. Text, code, and AUTO are some of the typical names for this section.



Characteristic bits:



'IMAGE_SCN_CNT_CODE', 'IMAGE_SCN_MEM_EXECUTE', and 'IMAGE_SCN_MEM_READ'



The AddressOfEntryPoint points to an offset in this section where the first function to be launched is located.

Data Section

This section contains initialized static values (for example, int x 3D 10;) Characteristic bits:

'IMAGE_SCN_CNT_INITIALIZED_DATA', 'IMAGE_SCN_MEM_READ', and 'IMAGE_SCN_MEM_WRITE'

Some linkers for this section do not set the bit for writing. Data, idata, and DATA are typical names for this section, among others.



BSS Section



This section contains the uninitialized data (int x;), and it is very similar to the data section. The 'IMAGE_SCN_CNT_UNINITIALIZED_DATA' bit set is found here instead of 'IMAGE_SCN_CNT_INITIALIZED_DATA'. Such a file will contain only the section header but not the section itself. The section, itself, will be created only by the loader, and it will be filled in with zero bits. Bss and BSS are typical names for this section, among others.



Exported Symbols



This section contains the exported functions, mostly found in DLL files. (Normal executable files usually don't contain exported symbols.) '.edata'



is the most frequent name for this section.



The structure of the export directory table is as follows:

Offset 0 - CHARACTERISTICS - size DWORD

This item isn't much used; it is set to zero.

Offset 4 - TIME DATE STAMP - size DWORD

This item contains the time when the data was created, and it is in the time_t-format. However, it isn't frequently set, and many compilers set it to 0.

Offset 8 - MAJOR VERSION - size WORD

This is the user-settable major version number, and it is typically set to zero. If it is not zero, it contains information about the version.

Offset 10 - MINOR VERSION - size WORD

This is the user-settable minor version number, and it is typically set to zero. If it is not zero, it contains information about the version.

Offset 12 - NAME RVA - size DWORD

The RVA of the DLL ASCII name. This is an ASCII string ending in 0.

Offset 16 - BASE or ORDINAL BASE - size DWORD

The first valid exported ordinal. This field specifies the starting ordinal number for the export address table for this image, and is normally set to 1. (The export address table contains the address of exported entry points and exported data and absolutes.)

Offset 20 - NUMBER OF FUNCTIONS - size DWORD

The number of the exported items.

Offset 24 - NUMBER OF NAMES or NUMBER OF ORDINALS - size DWORD

The number of the exported names. While each exported item usually has only one name, one item may have more names or even none, in which case it is only possible to call the exported function by means of the "ordinal number."

Offset 28 - ADDRESS OF FUNCTIONS - size DWORD

The relative virtual address to the export address table, relative to the image base. Here are the 32-bit values that are the RVA to the exported function. If the RVA in the list is 0, the exported function hasn't been used. The RVA may point into the section containing the export directory, and this is called a forwarded export. A forwarded export is a pointer to an export in another binary file.

Offset 32 - ADDRESS OF NAMES - size DWORD

The relative virtual address of the export name table pointers, relative to the beginning of the image base.

Offset 36 - ADDRESS OF NAME ORDINALS or ADDRESS OF ORDINALS - size DWORD

Relative virtual address of the export ordinals table entry, relative to the image base.



Imported Symbols



This section contains all the imported functions. If the program calls an API function, the information for the call will be located in this section. When the file is being loaded into memory, the loader reads the import information and modifies the file in memory. "idata" is the most common name for this section.



This section starts with a field containing the IMAGE_IMPORT_DESCRIPTOR. It contains an array of import direct entries - one for each DLL library that the file references. The last directory entry is empty (null), and you can find the end of the directory table by finding the IMAGE_IMPORT_DESCRIPTOR that contains zero bits.

IMAGE_IMPORT_DESCRIPTOR Structure



The IMAGE_IMPORT_DESCRIPTOR structure is special because it contains two parallel fields with pointers to the IMAGE_THUNK_DATA. The first field, called by the ORIGINAL FIRST THUNK, is always the same. The second field, called by the IMAGE_IMPORT_DESCRIPTOR, is rewritten every time the PE loader launches it. The loader searches for the address of each function and rewrites the IMAGE_THUNK_DATA with the address of each imported function.



The IMAGE_IMPORT_DESCRIPTOR structure is as follows:

Offset 0 - ORIGINAL FIRST THUNK or characteristics - size DWORD

This item contains the RVA to the IMAGE_THUNK_DATA.

Offset 4 - TIME DATE STAMP - size DWORD

Time when the file was created; usually zero.

Offset 8 - FORWARDER CHAIN - size DWORD

This item contains a pointer to the first redirected function. It is sometimes used when other functions have been redirected into another DLL.

Offset 12 - NAME - size DWORD

This item contains the RVA to an ASCII string ending in 0. It is the name of the imported DLL library.

Offset 16 - FIRST THUNK - size DWORD

This item is an RVA to a field ending in a zero bit containing the RVA to the IMAGE_THUNK_DATA. Each RVA is for one imported function, and it is often called the pointer to the

IMAGE_IMPORT_BY_NAME structure.



Note The import may only be an ordinal number.

IMAGE_THUNK_DATA



Each structure of the IMAGE_THUNK_DATA corresponds to one imported function. If a function was imported by the ordinal value, the upper bit in the IMAGE_THUNK_DATA will be set. For example, 0x80000010 from the field for the KERNEL32.DLL means that every tenth function of this DLL will be imported. This method isn't recommended, though, because if there is a change in the DLL version, the tenth function may be different than it was originally.



If the function was imported by name, the IMAGE_THUNK_DATA will contain an RVA for the

IMAGE_IMPORT_BY_NAME structure.

IMAGE_IMPORT_BY_NAME Structure

The structure of IMAGE_IMPORT_BY_NAME is as follows:

Offset 0 - HINT - size WORD

This is the best estimate of what the export ordinal for the exported function is like. This value doesn't have to be correct; the loader uses it as a recommended value for searching for the exported function. It is often zero.

Offset 2 - BYTE - size?



This is an ASCII string with the name of the imported function. When loading the file, the PE loader uses the initialization information from the IMAGE_THUNK_DATA for finding the imported function's address. The loader will rewrite the IMAGE_THUNK_DATA with the address of the imported function.



Resources



The PE file's resources (menu, dialog boxes, and so on) are located in this section. Resources are located in the main directory, which contains subdirectories. These subdirectories may contain further subdirectories.



The main directory and the subdirectories have a common structure, IMAGE_RESOURCE_DIRECTORY, which has the following format:

Offset 0 - CHARACTERISTICS - size DWORD

Theoretically, the flag for resources should be here, but actually it isn't used. Therefore, this is only zero.

Offset 4 - TIME DATE STAMP - size DWORD

The time when the resource was created.

Offset 8 - MAJOR VERSION - size WORD

The resource version should be located here, but usually it is zero.

Offset 10 - MINOR VERSION - size WORD

The resource version should be located here, but usually it is zero.

Offset 12 - NUMBER OF NAMED ENTRIES - size WORD

Number of field items using names. (To understand this, you must study the structure of the directory entries field.)

Offset 14 - NUMBER OF ID ENTRIES - size WORD

Number of field items using an identification number. (To understand this, you must study the structure of the directory entries field.)

Offset 16 - IMAGE RESOURCE DIRECTORY ENTRY - size NUMBER_OF_NAMED_ENTRIES + NUMBER_OF_ID_ENTRIES



While not actually an item of the IMAGE_RESOURCE_DIRECTORY structure, this immediately follows it. The sum of the NUMBER_OF_NAMED_ENTRIES and the NUMBER_OF_ID_ENTRIES is the size of the field. Items in the field may point to other subdirectories or to the IMAGE_RESOURCE_DATA_ ENTRY, which describes where the resources are located in the file.



You will usually need to go down at least three levels to get to the IMAGE_RESOURCE_DATA_ENTRY. The first directory is located at the highest level, and you can find it at the beginning of the resource section. Other subdirectories are divided in accordance with the resource type (dialog box, menu, and so on). Each of these type-divided subdirectories has one identification subdirectory, which is always marked with a text string (the name of the menu) or by an identification number.



IMAGE_RESOURCE_DIRECTORY_ENTRY Structure



The structure of the IMAGE_RESOURCE_DIRECTORY_ENTRY is as follows:

Offset 0 - NAME - size DWORD

If the upper bit (0x80000000) is set to zero, this field will contain the ID number item for the resource identification. If the upper bit is set to 1, the field will contain the offset item (relative to the beginning of the resource section) to the IMAGE_RESOURCE_DIR_STRING_U structure. This structure contains a WORD value representing the length, and, right behind it, a Unicode string with the name of the file.

Offset 4 - OFFSET TO DATA - size DWORD

If the upper bit is set (0x80000000), this item will contain an offset to the following IMAGE_RESOURCE_DIRECTORY. If the upper bit isn't set, this field will contain an offset to the IMAGE_RESOURCE_DATA_ENTRY structure.

This structure determines where resource data is located in the file, its size, and code page.

IMAGE_RESOURCE_DATA_ENTRY Structure

The structure of the IMAGE_RESOURCE_DATA_ENTRY is as follows.

Offset 0 - OFFSET TO DATA - size DWORD

RVA address of the resource data.

Offset 4 - SIZE - size DWORD

Size of the data.

Offset 8 - CODEPAGE - size DWORD

The code page.

Offset 12 - RESERVED - size DWORD

A reserved area.



Chapter 11: Suggestions for Better Software Protection



Overview



Protecting software against illegal copying has both advantages and disadvantages.



It frequently happens that developers underestimate the testing process, and the protection that they add causes problems for users. For example, tricks that protect against CD copying don't always work correctly on all CD-ROM drives. I have seen programs where it wasn't possible to enter the correct registration number.

Therefore, it is important to test as thoroughly as possible. Testing should also be performed with all operating systems for which the application is designed. If something works correctly with Windows 9x, it might not work correctly with Windows NT, 2000, or XP. Anti-debugging tricks also should not be used too much, and only those that work extremely well should be used at all. However, this doesn't mean that you should be afraid to try new methods.

Remember that crackers are often better informed than you are. The best of them are also excellent programmers, and they cannot be surprised easily. It is, for example, more and more common to use RSA encrypting methods for registration numbers. Only a few people understand that even if you use a 128-bit encrypting key, or even the 512-bit key, it is still possible to break the protection and create a registration number generator. It is impossible to break the RSA protection with a 1024-bit key. There are many examples like this.

The best way to protect your software is to keep your eyes open for the latest developments. The situation changes incredibly quickly. If someone breaks through your protection, don't surrender, but fight even harder, because there is always a way to make the protection better.

You can find so-called "crackme" programs on the Internet, mostly created by crackers. You can find new ideas and methods there, some of which are excellently programmed and hard to overcome. Study them and you may discover something new.

Rules for Writing Good Software Protection

It is difficult but not impossible to program good software protection. But remember: If you use typical programming methods when writing a new protection program, you make the cracker's job easier. Most crackers use several well-tested methods to crack particular types of protection and, as such, are able to remove 80 to 90 percent of protection programs. The key is to use less-common methods, which will slow the cracker down or even stop him.



Therefore, I present these rules for writing good software protection.

Never give a testing routine a self-describing name. For example, if you use the function name TestRegistration in a DLL library, the cracker will immediately know where in the code to focus his

effort.

Avoid unnecessary error messages. If the program gives an error message after an incorrect registration number has been entered, the cracker can simply search the program code for the error message to track down the procedure that called it. When error messages are unavoidable, hide them as much as possible, and create them (dynamically) in real time rather than use resources for them. It's also a good idea to encode the data and the procedure that creates an error message, which makes it much more difficult for the cracker to find in the disassembled code.

Use very good encoding for the registration files and other protections. If this doesn't stop the cracker, it will at least slow him down considerably. I recommend RSA 1024, which is unbreakable.

Use random tests for the registration. There is a good chance that this protection will succeed, especially if you use a testing method that differs from the original one. These random tests should start several days or hours after registration. Crackers really hate them because it usually takes a lot of time to remove them.

Don't start the registration number test for several seconds or even minutes after the registration. This will make it much more difficult for the cracker to trace the application in a debugger. Better still, require the program to restart before testing begins.

Use checksums in your application. If you test your EXE and DLL files, and even other files, for changes, crackers will be unable to modify them with patches. However, they will still be able to modify the code directly in memory, so it is a good idea to test for changes to the application code in memory, as well. It is also a good idea to perform checksums for smaller sections of your program, because a cracker may change the checksum to the correct value after he changes the program. When you perform checksums on smaller sections of the program, you make the cracker's job very

difficult.

Use more checking routines to make it harder to remove the protection. Any one routine should test only a part of the correctness of the registration, not the whole of it, to prevent the registration being understood when a cracker discovers it. By using more than one checking routine, the cracking of the program will often be incorrect because the cracker may miss one or more of the checking routines.

Have the program change while running. By having the program change as it runs, you make its code more difficult to understand. You can also use this technique to create false testing routines that will confuse the cracker while they are disassembling your program.

Put your registration information in a non-obvious place. If you keep the registration information in the Windows registry, it can be easily discovered. Instead, keep it in a file that nobody would suspect would contain the registration, like a DLL. By placing such a DLL into the Windows system directory, you will make the registration situation very confusing for the cracker. Even if a cracker uses a monitor to follow access to files, it may take a long time before he understands that the registration is in such a file. Better still, locate the registration in more than one place to make it likely that the protection will be removed incorrectly.



Encrypt the registration. Encrypt your registration using a technique that is dependent on the particular computer's hardware to make it impossible to use the file to register the program on another computer. (It's a good idea to use the serial number of the hard drive.)

Don't be afraid of long registrations. The longer the registration file or number, the longer it takes to understand it. If the registration file or number is sufficiently long, it may contain important information that the program needs, and if such a program is patched, it will not run correctly.

Test several current bits of data when using time-limit protection, such as file date, system.dat, and bootlog.txt. If the current date or time is the same or smaller than when the program was run previously, it will be clear that the time was adjusted. Also, you can save the date and time after each launch of the program, and then test the current date and time during a new launch.

Testing routines may be unbelievably long. Something that takes only a few seconds when a program is running may take an extremely long time to run while disassembling or debugging. Nevertheless, make sure that it isn't possible for the cracker to tell whether this is important or useless code, or they will know whether they can jump right over it. Your testing routine should run anywhere from 10 to 30 seconds in length.

If you limit your program's functionality, make sure that the program doesn't contain the code for the limited function. Many developers make the mistake of including in the unregistered program the code for a function that is only to be available after registration (the ability to save files, for example). In such a case, the cracker can modify the code so that the function will work.



A better approach is to include parts of the code (a sufficiently long bit) together with the registration, such as an EXE file. Under the best protection strategies, the user receives the code upon registration, and the program will use this code after it checks the registration. With such a protection scheme, it's virtually impossible for the cracker to remove the protection without the correct registration.

Also, consider encrypting sections of the code that are not supposed to function in an unregistered version, to be decoded only after registration. The decoding key must then contain the registration, so that the program cannot be decoded without it.

If your program has been cracked, release a new version. Frequent updates to your program may put off crackers, especially when the protection was removed by changing the code, because the patch will not work in the new version.

Use the best, current compression or encoding programs to encode your software. Keep an ear to the ground to find out which is currently the best compression or encoding program, and which have decoders. A good compressor will be difficult for a cracker to remove.

If your application uses a registration number, that number should never be visible in memory. This means that it should be impossible to find your program's registration number when the program is being debugged.



When programming a method that checks to see whether the correct registration number was entered, do something other than just comparing two strings. The best way is to encode the entered registration number and the correct registration in the same way. In this way, the two numbers can be compared without risk of the cracker discovering the code.



You might also compare a checksum of the entered registration number with the checksum of the correct registration number, though if you do so, you will have to use more checking methods to really make sure that the correct registration number was entered, rather than modified in accordance with the checksum that the cracker had seen in his debugger.

Consider requiring online registration. The Internet has greatly increased the feasibility of online registration. When a program is registered online, its registration data is sent to a particular server. In its most basic form, this server then sends back information to the program telling it whether the registration was successful or not. However, the server can also be used to send data that the program needs in order to launch the registered application. This data may range from important parts of the code to a key needed to decode parts of the program.

Remember the anti-debugging tricks and various anti-disassembling methods that have been described in this book. Be sure that the innovations you learn about and choose to implement are functional. If you cannot debug or disassemble something, you cannot remove it either.

Test your software's protection. Test your software's protection for all operating systems under which it will run. Often, protection that works with Windows 9x doesn't work correctly with Windows NT, 2000, or XP.



Keep Current

Finally, visit these web sites to stay abreast of the latest developments.

http://w3.to/protools/ A page focusing on new tools for programmers. You can find

compression, encoding, monitoring, and decoding programs here.

http://win32asm.cjb.net A great page devoted to the assembler programming language. It is

especially good for beginners, but even more experienced people can learn something new here.

http://www.egroups.com/list/exelist A discussion list about compression and encoding programs.

http://elicz.cjb.net A page about system programming with excellent source code.

http://www.anticracking.sk My page, focused on software protection against illegal copying.





Glossary of Terms



A-C



Algorithm

A detailed sequence of actions to perform in order to accomplish some task. API (application programming interface)

The interface by which an application accesses operating system and other services. An API is

defined at source code level and provides a level of abstraction between the application and the

kernel to ensure the portability of code. API calls

Functions included in the Microsoft Windows DLL libraries. They should make the programmers' job easier.

API hook

Replacement of a library function with another code, without a program being aware of it. Application code

The program code for an application.

Assembler

A program that converts assembly language into machine code. Assembly

A programming language. Breakpoint

A point in a program that, when reached, causes some special behavior. It is usually associated with debugging. During an interactive debugging session, when the breakpoint is reached, program execution halts, at which point the cracker can examine the contents of both the memory (and variables). Brute force attack

An attempt to decrypt a specific encrypted text by trying every possible key. Checksum

A computed value that depends on the contents of a block of data. A checksum is transmitted or stored along with the data in order to detect corruption of the data. Compiler

A program that converts another program from a programming language to machine language (object code). Some compilers output assembly language, which is then converted to machine language by a separate assembler. Each programming language must have its specific compiler. Most compilers for higher programming languages can also work with assembly. A compiler differs from an assembler in that each input statement does not, in general, correspond to a single machine instruction. Compression

A mathematic method for "decreasing" the size of the compressed data and the resulting decrease

in their volume. Compressor

An application compressing data or files. Coprocessor

Any computer processor that assists the main processor by performing certain special functions, usually much faster than the main processor could perform them in software. The coprocessor often decodes instructions in parallel with the main processor and executes only those instructions intended for it.

Crack

A program that removes software protection from an application after it has been launched. When a person removes software protection, they are said to be cracking. Cracker

A person who removes protection from software. CRC (cyclic redundancy code)

A number derived from, and stored or transmitted with, a block of data in order to detect corruption. By recalculating the CRC and comparing it to the value originally transmitted, the receiver can detect certain errors or the validity of a file.



D-M



Debug

To tune an application and search for errors in the program code. For crackers, it means to search the application code for protection schemes. Debugger

A tool that can step through execution of a program and examine variables and memory. Debuggers can also set breakpoints at which program execution will stop when the program is running in the debugger. Crackers often use a debugger when cracking. Decoder

An application decoding data or a file into the original form that existed before encoding. Decompiler

An application that can translate an executable file back into the programming language in which it was created. You can use a decompiler only for an application that was created in a programming language supported by the particular decompiler.

Delphi

A programming language from Borland. Disassembler

An application that can translate an executable file back into the original programming language, most frequently assembler. This term is sometimes used to describe decompilers. DLL (dynamically linked library)

A library that is linked to applications when they are loaded or run.

Encode

To convert data into a given format.

Encoder

Any program or algorithm that can encode data or a file by means of a selected encryption. EPROM (erasable programmable read-only memory)

Programmable read-only memory (programmable ROM) that can be erased and reused. EPROM

keeps data even after disconnecting from the power source. EXE file

An executable binary file. EXE protector

An application that tries to protect EXE files and other executables from debugging, decompiling, disassembling, and cracking.

Freeware

An application that may be used without paying for it.

Key

A value used to identify a record in a database.

Linker

A program that combines one or more files prepared by a compiler into a single file containing loadable or executable code (such as EXE, COM, or DLL files). Each operating system has its own type of linker because their executable files are different. Machine code

The representation of a computer program that is actually read and interpreted by the computer.



N-Z



Object code

The machine code generated by a source code language processor, such as an assembler or compiler.

Patch

To change the program code in memory or directly in the file. P-Code

A type of software compilation in Visual Basic.

Pirate

A person who illegally spreads software. Pirate group

A group of people who illegally spread software. Processor

A functional unit in a computer that performs the program's execution. Program release

To release an application into circulation, or the released program itself. Register

One of a small number of high-speed memory locations in the processor. Source code

The form in which a computer program is written by the programmer. Also source.

Trace

Tracing application code in a debugger. VxD (virtual device driver)

A device driver under Windows 3.x/Windows 95 running as part of the kernel, and thus having access to the memory of the kernel and all running processes, as well as raw access to the hardware. VxD's usually have the filename extension .386 under Windows 3.x and .vxd under Windows 95.

XOR

Encoding of program code using the XOR instruction.

List of Figures



Chapter 2: Cracking Tools



Figure 2.1: It is really easy to disassemble a program in WinDasm Figure 2.2: IDA looks like a DOS program, but it is a fully 32-bit application Figure 2.3: SoftICE contains wonderful and detailed documentation Figure 2.4: Running SoftICE during a program tune-up Figure 2.5: SoftICE Symbol Loader



Chapter 3: The Basic Types of Software Protection



Figure 3.1: Registration number is always the same

Figure 3.2: Registration number changes in accordance with the entered name

Figure 3.3: When an incorrect registration number is entered, the registration is unsuccessful

Figure 3.4: Every computer requires a different registration number

Figure 3.5: At first sight, a registration requirement programmed in Visual Basic isn't different from other languages

Figure 3.6: A program is ready to send registration information to the control server via the Internet Figure 3.7: The program displays an error message if an error occurs while connecting to the server Figure 3.8: This program may be used for 30 days. Once this period expires, the correct registration number must be entered.

Figure 3.9: The time limit has expired. Now the user must register to receive a registration file. Figure 3.10: This is a registration key file for Kaspersky's antivirus program, AVP Figure 3.11: This program cannot be registered directly; the user must buy a registered version without protection

Figure 3.12: A very complicated registration file

Figure 3.13: When a hardware key isn't connected, the program cannot be started

Figure 3.15: The application will start with some functions limited

Figure 3.14: The program has just found out that the hardware key isn't connected

Figure 3.16: One version of the HASP hardware key

Figure 3.17: There are several kinds of Sentinel hardware keys



Chapter 4: CD Protection Tricks



Figure 4.1: Protective software has just found out that the original CD wasn't inserted

Figure 4.2: VOB is given away by the section name in the PE file header

Figure 4.3: You can even find the version used at the end of the protected file

Figure 4.4: Demo version of CDRWin, which allows you to write at only 1x speed. Notice the

warning that the software is heavily protected against attempts to remove the protection. In fact,

pirate versions do not write correctly.



Chapter 5: Program Compression and Encoding—Freeware and Shareware



Figure 5.1: You cannot select maximum compression in the unregistered version of ASPack Figure 5.2: ASPack lets you choose from several languages Figure 5.3: Luckily Ding Boy's PE-Crypt has a menu in English Figure 5.4: NeoLite

Figure 5.5: PECompact's menu is a slightly larger one than those of other compression programs Figure 5.6: Don't waste your time looking for a classic 32-bit-application graphic display in

PELOCKnt

Figure 5.7: With PE-Crypt you have to think twice about which items to select in the menu before encoding an application

Figure 5.8: PE-SHiELD, presently the best encoding program

Figure 5.9: Petite is supplied with a graphical interface that makes working with it easier

Figure 5.10: Demo version of Shrinker

Figure 5.11: UPX can decompress files using the -d switch

Figure 5.12: WWPack32's environment makes it easier to compress more files at a time



Chapter 6: Commercial Software Protection Programs



Figure 6.1: It isn't difficult to generate registration keys in ASProtect

Figure 6.2: The preview function for this program only works after registration

Figure 6.3: It is almost impossible for a cracker to find the correct registration key for a program

protected by ASProtect

Figure 6.4: At first glance, the registration file for an application protected by FLEXlm doesn't seem complicated

Figure 6.5: The InstallShield environment is a pleasant surprise

Figure 6.6: Registration check during application installation

Figure 6.7: Creating a project in the Armadillo Software Protection System

Figure 6.8: Almost anybody can learn how to work with Vbox Builder

Figure 6.9: It is very easy to create an initial dialog box for your program with Vbox

Figure 6.11: It's really easy to work with keys in SVKP

Figure 6.12: Set up of trial version parameters takes no more than a minute



Chapter 7: Anti-Debugging, Anti-Disassembling, and Other Tricks for Protecting Against Softice and TRW



Figure 7.1: ASProtect has just found an active debugger

Figure 7.2: Bleem! contains powerful protection that also tries to discover a debugger Figure 7.3: FrogsICE is one of the biggest enemies of anti-debugging tricks



Chapter 9: Other Protection Tricks



Figure 9.1: Besides decoding, ProcDump is able to do other useful things that any normal programmer can use

No comments:

Post a Comment