Page 1 of 2 12 LastLast
Results 1 to 15 of 23

Thread: Harlequin's "Protecting against TerminateProcess" - impossible to work

  1. #1
    DakienDX
    Guest

    Harlequin's "Protecting against TerminateProcess" - impossible to work

    (First part, since my message is longer than 4098 bytes)

    Hello everybody !

    First of all, I don't want to talk about who is able to program good programs and who's not.
    I haven't seen much of Harlequin so I can't judge him, this error could have happened to erverybody.

    To come to the main subject of this post, you know that Harlequin tried to find a way to protect programs from being terminated by trojan horses. They use the API-function TerminateProcess() to terminate a process. This function doesn't send any warning to the application being terminated and just kills it.

    Harlequin tried to fix this by injecting some code into Kernel32.dll to call an external library which asks if you wish to allow the program which tries to terminate an other one to do it.

    He called GetCurrentProcessId to get the PID of the process calling TerminateProcess() and the creates a snapshop of all processes and compares the PID with them to get the path of the calling program.
    This is a good idea and well implemented.

    But... This isn't made at first.

    At first he tries to get the path of the application to be terminated.
    He does it the following way:

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

    and then compares the "PID" to the processes in the snapshot. But the "PID" isn't the ProcessID, it's the HANDLE of the process to be terminated, so you can't find any matches in the snapshot, because you would compare if the PID and a HANDLE are the same. If it doesn't find any match, it returns with EAX = 0. This forces the Kernel32.dll patched code to exit without terminating. This does mean two things: 1.) you can't terminate any process any longer, 2.) since the return to the calling process is made via a "ret" and not via a "ret 8" (since TerminateProcess() has two parameters on the stack), the calling process has the stack 8 bytes to low and will probably crash.

    I don't know if there is a way to get the PID of a process if you have only it's HANDLE.


    (Continued in the next part)
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  2. #2
    DakienDX
    Guest
    (Last part)

    But now the question: Why did it work with the example PID.EXE included in the package together with the other sources? What made Harlequin think his code works?

    TerminateProcess() jumps to this code:

    pushad ; save all regs - IMPORTANT (*)
    push 0AFFC5F96h ; lib: HTerm.dll
    call 76D4h ; LoadLibraryA
    test eax,eax
    je @@1
    push 0AFFC5FA0h ; func: Term
    push eax ; lib base
    call 6DACh ; GetProcAddress
    test eax,eax
    je @@1
    call eax ; call Term - IMPORTANT (*)
    test eax,eax
    jne @@2
    @@1:
    popad
    ret ; BUG - should be "ret 8"
    @@2:
    popad
    push esi ;replaced instruction
    push edi ;replaced instruction
    call 0A298h ;replaced instruction
    jmp 25CBBh ;back to TerminateProcess


    Remember this?

    Term proc
    Start:
    mov eax,dword ptr[esp+4] ;get the PID from the stack

    This doesn't set EAX to the value passed to TerminateProcess(), but to the value of EDI (since the PushAD) !

    In the PID.EXE example program he uses this code

    mov edi,[PIDStruct.th32ProcessID] ; IMPORTANT (*)
    push edi
    push offset format
    push offset PID
    call _wsprintfA
    call MessageBoxA,Hinst,offset PID,offset FileName,0
    call OpenProcess,PROCESS_TERMINATE,0,[PIDStruct.th32ProcessID]
    mov Phand,eax
    call TerminateProcess,Phand,0 ; HANDLE, ExitCode

    Since EDI isn't chanced by Windows, EDI contains the PID and so it can find the path of the process to be terminated in the snapshot.


    Again, I didn't want to attack Harlequin, I only wanted to tell and explain you, why his patch doesn't work as it should and so doesn't protect our firewalls from being terminated by trojan horses.
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  3. #3
    hz
    Guest
    Hiya DakienDX,
    I found it very informative, thanks.
    "Again, I didn't want to attack Harlequin", I'm sure Harlequin won't
    regard it as an attack, we are all here to learn, right?.
    regards

    Man that was a polite response, does that get me in the new "elite" forum? ;D
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  4. #4
    Lord Rhesus
    Guest
    Harlequin's working in dark dingy foreign lands at the moment so he probably won't be able to reply. But yes your absolutely right, we can assume that when Harlequin was writing the HTerm.dll and was looking for where the handle was located on the stack he must have noted down what the handle was and searched the stack for it. At the first occurrence (which was edi's value as left from the pusha) he must have assumed that this was the handle when actually it was the value further along the stack. A simple fix for this would be to change the code in Hterm.dll from:

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

    to:

    ;---code snippet start---
    ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    ; Term Exported function
    ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    Term proc
    Start:
    mov eax,dword ptr[esp+38h] ;get the PID from the stack
    mov PID,eax ;and save it
    ;---code snippet end---

    Well spotted DakienDX!
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  5. #5
    DakienDX
    Guest
    Hello Lord Rhesus !

    Your suggestion won't fix the problem.

    mov eax,dword ptr[esp+38h] gets the parameter pushed originally on the stack to TernimateProcess(), but this is NOT the PID, but the HANDLE to the process.

    A HANDLE is something like 10h,14h,18h,..., but a PID is something like FFFF5353h,FFEFA742h,FFFE8681h or similar.

    I think (as far as I know) that it's impossible to get the path of a process if you have only it's HANDLE (and not it's PID) with standard API-calls.
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  6. #6
    Lord Rhesus
    Guest
    doh! Silly me, I didn't read the post properly and I thought that the only problem was the stack push. When I posted the *fix* (be it not an actual fix as it doesn't solve the underlying problem) I only tried it using the pid program before posting. Just before checking the message board again I tried to see if it would ask when I tried to kill something in procdump (which uses terminateprocess). It didn't, all I can say is doh!
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  7. #7
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Posts
    4,129
    Blog Entries
    5
    Hi guys,

    I see what you mean. It looks like the HANDLE of the current process, which is passed to TerminateProcess before term.dll is called and saved with the pushad, is being compared in Loop1 with the PID stored in the PROCESSENTRY32 structure after Process32First/Next is called. It's like comparing apples and oranges.


    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


    It's in the 2nd part where the actual PID is retrieved from the calling process with GetCurrentProcessID, compared *again* with the PIDStruct.th32ProcessID value, and when a match is found in Loop2 jumps to the MessageBox giving you the option to abort the TerminateProcess.

    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
    .
    .
    invoke MessageBoxA


    Since the current process is always going to be the one which called TerminateProcess in kernel32.dll, I'm wondering why Loop1 is there at all. Why not just call CreateToolhelp32Snapshot, which creates a handle to the snapshot which is used by the other Toolhelp32 API's, then go through the Process32First/Process32Next routine, comparing the PIDStruct.th32ProcessID value and the GetCurrentProcessID returned PID as it does anyway in Loop2?

    It just sort of looks like
    Start:
    mov eax,dword ptr[esp+4] ;get the PID from the stack
    mov PID,eax ;and save it

    and Loop1 are redundant since we know we can get the current PID (the terminating program) with GetCurrentProcessID.

    Kayaker

  8. #8
    DakienDX
    Guest
    Hello Kayaker !

    The first loop was actually meant to get the name of the program which is to be terminated.

    A "C:\WINDOWS\SYSTEM\ATI32KEY.EXE wants to terminate a program. Continue ?" message would not do the thing it was meant to do.
    It should do a "C:\WINDOWS\SYSTEM\ATI32KEY.EXE wants to terminate C:\Program Files\FireWallAntiVirus\FireWallAntiVirus.exe. Continue ?" message to inform the user what's going on.

    Therefore the first loop was implemented, but if it fails, it exits without asking if a program is allowed to terminate a UNKNOWN program.
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  9. #9
    Harlequin
    Guest
    Hi


    First of all I need to say that if you are looking for high quality programming you will not find it in my work :-) I do not profess to be a programmer just someone who likes to play with a few bytes here and there. I have no expertise and am like many only learning as I go. My work is available only because I am prepared to make a fool of myself if it helps others in some way. Obviously my intention is to help others by sharing what I do know, but, if indeed some learn by my mistakes then thats ok too, the end result is the same :-) Whilst it is most certainly true that no matter how much you know there is always someone who knows more, the reverse is also true. As such I am a firm believer that the more information avaiable the easier it is to progress. Please forgive me if I make errors but the only sure way not to do so is to do nothing!

    Secondly I would like to appologise for my delay in responding to your posts, as Lord Rhesus mentioned I have been away and have only just returned.

    Ok.

    The error which you point out is of course correct, I wrote this soution in a big hurry and then had to go away almost immediatly afterwards. However I also had noticed the error before I left and had made an attempt at a workaround. This I sent to +Tshep who replaced the original with the new one, as the original had only been up a few hours I did not think that anyone would have downloaded it, sorry. Download the current source code to see my changes.

    You pointed out that there is no way to determine the process ID via its handle and on this I have to agree, I have not as yet managed to find a way to do this either. So I have been unable to find a hard and fast solution to the fault. What I have done is to ammend the code so that is searches through the last 40 dword values on the stack checking for a PID. This is not and ideal solution as the application which calls the TerminateProcess API may not have used this stack etc etc. It will trap more instances of the PID though and therefore more often display the name of the application which is to be terminated. The second change I made was to simply display a default message box which informs that an application is trying to Terminate another even though the name of the process to be terminated was not identified. This does work and has been working for me for some time without problem.

    To be honest I am a little disappointed, I wrote this thing very quickly as one possible answer to a potential problem. It was not intended to be a final solution, I was hoping that during the time I was away mayby someone would have improved on this idea and/or bettered it. I accept that the faults you point out are valid, However I included all the source code, an explanation of what my intentions were and a full description of how kernel32.dll was injected. I intentionally did not make a patch program and sincerely believed that any one capable of implementing the changes to kernel32.dll would also be capable of altering and improving my code. Surely it would have been better to use this as an opportunity to not only point out a fault but to offer an alternative or a fix? this way others may have learnt more still, myself included :-)


    Harlequin
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  10. #10
    tsehp
    Guest
    to have a processId with it's handle :
    use what kayaker says : snapshots, then you'll get into the following structure :typedef struct tagPROCESSENTRY32
    {
    DWORD dwSize;
    DWORD cntUsage;
    DWORD th32ProcessID; // this process
    ULONG_PTR th32DefaultHeapID;
    DWORD th32ModuleID; // associated exe
    DWORD cntThreads;
    DWORD th32ParentProcessID; // this process's parent process
    LONG pcPriClassBase; // Base priority of process's threads
    DWORD dwFlags;
    CHAR szExeFile[MAX_PATH]; // Path
    } PROCESSENTRY32;

    so you can build a struct of processId running, then for each procId, use :
    HANDLE OpenProcess(

    DWORD dwDesiredAccess, // access flag
    BOOL bInheritHandle, // handle inheritance flag
    DWORD dwProcessId // process identifier
    );

    with each of processId's you have on first structure, you compare to the handle you've found on the stack and you're done.
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  11. #11
    Harlequin
    Guest
    Hi +Tshep

    Thanks for the input, if only it were that simple!
    I had previously tried using OpenProcess to determine the handle of the process to be terminated however...
    While OpenProcess does indeed return a handle to the process it does not return THE handle. This can be demonstrated by sequencing several OpenProcess calls one immediatly after the other. Each call returns a new, unique handle.

    As I need to make a direct correlation between the handle which was orignially passed to TerminateProcess and the process ID this method will not work.

    This problem is still bothering me and a solution must be possible so any input is eagerly welcomed.

    About the best I have at the moment is to inject Process32Next to save the PID of the last check. This still is not a good solution as the terminating process could have done the identification checks in bulk saving all the handles and then looping through to terminate them. There are also no guarantees that the process was not found on the first pass using Process32First.


    Harlequin
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  12. #12
    I think I found a working solution to the problem. If it's of any interest I can post it here, tho I haven't completed the code yet. I checked Harlequin's essay on Tsehp's mirror, but couldn't see any updates, so I figured the problem was still around.

    Blue skies
    Fake

  13. #13
    Harlequin
    Guest
    HeHe

    I would love to see your solution. You are correct in thinking that I haven't found a way around it yet. I am currently looking into API hooks as an option, hoping to avoid the necessity of patching the system dlls too.

    Please post your code or email me you might just be in time to stop me becoming a bald, alchoholic pill popper.

    Harlequin
    I promise that I have read the FAQ and tried to use the Search to answer my question.

  14. #14
    Well, I hope this will stop you in time, as otherwise you will end up like me, swearing, hissing, bitching at,not to forget cursing, threatening, and hating Microsoft, and their stupid company politics and/or lameass programmers.

    Well, a quick sum up:
    We have:
    - handle to the process being terminated
    We need:
    - processId of process being terminated (actually just the name, but the processId is a quick way to the name)

    So I started debugging windows, to figure out how to attain the goal. A good old gut feeling told me that TerminateProcess had to convert the handle given to it to something else. And after looking a bit into it, I found out that it does. The handle is turned into an address in memory that contains info about the process. If you do a "proc" when in softice, you'll see that in the first column after the name, there is a value, usually round 81xxxxxxh. This is the address of the particular process (referred to from here on as pProcess, since that's the softice name).
    This pProcess is calculated from the handle given to TerminateProcess plus the equivalent pProcess for the current process. Ofcourse, this is worth more or less nothing, if it's not possible to relate the pProcess to the ProcessId. Here comes the beautiful part, the pProcess and the ProcessId are technically the same, only difference lies in an xor function. To get the ProcessId from the pProcess, you xor by a specific value, and vice versa with same value.
    So, now the goals that need be attained have changed:
    - Get pProcess of current process, to be able to get pProcess of process being terminated
    - Get xor value needed to turn pProcess into ProcessId

    Amazingly, this is actually quite easy. The handle for the process to be terminated we have. The rest we can actually get from the nice and simple GetCurrentProcessId function.
    This function first pushes the pProcess of the current process onto the stack, and doesn't delete it from there (goal one achieved). Then it moves the xor value to eax, and xors eax with the stack. Thus the stack will contain the pProcess of the current process, and eax will contain the ProcessId. Xor the two and you get the xor value.
    Now, in the address space of pProcess for the current process, pProcess+44h holds a pointer to an index of all the currently running processes. Add 8 to the pointer, plus the handle to the process to be terminated times two, and you get an address in the index, that holds the pProcess of the process to be terminated.
    The best part of all this: this should actually be a universal way to achieve what we need, from win95 to winMe.

    Now, if you'll excuse me for a minute:
    MICROSOFT SUX!!!!!!!!!!!!
    I spent more than eight hours debugging the system, searching the memory, analyzing the pProcess structure and stuff. All because Microsoft doesn't want you to be able to get your hands on the pProcess address (I'm lacking an api for converting the ProcessId into the pProcess in kernel32), or allow you to obtain a ProcessId for any given process. Bastards piss me off.
    Sorry, steam venting.

    Blue skies
    Fake

    Ps. I included the source of my program+the dll needed. The code to obtain the correct ProcessIds is in the dll. Do excuse the lousy coding discipline of mine, everything is a mess (which is why I wrote this lengthy and probably unnecessary and/or boring txt). Hope you find what you need tho.

  15. #15
    Sorry for posting again, found out it didn't include the source for the dll. Here it is.

    Blue skies
    Fake

Similar Threads

  1. Replies: 0
    Last Post: February 13th, 2014, 07:42
  2. how to generat "1" instead of "uncounted" license
    By joyung in forum The Newbie Forum
    Replies: 38
    Last Post: April 10th, 2012, 03:57
  3. Replies: 4
    Last Post: May 28th, 2009, 13:02
  4. Replies: 1
    Last Post: December 14th, 2007, 13:35
  5. Making IDA work "like" w32dasm
    By instant in forum Tools of Our Trade (TOT) Messageboard
    Replies: 11
    Last Post: October 24th, 2002, 18:50

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •