Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   С/С++, C#, Delphi, .NET, Asm (https://forum.antichat.xyz/forumdisplay.php?f=24)
-   -   Событие завершения процесса (https://forum.antichat.xyz/showthread.php?t=76643)

gevara 10.07.2008 09:33

Событие завершения процесса
 
Есть произвольный код в адресном пространстве некоторого процесса. Этот код должен отлавливать событие завершения этого процесса. Что-то типа сообщения DLL_PROCESS_DETACH, но это сообщение приходит динамическеим библиотекам, а у меня произвольный код. Можно, конечно, перехватывать функцию ExitProcess, ExitThread, но не хочу гемора с перехватом...

0verbreaK 10.07.2008 09:52

а вопрос?

gevara 10.07.2008 10:40

Код сисдит в апликухе и должен сохранить логи по завершении процесса.

slesh 10.07.2008 12:27

Теоретически можно попробовать следующее.
При старте процесса в стеке сохраняется адрес возврата именно по этому и можно завершать программу по команте ret (если в стек ничего не добавлялось)
При нормально анализе можно в стеке найти этот адрес и подменить на адрес своего обработчика. Геморно но всёже как один из вариантов
ну и + желательно ExitProcess перехватить и тоже пустить на свой обработчик

Ryu 10.07.2008 12:30

если код действительно в том же процессе, то только сообщение обрабатывать и хукать ничего не надо. (wm_terminate или что то в этом роде), но это только если приложение win а не дос.

slesh 10.07.2008 12:50

2 Ryu млин, а если приложения WIN но без оконное? ТОгда регистрировать обработчик сообщений нужно. А если консольное, то вообще тогда нужно через SetConsoleCtrlhandler()
так что отлавливать сообщения - многое нужно учесть. При том, что как выше сказано код в произвольном процессе и следовательно он туда както внедряется и должен быть по крайней мере независимым от своего расположения в памяти.

P.S. Ну если код ведет логи, то пусть сразу их и пишет и делает чтото типа flush чтобы сразу скинуть на винт, а там при заврешении процесса - автоматом все дискрипторы загроются

Jes 10.07.2008 13:20

Цитата:

Сообщение от gevara
Можно, конечно, перехватывать функцию ExitProcess, ExitThread, но не хочу гемора с перехватом...

а какой гемор? поменяй в таблице импорта адреса и всё
(если там конечно не динамич импорт)

SlyBit 10.07.2008 13:22

gevara

Тут только перехватывать ZwExitProcess и ZwExitThread.

P.S. Фишка со стеком, как и с оконными сообщениями не прокатит, если приложение само вызывает ExitProcess.

slesh 10.07.2008 13:32

2 SlyBit Блин чем дальше в лес, тем больше дров, которые человек наломает.
Такими темпами можно будет уже говорить о методах сплайсенга функций а там уже и до Native Api дойдем со стороны ядра.

P.S. 2 gevara cразу скажи в каком приложении ты это собираешься делать? или в любых. А то мы можем так далеко зайти в обсуждении, а на самом деле всё будет просто.

gevara 10.07.2008 16:16

приложение оконное.
- адрес возврата подменить не получится - что если приложение многопоточное? перехватывать CreateThread проще перехватить ExitThread
- Вообще сообщение DLL_PROCESS_DETACH приходит либам в том случае, когда последний поток завершается вызовом ExitThread, то есть перехват оконных сообщений скорее всего всё-таки возможен, но я почему-то не вижу сообщения WM_TERMINATE... такого сообщения в SDK нет вообще.
- есть WM_CLOSE, WM_DESTROY, WM_QUIT, но стоит ли надеяться на эти сообщения? множество апликух не юзают их вообще. апликуха может иметь несколько окон и при закрытии одного из них апликуха продолжает работать (приходит сообщение WM_CLOSE)
- есть вот какая тема - добавить свою запись в двусвязный список LDR_MODULE (или как его там...), указав адрес обработчика, которому и будут приходить сообщения DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH и прочие...

gevara 10.07.2008 16:24

Цитата:

Тут только перехватывать ZwExitProcess и ZwExitThread.
таких функций не сущесмтвует

Hellsp@wn 10.07.2008 17:23

вот такие функции есть:

ntdll.ZwTerminateProcess
ntdll.ZwTerminateThread

SlyBit 10.07.2008 17:43

Цитата:

таких функций не сущесмтвует
сорри, ошибся, имел ввиду:

ntdll.ZwTerminateProcess
ntdll.ZwTerminateThread

Цитата:

- есть вот какая тема - добавить свою запись в двусвязный список LDR_MODULE (или как его там...), указав адрес обработчика, которому и будут приходить сообщения DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH и прочие...
Вообщем ты себе все круто усложняешь. Лучше все же вернись к перехвату функций, можно подменой адреса в таблице ипорта (как советовал Jes), если не хочешь возиться со сплайзингом.

Если хуки тебя не в какую не устраивают, то можно вместо добавления новой структуры в двусвязный список загруженных библиотек (довольно палевно, т.к. такие программы как Process Explorer увидят странную библиотеку), изменить базу существующей библиотеку на свой обработчик. При вызове твоего обработчика проверяй 2-ой параметр Reason на равенство коду DLL_PROCESS_DETACH, пиши там свои логи и затем вызывай оригинальную точку входа библиотеки с такими же параметрами. Подменяй точку входа у невыгружаемых библиотеках, таких как user32.dll, kernel32.dll, ntdll.dll.

Теперь постараюсь объяснить как выйти на этот список и что нужно менять.

Для начала нужно получить адрес структуры PEB процесса. Указатель лежит по адресу fs:[30h], либо можно взять его из структуры PROCESS_BASIC_INFORMATION по смещению +4 байта. Указатель на саму струкуру PROCESS_BASIC_INFORMATION получаешь вызвав функцию NtQueryInformationProcess со вторым параметром равным 0 (ProcessBasicInformation). Указатель на PEB есть.

По смещению 0xC в PEB находится указатель на структуру PPEB_LDR_DATA. В этой структуре находятся 3 указателя на точки входа двусвязных списков, содержащих информацию о библиотеках на стадии загрузки, уже загруженных в память и на стадии инициализации. Нас интересует второй указатель.

Теперь нам нужно обойти список LDR_DATA_TABLE_ENTRY и сравнить имя BaseDllName с "ntdll.dll" например. Как находим, заменяем EntryPoint на наш обработчик.

Это все в теории, если что-то не так - поправьте.

NTDLL.h

gevara 10.07.2008 18:13

Что толку от перехвата таблицы импорта, если не знаешь какой именнго модуль завершит последний поток? вообще прога может завершить своё выполнение по рету и ExitProcess будет вызван из kernel32 - тут только сплайсинг. а вариант с изменением адреса входа действительно реальный, хотя, возможно, и не самый лучший. ведь адрес точки входа будет лежать за границами модуля, что может вызвать подозрения...

SlyBit 10.07.2008 18:26

да, я про таблицу экспорта kernel32 и ntdll конечно...какая нафиг импорта ;) мне кажется вариант со сплайзингом такой же палевный как и подмена адреса точки входа либы.

Цитата:

вообще прога может завершить своё выполнение по рету и ExitProcess будет вызван из kernel32
Подменяй тогда адрес возврата.

zl0y 10.07.2008 18:50

KiSystemFastRet+ZwTerminateProcess.

gevara 10.07.2008 20:16

Цитата:

Сообщение от SlyBit
да, я про таблицу экспорта kernel32 и ntdll конечно...какая нафиг импорта ;) мне кажется вариант со сплайзингом такой же палевный как и подмена адреса точки входа либы.


Подменяй тогда адрес возврата.

про адрес возврата я уже сказал - не катит на случай многопоточности

SlyBit 10.07.2008 20:19

А вот и пример подмены точки входа библиотеки подоспел (описание несколькими постами выше):

Код:

#include <windows.h>
#include "ntdll.h"
 
#pragma comment(linker, "/ENTRY:Main")
 
typedef DWORD (WINAPI *PTRUEENTRY)(HMODULE Module, DWORD Reason, LPVOID Reserved);
 
PVOID dwTrueAddr;
 
DWORD WINAPI CatchExit(HMODULE Module, DWORD Reason, LPVOID Reserved)
{
    if(Reason == DLL_PROCESS_DETACH) {
        // Событие перед завершение приложения
    }
    return ((PTRUEENTRY)dwTrueAddr)(Module, Reason, Reserved);
}
 
VOID WINAPI HookDllEntry(PWCHAR pDllName, PVOID pDummyEntry)
{
    PPEB pPeb;
    PPEB_LDR_DATA pPebLdrData;
    PLDR_DATA_TABLE_ENTRY pLdrDataTableEntry;
    DWORD dwBlink;
 
    __asm {
        mov eax, fs:[0x30]
        mov pPeb, eax
    }
   
    pPebLdrData = (PPEB_LDR_DATA)pPeb->Ldr;
   
    // Запоминаем адрес последнего элемента в двусвязном списке
    dwBlink = *(PDWORD)pPebLdrData->InMemoryOrderModuleList.Blink;

    // Получаем указатель на первую структуру в двусвязном списке
    pLdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)pPebLdrData->InLoadOrderModuleList.Flink;
   
    // Обходим все структуры и как находим библиотеку с именем pDllName, изменяем её точку входа
    do {
        pLdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)pLdrDataTableEntry->InLoadOrderModuleList.Flink;
        if(!lstrcmpW(pLdrDataTableEntry->BaseDllName.Buffer, pDllName)) {
            dwTrueAddr = pLdrDataTableEntry->EntryPoint;
            pLdrDataTableEntry->EntryPoint = pDummyEntry;
        }
    } while(dwBlink != *(PDWORD)pLdrDataTableEntry->InMemoryOrderModuleList.Flink);
}
 
VOID WINAPI Main()

    HookDllEntry(L"ntdll.dll", CatchExit);
    ExitProcess(0);
}


gevara 10.07.2008 20:29

SlyBit, пасиб, конечно. но это вобщем-то понятно. у меня ситуация несколько другая - вот я и думаю что сделать лучше: перехват PEB или сплайсинг ExitThread или ExitProcess. Вообще-то мой код и так хучит кое-какие функции. В частности имеет доступ к оконным сообщениям. так что идеальный вариант - отслеживать сообщения (есали такие есть) о закрытии всех окон или что-то типа этого...

SlyBit 10.07.2008 21:18

gevara

Я думаю, что оконные сообщения отпадают по как минимум 2м причинам: не отслеживается самостоятельный вызов ExitProcess программой, не всегда они "правильно" обрабатываются программой. Пример Outpost, при посылке его главному окну сообщения WM_CLOSE (щелчек на крестик в правом верхнем углу), оно просто сворачивается, вместо того чтобы закрыться. На сообщения WM_DESTROY и WM_QUIT вообще нет никакой реакции. Калькулятор же спокойно закрывается получив WM_CLOSE.

Выбор между модификацией PEB и сплайзингом ExitProcess и ExitThread. В первом случае все просто. Во втором нужно считать количество потоков, приложение закроется как только закроют его последний поток. Для этого нужно будет перехватывать CreateThread и икрементировать счетчик кол-ва потоков и декрементировать при выхове ExitThread, либо подсчитывать количество потоков перед вызовом ExitThread.

razzzar 10.07.2008 22:21

когда-то писал программку, в которой надо было выполнить код, при завершении собственно этой программы. помогла кртшная функция _onexit(); она ставит функции на выполнение, при завершении процесса.
http://msdn.microsoft.com/en-us/libr...08(VS.71).aspx

gevara 10.07.2008 23:15

Цитата:

Сообщение от SlyBit
gevara

Я думаю, что оконные сообщения отпадают по как минимум 2м причинам: не отслеживается самостоятельный вызов ExitProcess программой, не всегда они "правильно" обрабатываются программой. Пример Outpost, при посылке его главному окну сообщения WM_CLOSE (щелчек на крестик в правом верхнем углу), оно просто сворачивается, вместо того чтобы закрыться. На сообщения WM_DESTROY и WM_QUIT вообще нет никакой реакции. Калькулятор же спокойно закрывается получив WM_CLOSE.

Выбор между модификацией PEB и сплайзингом ExitProcess и ExitThread. В первом случае все просто. Во втором нужно считать количество потоков, приложение закроется как только закроют его последний поток. Для этого нужно будет перехватывать CreateThread и икрементировать счетчик кол-ва потоков и декрементировать при выхове ExitThread, либо подсчитывать количество потоков перед вызовом ExitThread.

Вынужден тебя огорчить.
1) при перехвате ExitProcess считать потоки совершенно не обязательно
2) при перехвате ExitThread их считать тоже не обязательно. посмотри что делает система:
Код:

7C80CCA9 > 6A 14            PUSH 14
7C80CCAB  68 10CD807C      PUSH kernel32.7C80CD10
7C80CCB0  E8 1658FFFF      CALL kernel32.7C8024CB
7C80CCB5  64:A1 18000000  MOV EAX,DWORD PTR FS:[18]
7C80CCBB  8BF0            MOV ESI,EAX
7C80CCBD  8975 E0          MOV DWORD PTR SS:[EBP-20],ESI
7C80CCC0  33FF            XOR EDI,EDI
7C80CCC2  57              PUSH EDI
7C80CCC3  6A 04            PUSH 4
7C80CCC5  8D45 E4          LEA EAX,DWORD PTR SS:[EBP-1C]
7C80CCC8  50              PUSH EAX
7C80CCC9  6A 0C            PUSH 0C
7C80CCCB  6A FE            PUSH -2
7C80CCCD  FF15 2811807C    CALL DWORD PTR DS:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationThread
7C80CCD3  3BC7            CMP EAX,EDI
7C80CCD5  75 05            JNZ SHORT kernel32.7C80CCDC
7C80CCD7  397D E4          CMP DWORD PTR SS:[EBP-1C],EDI
7C80CCDA  75 28            JNZ SHORT kernel32.7C80CD04
7C80CCDC  FF15 F414807C    CALL DWORD PTR DS:[<&ntdll.RtlFreeThread>; ntdll.RtlFreeThreadActivationContextStack
7C80CCE2  E8 3A000000      CALL <JMP.&ntdll.LdrShutdownThread>
7C80CCE7  39BE 940F0000    CMP DWORD PTR DS:[ESI+F94],EDI
7C80CCED  0F85 F6770300    JNZ kernel32.7C8444E9
7C80CCF3  C686 750F0000 01 MOV BYTE PTR DS:[ESI+F75],1
7C80CCFA  FF75 08          PUSH DWORD PTR SS:[EBP+8]
7C80CCFD  57              PUSH EDI
7C80CCFE  FF15 6C14807C    CALL DWORD PTR DS:[<&ntdll.NtTerminateTh>; ntdll.ZwTerminateThread
7C80CD04  FF75 08          PUSH DWORD PTR SS:[EBP+8]
7C80CD07  E8 96FD0000      CALL kernel32.ExitProcess


SlyBit 11.07.2008 02:03

Я совсем не огорчен, это были только мои предположения ;) Выходит в перехвате ExitThread нет необходимости. Однако приложение может поубивать свои потоки функцией TerminateThread или завершиться с помощью TerminateProcess.


Время: 22:42