Injecting Kernel32.dll

Protecting against TerminateProcess
published by +Tsehp April 2001
 

by Harlequin

  April  2001

 

  

Tools Used:

 
 W32Dasm V 8.93
 Text/Hex Editor
ProcDump
Hiew v6.0

 

 

Introduction

Ok I don't have a lot of time available just now but this one I feel needs doing so here goes........

From a post at Zor's board I found this link: http://www.nsclean.com/psc-bionet.html
and read the following:

A programmer named "Rezmond" maintains and distributes a remote control trojan horse backdoor program called "BioNet." BioNet has in the past been just another commonplace trojan horse until a new release approximately one month ago. The most recent "312 and 313" releases of BioNet now pose a severe risk as a result of new capabilities which exploit a major shortcoming in the design of ALL versions of Microsoft Windows which permits security software to be shut down without any indication to the user that their protective software is no longer functioning. In addition, the most recent releases of BioNet not only incapacitate security software, they can also corrupt the software in such a manner that it cannot be reloaded or replaced. This new capability destroys popular firewall, antivirus and antitrojan software prior to installing itself into the victim's system. Because of a major design flaw in Windows itself, there is no solution for this problem unless Microsoft redesigns Windows itself. Privacy Software Corporation and others in this business have brought this to Microsoft's attention on numerous occasions to no avail.

BioNet exploits a flaw in Windows involving a function called "TerminateProcess()" which unconditionally destroys a running program without any notification to the program that the "rug is being pulled from under it." Without any notification from Windows, a program cannot protect itself from being stopped. Once a process has been stopped, the underlying files can then be written to and corrupted by malicious software. BioNet and a number of other trojan horses are now starting to exploit this routinely, nullifying antivirus and other security programs such as firewalls or file scanners. Since the "TerminateProcess" function does not send a message from Windows to the affected program, allowing it to veto a shutdown, there is no defense for security software to prevent the function from serruptitously ending any security program. Our BOClean product has just been redesigned to attempt to protect against this in our own product but others in the security business need to take similar steps to at least notify people that their security software has been shut down. Ultimately, Microsoft must be encouraged to provide a message to running programs that they are about to be shut down so that some means of protection can be provided to users of these programs or at minimum the ability to provide a message that the program is being shut down.

Well the words ' there is no solution for this problem' are a little severe! there is always a solution and it does not mean sitting around for our friend Billy to fix his software. So in this little essay I will attempt to fix this little problem and protect our FireWalls. :-)

Preliminaries

As with any code injecting but more importantly so with system files ALWAYS BACKUP FILES FIRST!!!!!!

The TerminateProcess() API is found in the kernel32.dll. It is my intention to inject some code into the dll which will give us more control over this API. However the fact that kernel32.dll is a system dll loaded with windows and protected by it makes it somewhat difficult to work on. To get around this problem I simply made a copy of kernel32.dll and named it Kernelxx.dll. Due to the fact that kernel32.dll always loads at address BFF70000h and one copy (the real one) is already loaded it was neccassary to edit the base address of my kernelxx.dll. For this I used ProcDump and changed the base address to AFF70000h. I then wrote myself a quick little program which uses the TerminateProcess API which I could use to test my alterations. After compiling this program I hex edited the kernel32.dll name in its import table to kernelxx.dll so that it would use my copy.

At a glance it would appear kernel32.dll does have several hard coded references within its functions but the fact that the real kernel32.dll is loaded at these addresses my copy seems to work ok.

Another problem I encountered here was that kernel32.dll does not have any imports, or none that are very easily apparent. It does use the MessageBoxA in User32.dll somehow but I could not easily determine how and as I said time is precious just now. To this end I decided to use a similar method to that in my W32Dasm Disassembled essays placing the majority of my code in an external dynamic link library and simply calling this library from kernel32. This also has the advantage that the dll can be used with any version of kernel32.dll once the load and call have been patched in. It is not my intention to produce a patch for this essay, once it is written I will be protected :-) if you want to be too then you will have to patch your own kernel32.dll :-)

Due to the difficulties of pathcing a dll in w32dasm (not impossible but not easy) especially one which is running in protected memory. (I circumvented this by changing the image address to 0FF70000h but it still was not very attractive.) I decided to use Hiew a program I have not used before but which proved to be very useful. So that w32dasm was not holding open my dll I made a second copy of it an called this one kernelw3.dll and disassembled this one in w32dasm. Then between Hiew and this deadlisting the changes were relatively painless.

The Essay

Objectives: 

  1. Direct program flow from TerminateProcess to call our dll

  2. Call the dll
  3. Display a messagebox asking the user if the process should be terminated.
  4. Return control to TerminateProcess dependant on the users response.

Ok so first off disassemle kernelw3.dll in w32dasm and locate our API.

Exported fn(): TerminateProcess - Ord:02FDh
:AFF95CB4 56 push esi
:AFF95CB5 57 push edi
:AFF95CB6 E8DD45FEFF call AFF7A298
:AFF95CBB A17094FCBF mov eax, dword ptr [BFFC9470]
:AFF95CC0 50 push eax
* Reference To: KERNEL32.Ordinal:0061
|
:AFF95CC1 E8F2E4FDFF call AFF741B8

I decided to jump away from the code just as soon as we arrive to make things less complicated. So I located some free space to jump to at: AFFC5170h. With the w32dasm highlight bar positioned over the relevant line the file offset can be read in the status bar. So in Hiew goto(F5) 25CB4h and patch in:

jmp 55670

to jump to our free space.

Note that this free space is directly after the export table but strangely (luckily:) it is still in the code section. However make sure you leave a few 00 bytes after the export table as you do not want kernel32.dll thinking that your code is part of its export table. I left 3 dwords just to be safe.
Now I need to load my dll and the function within it, I have not written it yet but that does not concern me at the momment. Using w32dasm to scroll down through the zero bytes at the end of the code section I decide to place my dll name at offset: 55F96h RVA:AFFC5F96h, once again using Hiew just changing modes to Hex I edit in HTerm.dll. (I don't know why I never used this program before but I will certainly be using it more from now on :)

So now back to our freespace at 55164h and push our new dll name onto the stack, then using w32dasm to locate the LoadLibraryA function in kernelw3.dll simply edit in a call to it:

pushad
push 0AFFC5F96h ;HTerm.dll
call 76D4h
test eax,eax
je 55194h

Now when it returns the library handle will be in eax so we push it onto the stack then we need to push our function name so first edit it into the file at offset: 55FA0h 'Term' and then push it onto the stack. Finally locate GetProcAddress and call it:

push 0AFFC5FA0h ;Term
push eax
call 6DACh
test eax,eax
je 55194h
call eax

So now we are effectively in our new dll carrying out our yet to be written code. I want to return from that dll with the result of the user decision so lets say we place the result in eax. Here we now need to check eax and act accordingly. Lets say that a 1 in eax and we should terminate the process and a 0 and we do not:

test eax,eax
jne 55196h
popad
ret ;Leave kernel32.dll
popad
push esi ;replaced instruction
push edi ;replaced instruction
call 0A298h ;replaced instruction
jmp 25CBBh ;back to TerminateProcess

and that does us. I just entered default jumps then edited them afterwards when I knew where I wanted to jump to.
You will notice that if there is an error loading the dll or retrieving the function handle then the default is not to Terminate the process. This makes sure that simply deleting the dll does not defeat our new protection.
That was pretty simple and painless, I have suffered to long without Hiew.

Ok for the dll.

I will not go into details about it here I shall include the source code with this essay and of course the compiled version. A basic summary of what it does:

and that is that, done....

The final W32Dasm output of the above looks like this:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:BFF95CB4(U)
|
:BFFC5170 60 pushad

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:BFFC5106(C)
|
:BFFC5171 68965FFCBF push BFFC5F96
* Reference To: KERNEL32.LoadLibraryA
|
:BFFC5176 E85925FBFF call BFF776D4

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:BFFC5110(C)
|
:BFFC517B 85C0 test eax, eax
:BFFC517D 7415 je BFFC5194
:BFFC517F 68A05FFCBF push BFFC5FA0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:BFFC511F(C)
|
:BFFC5184 50 push eax
* Reference To: KERNEL32.GetProcAddress
|
:BFFC5185 E8221CFBFF call BFF76DAC
:BFFC518A 85C0 test eax, eax
:BFFC518C 7406 je BFFC5194
:BFFC518E FFD0 call eax
:BFFC5190 85C0 test eax, eax
:BFFC5192 7502 jne BFFC5196

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:BFFC5119(C), :BFFC517D(C), :BFFC518C(C)
|
:BFFC5194 61 popad
:BFFC5195 C3 ret



* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:BFFC5131(C), :BFFC5192(C)
|
:BFFC5196 61 popad
:BFFC5197 56 push esi
:BFFC5198 57 push edi
:BFFC5199 E8FA50FBFF call BFF7A298
:BFFC519E E9180BFDFF jmp BFF95CBB

NOTE:
The above works fine with the copy and the image base adjusted to AFF70000h but when you are going to copy it back to kernel32.dll either as a patch or directly remember to change the direct memory references to BFF.

 

In Conclusion 

Stuff waiting for Big Billy to fix these little problems nothing is impossible. I am not going to make this whole thing a patch you will have to alter your own kernel32.dll the process is pretty simple! It must be I did it :-) Besides there are probably so many versions of kernel32.dll out there that a patch would be impractical.

You will of course have to place the HTemp.dll in your system directory so kernel32.dll can find it

I will include the compiled HTerm.dll and its source code with this essay in a pack Here.
I will also include the program which I used for testing (PID.exe) although the import table is set back to normal. This program will simply load calc.exe determine its PID and then attempt to close it.

Final Notes

Thanks go to:

 Big Billy for giving me these problems to fix

Kemerovo? I think. The author of Hackers View an amazing little program which made all this possible in only an hour. I made a mistake pushing the parameters to GetProcAddress in the wrong order but Hiew even runs in dos so correcting the problem was a simple affair :-). I wish I had found it a long time ago :)

 

 

Essay by:      Harlequin
Page Created: 12th April 2001

I will include the dll source with the essay but just in case it becomes seperated I will place the code from the .asm file here, I am sure you can fill in the .inc file etc yourself:

.386
.MODEL FLAT,STDcall

include HTerm.inc

;Local Function prototypes
Term PROTO

.DATA
BoxTitle db 'Terminate Process?',0
Message db '%s is attempting to terminate %s. Proceed?',0

Proc1 db 80h DUP(?)
Temp db 80h DUP(?)
WsprintfArg dd 2 DUP(?) ;Arguments for wvsprintf

PID dd ?
Shnd dd ?
hinst_ dd ?

PIDStruct PROCESSENTRY32 <> ;Process structure as defined in the inc


.CODE

EntryPoint proc DLLinst:DWORD, Reason:DWORD, Reserved1:DWORD
mov eax,1
ret
EntryPoint endp

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Term Exported function
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Term proc
Start:
mov eax,dword ptr[esp+4] ;get the PID from the stack
mov PID,eax ;and save it

invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0 ;create a snapshot
mov Shnd,eax
mov eax,SIZE PIDStruct
mov PIDStruct.dwSize,eax
invoke Process32First,Shnd,offset PIDStruct
Loop1:
mov eax,dword ptr[PIDStruct.th32ProcessID]
mov edx,PID
cmp eax,edx
je FoundIt
invoke Process32Next,Shnd,offset PIDStruct
test eax,eax
jne Loop1
RET


FoundIt:
invoke lstrcpyA,offset Proc1,offset PIDStruct.szExeFile
invoke GetCurrentProcessId
mov PID,eax
invoke Process32First,Shnd,offset PIDStruct
Loop2:
mov eax,dword ptr[PIDStruct.th32ProcessID]
mov edx,PID
cmp eax,edx
je GotYa
invoke Process32Next,Shnd,offset PIDStruct
test eax,eax
jne Loop2
GotYa:
mov WsprintfArg,offset PIDStruct.szExeFile
mov WsprintfArg+4,offset Proc1
invoke wvsprintfA,ADDR Temp,ADDR Message,ADDR WsprintfArg

invoke MessageBoxA,0,offset Temp,offset BoxTitle,1034h
.IF eax!=6
xor eax,eax
.ENDIF
RET
Term Endp
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; End Term exported function
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@


End Entrypoint