Как обязательно удалить самое себя после завершения работы.

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Как обязательно удалить самое себя после завершения работы.

Сообщение ANDLL » 18.06.2005 (Сб) 10:31

Итак,

Задача

Надо
1) Удалить саму себя или любой другой файл после завершения работы программы.
2) Запретить вмеру воспитанному пользователю каким бы то не было способом воспрепятствовать удалению файла(ов)
Типичное решение
Написать bat-файл, который запускает нашу программу а потом удалаяет ее.
Полностью реализует пункт 1. И совершенно проваливает пункт 2: пользователю достаточно закрыть окно cmd, и подтвердить "закрытие зависшего приложения".

Пункт 2 под микроскопом

Итак, что может сделать пользователь, что бы восприпятствовать удалению фалйа:
1) Завершить процесс, который этот файл будет удалять.
2) Экстренно перезагрузить компьютер(скажем, нажатием на "reset")
Мы будем решать вопрос лишь 2.1. 2.2 нас не интересует, ибо тут все элементарно просто: пишем bat-файл, кидаем его в RunOnce, и все... Если только наш вмеру воспитанный пользователь не сообразит загрузить компьютер в безопасном режиме :)

О том, как мы будем решать пункт 2.1

Идея до смешного проста: мы находим некоторый системны процесс, который пользователю не придет в голову завершать(он ведь не знает метод нашей защиты). В нашем случае это будет explorer, который мы будем находить, как процесс окна Shell_TrayWnd. Кого этот способ не устраивает может модифицировать его по вкусу...
Далее мы записываем простенький код, аналог которого на VB выглядит так:
Код: Выделить всё
    Dim hProcess As Long
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, OurProcessId)
    WaitForSingleObject(hProcess, -1)
    CloseHandle hProcess
    DeleteFile OurFileName(в нашем случае это будет путь к нашему EXE-файлу)
    ExitThread 0

Причем записываем его в адресное пространство нашего системного процесса.
Затем этот код запускает(в отдельном потоке, естественно)
Как видите, методика до смешного проста.
Увы, как мы скором увидим, реализация этой методики далеко не так проста...

Наш код в удаленном процесcе(RemoteThread)

Ясное дело, что писать VB-код в другой процесс было бы чистым... В-общем мы так делать не будем.
Мы напишем альтернативу этому коду на ассемблере и сохраним его в файл RemoteThread.asm
Код: Выделить всё
BITS 32
jmp StartPoint

OpenProcess                 dd 0 ;функция OpenProcess
WaitForSingleObject         dd 0 ;функция WaitForSingleObject
CloseHandle                 dd 0 ;функция CloseHandle
DeleteFile                  dd 0 ;функция DeleteFile
ExitThread                  dd 0 ;функция ExitThread
ProcessId                   dd 0 ;идентификатор нашего процесса
fName                       dd 0,0,0, 0,0,0,0,0,0,0, 0,0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 ,0,0,0,0,0,0, 0,0,0 ;имя нашего файла(не должно быть более 255 символов(если теоретически оно будет больше, надо просто поменять эту константу)

CallPageBase: ;эта процедура определит базовый адрес страницы и поместит его в ebp
mov ebp,[esp]
sub ebp,SomePoint
ret

StartPoint: ;тут начинается наша программа

mov ecx,SomePoint
call CallPageBase
SomePoint:;сюда будет указывать [esp]

;Теперь в ebp хранится адрес начала нашего кода

push dword [ebp+ProcessId] ;|
push dword 0               ;|
push dword 01f0fffh        ;|==>OpenProcess(PROCESS_ALL_ACCESS,0,ProcessId); где PROCESS_ALL_ACCESS=0x1f0fff
mov edx,[ebp+OpenProcess]  ;|    Открываем наш процесс
call edx                   ;|
mov ebx,eax ;будем храниться описатель нашего процесса в ebx

;Теперь в ebx хранится hProcess нашей программы

push dword -1                     ;|
push ebx                          ;|==>WaitForSingleObject(ebx,-1);
mov edx,[ebp+WaitForSingleObject] ;|    Ждем, пока наш процесс угаснет
call edx                          ;|

push ebx                  ;|
mov edx,[ebp+CloseHandle] ;|==>CloseHandle(ebx)
call edx                  ;|

mov edx,ebp   ;|
add edx,fName ;|==>edx=fName

push edx                 ;|
mov edx,[ebp+DeleteFile] ;|==>DeleteFile(edx)
call edx                 ;|

push dword 0             ;|
mov edx,[ebp+ExitThread] ;|==>ExitThread(0)
call edx                 ;|
int 3 ;===|
int 3 ;===|
int 3 ;===|==>собственно, маленькая отладочная заглушка
int 3 ;===|
int 3 ;===|

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

Итак, теперь наша задача написать VB-шную функцию WaitToDeleteFile, которая будет засылать этот код в удаленный процесс.

WaitToDeleteFile
Функция принимает в качестве параметра Id процесса, в который надо "вживлятся", имя файла, который надо удалить и сам код, который будет удалять.
функцию можно вызывать сколько угодно раз, для разный файлов и процессов. Можно вживлятся несколько раз в один процесс. Можно удалять один файл сразу несколькими процессами(для верности).
Код: Выделить всё
Private Sub WaitToDeleteFile(ByVal RemoteProcessId As Long, ByVal fName As String, ByRef Code() As Byte)
   
    'Преобразуем имя файла из Unicode в ASCII
    Dim nStrFName As String
    nStrFName = StrConv(fName, vbFromUnicode)
    If LenB(nStrFName) > 255 Then Err.Raise 126, , "File path is too long": Exit Sub
    Dim hRemoteProcess As Long
   
    'Сохраняем в переменную начало нашего кода
    Dim nFirstByteAddr As Long
    nFirstByteAddr = VarPtr(Code(0))
   
    'Загружаем kernel32.dll(точнее, она уже загружена, так что просто получаем ее hInstance)
    Dim hKernelLib As Long, pProc As Long
    hKernelLib = LoadLibrary("kernel32.dll")
   
    'Тут слегка модифицируем код. Записываем в него имя нашего файла, Id нашего процесса и адреса нужных ему системных API-функций
    pProc = GetProcAddress(hKernelLib, "OpenProcess")
    Call CopyMemory(nFirstByteAddr + &H5, VarPtr(pProc), 4)
    pProc = GetProcAddress(hKernelLib, "WaitForSingleObject")
    Call CopyMemory(nFirstByteAddr + &H9, VarPtr(pProc), 4)
    pProc = GetProcAddress(hKernelLib, "CloseHandle")
    Call CopyMemory(nFirstByteAddr + &HD, VarPtr(pProc), 4)
    pProc = GetProcAddress(hKernelLib, "DeleteFileA")
    Call CopyMemory(nFirstByteAddr + &H11, VarPtr(pProc), 4)
    pProc = GetProcAddress(hKernelLib, "ExitThread")
    Call CopyMemory(nFirstByteAddr + &H15, VarPtr(pProc), 4)
    pProc = GetCurrentProcessId
    Call CopyMemory(nFirstByteAddr + &H19, VarPtr(pProc), 4)
    Call CopyMemory(nFirstByteAddr + &H1D, StrPtr(nStrFName), LenB(nStrFName))
   
    'Открываем "системный" процесс
    hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, RemoteProcessId)
   
    'Резервируем в нем страницу для нашего кода
    'Здесь есть один недостаток: эта страница так и останется в оперативной помяти, т.к. нет кода, который бы ее выгружал.
    'Итого здесь мы потеряем 4Кб оперативной памяти
    Dim hMem As Long
    hMem = VirtualAllocEx(hRemoteProcess, 0, UBound(Code) + 1, MEM_COMMIT, PAGE_READWRITE)
   
    'теперь копируем наш код в эту страницу и выставляем ей права чтения и выполнения(PAGE_EXECUTE_READ)
    Dim tmpLng As Long
    WriteProcessMemory hRemoteProcess, hMem, nFirstByteAddr, UBound(Code) + 1, tmpLng
    VirtualProtectEx hRemoteProcess, hMem, UBound(Code) + 1, PAGE_EXECUTE_READ, tmpLng
   
    'Запускаем наш код в отдельном потоке
    CreateRemoteThread hRemoteProcess, 0, 0, hMem, 0, 0, tmpLng
   
    'Закрываем "системный" процесс(в смысле его описатель...)
    CloseHandle hRemoteProcess
   
    'Закрываем описатель kernel32.dll
    FreeLibrary hKernelLib
End Sub


Заключение

Собственно все... Сам ассемблерный код можно хранить в отдельном файле или в ресурсах(как я это делаю).
Использованный компилятор ассемблера: nasmw. Спасибо его авторам.
Во вложении можете просмотреть работоспособный пример.

Если gaidar захочет, он может кинуть эту статью на vbstreets.
Особая благодарность tyomitch'у и GSerg'у за помощь в преобретении знаний, необходимых для написания всего этого.
Вложения
RemoteThread.zip
RemoteThread
(115.83 Кб) Скачиваний: 52
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

gaidar
System Debugger
System Debugger
 
Сообщения: 3152
Зарегистрирован: 23.12.2001 (Вс) 13:22

Сообщение gaidar » 18.06.2005 (Сб) 12:58

Сделаем, а почему бы не тсделать :)

ТОЛЬКО О ТАКОМ НАДО ПО E-MAIL УВЕДОМЛЯТЬ. ФОРУМ Я ИНОГДА НЕДЕЛЯМИ НЕ ЧИТАЮ.
The difficult I’ll do right now. The impossible will take a little while. (c) US engineers in WWII
I don't always know what I'm talking about, but I know I'm right. (c) Muhammad Ali

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 19.06.2005 (Вс) 15:13

Статью кидать поздно, она уже есть :)

http://www.catch22.net/tuts/selfdel.asp
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 19.06.2005 (Вс) 16:50

Будет еще один вариант :)
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

gaidar
System Debugger
System Debugger
 
Сообщения: 3152
Зарегистрирован: 23.12.2001 (Вс) 13:22

Сообщение gaidar » 19.06.2005 (Вс) 23:03

А мы уже опубликовали :)
The difficult I’ll do right now. The impossible will take a little while. (c) US engineers in WWII
I don't always know what I'm talking about, but I know I'm right. (c) Muhammad Ali

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Re: Как обязательно удалить самое себя после завершения рабо

Сообщение tyomitch » 23.06.2005 (Чт) 16:01

Мои 3¢:

ANDLL писал(а):Наш код в удаленном процесcе(RemoteThread)

Явно стоило бы отметить, что CreateRemoteThread есть только в WinNT. Стало быть, в Win98 такой метод фундаментально неприменим.

ANDLL писал(а):Ясное дело, что писать VB-код в другой процесс было бы чистым... В-общем мы так делать не будем.

А между тем, я уже выкладывал пример "вживления" VB-кода в другой процесс через глобальный хук. У него есть два существенных преимущества перед приведённым способом:
1) не нужно ассемблера
2) работает и в Win98

ANDLL писал(а):
Код: Выделить всё
CallPageBase: ;эта процедура определит базовый адрес страницы и поместит его в ebp
mov ebp,[esp]
sub ebp,SomePoint
ret

Зачем столь интеллектуально определять базовый адрес, когда его можно прописать в патч без особых усилий - так же, как прописываются адреса вызываемых функций и имя удаляемого файла?
Изображение

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 23.06.2005 (Чт) 18:29

1) ok
2) Совершенно не подходит: Dll удалит EXE-шник, а кто удалит Dll?
3) Возможно, но мой способ тоже ничем не хуже... Даже быстрее...
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 23.06.2005 (Чт) 19:12

2) если длл-ка не сверхсекретная, то её можно удалить при перезагрузке.
3) как это он быстрее?
как какой бы то ни было код м.б. быстрее чем отсутствие кода?
Изображение

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 23.06.2005 (Чт) 20:16

Как это "отсутствие" кода?
А адрес ты прописывать будешь? И используется для этого CopyMemory. Что по твоему быстрее, эти пять

команд, или вызов API-функции? Не забывай, что прописка адреса тоже занимает время, как и

"интеллектальное" вычисление адреса. И вызываются они тоже один раз.

Кстати, а не может оказаться, что эти адреса вообще во всех версиях Windows - одинаковые константы?
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 23.06.2005 (Чт) 20:34

1. Я делаю WriteProcessMemory сразу нужных значений в чужое АП, а не через промежуточный байтовый массив. Т.ч. у меня потеря на лишней записи в память меньше.
Ну, и учитывай конечно, что время на запись этих пяти команд тоже экономится, когда их нет :-)
2. Не может оказаться.


ЗЫ: чего-то у тебя с чувством юмора не то (это я про "сдатый" и "ширше").
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 26.06.2005 (Вс) 14:28

Да, вот ещё вспомнил: если вместо hKernelLib = LoadLibrary("kernel32.dll") написать hKernelLib = GetModuleHandle("kernel32.dll"), то библиотеку потом не надо освобождать: экономится один вызов API.
Изображение


Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3

    TopList