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
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment