Версия для печати темы
CTPAX-X _ Статьи _ Самоудаляющийся .EXE файл
Автор: -=CHE@TER=- Jun 23 2007, 10:13
Вот, есть такая статья: http://www.catch22.net/tuts/selfdel.asp.
Она рассказывает о нелёгких поисках способа удалить программно .EXE файл изнутри. Т.е. написать такую программу, которая сможет себя удалить, когда вы её запустите. Как известно в DOS такой проблемы не существует и я, в своё время, делал Uninstall'ер для своей программы, который удалял всё, включая и себя самого, ибо после запуска .EXE файл уже был не нужен. В Windows .EXE файл недоступен (занят системой) до тех пор, пока запущенная программа не прекратила работать, в частности удалить его тоже нельзя.
Из данной статьи я взял универсальный способ-пример, довольно оригинального решения, файл "selfdel05.c" и попытался переписать его на Delphi (оригинал можно взять по ссылке выше - см. Download source).
CODE
//
// selfdel.c
//
// Self deleting executable for Win9x/WinNT (all versions)
//
// J Brown 1/10/2003
//
// This source file must be compiled with /GZ turned OFF
// (basically, disable run-time stack checks)
//
// Under debug build this is always on (MSVC6)
//
//
Uses Windows;
// #pragma pack(push, 1)
// #define
Const CODESIZE = $200;
//
// Structure to inject into remote process. Contains
// function pointers and code to execute.
//
Type
PSELFDEL = ^TSELFDEL;
TSELFDEL = Packed Record
ARG0: PSELFDEL; // pointer to self
opCodes: Array[0..CODESIZE-1] Of Byte; // code
hParent: THandle; // parent process handle
fnWaitForSingleObject: Function(hHandle: THandle; dwMilliseconds: DWORD): DWORD; stdcall;
fnCloseHandle: Function(hObject: THandle): BOOL; stdcall;
fnDeleteFile: Function(lpFileName: PChar): BOOL; stdcall;
fnSleep: Procedure(dwMilliseconds: DWORD); stdcall;
fnExitProcess: Procedure(uExitCode: UINT); stdcall;
fnRemoveDirectory: Function(lpPathName: PChar): BOOL; stdcall;
fnGetLastError: Function: DWORD; stdcall;
fRemDir: LongBool;
szFileName: Array[0..MAX_PATH-1] Of Char; // file to delete
End;
//#pragma pack(pop)
{$ifdef _DEBUG}
//{$define FUNC_ADDR(func) (PVOID)(*(DWORD *)((BYTE *)func + 1) + (DWORD)((BYTE *)func + 5))}
{$else}
//{$define FUNC_ADDR(func) func}
{$endif}
//
// Routine to execute in remote process.
//
procedure remote_thread(remote: PSELFDEL); stdcall;
Begin
// wait for parent process to terminate
remote^.fnWaitForSingleObject(remote^.hParent, INFINITE);
remote^.fnCloseHandle(remote^.hParent);
// try to delete the executable file
while (not remote^.fnDeleteFile(remote^.szFileName)) Do
Begin
// failed - try again in one second's time
remote^.fnSleep(1000);
End;
// finished! exit so that we don't execute garbage code
remote^.fnExitProcess(0);
End;
//
// Delete currently running executable and exit
//
Function SelfDelete(fRemoveDirectory: LongBool): Boolean;
Var
si: STARTUPINFO;
pi: PROCESS_INFORMATION;
context: TContext;
oldProt: DWORD;
local: TSELFDEL;
entrypoint: DWORD;
szExe: Array[0..MAX_PATH-1] Of Char;
remdel: Pointer;
dummy: dword;
Begin
result:=FALSE;
szExe:='explorer.exe'#0;
//
// Create executable suspended
//
ZeroMemory(@si, SizeOf(si));
si.cb:=SizeOf(si);
if CreateProcess(Nil, szExe, Nil, Nil, False, CREATE_SUSPENDED Or IDLE_PRIORITY_CLASS, Nil, Nil, si, pi) Then
Begin
local.fnWaitForSingleObject := @WaitForSingleObject;
local.fnCloseHandle := @CloseHandle;
local.fnDeleteFile := @DeleteFile;
local.fnSleep := @Sleep;
local.fnExitProcess := @ExitProcess;
local.fnRemoveDirectory := @RemoveDirectory;
local.fnGetLastError := @GetLastError;
local.fRemDir := fRemoveDirectory;
// Give remote process a copy of our own process handle
DuplicateHandle(GetCurrentProcess, GetCurrentProcess, pi.hProcess, @local.hParent, 0, FALSE, 0);
GetModuleFileName(0, local.szFileName, MAX_PATH);
// copy in binary code
remdel:=@remote_thread;
move(remdel, local.opCodes, CODESIZE); //move(local.opCodes, @remdel, CODESIZE); {!}
//
// Allocate some space on process's stack and place
// our SELFDEL structure there. Then set the instruction pointer
// to this location and let the process resume
//
context.ContextFlags := CONTEXT_INTEGER Or CONTEXT_CONTROL;
GetThreadContext(pi.hThread, context);
// Allocate space on stack (aligned to cache-line boundary)
entrypoint := (context.Esp - sizeof(TSELFDEL)) And Cardinal(Not $1F);
//
// Place a pointer to the structure at the bottom-of-stack
// this pointer is located in such a way that it becomes
// the remote_thread's first argument!!
//
local.Arg0:=Ptr(entrypoint); // local.Arg0 := TSELFDEL(entrypoint);
context.Esp := entrypoint - 4; // create dummy return address
context.Eip := entrypoint + 4; // offset of opCodes within structure
// copy in our code+data at the exe's entry-point
VirtualProtectEx(pi.hProcess, Ptr(entrypoint), sizeof(local), PAGE_EXECUTE_READWRITE, Ptr(oldProt));
WriteProcessMemory(pi.hProcess, Ptr(entrypoint), Pointer(@local), sizeof(local), dummy);
FlushInstructionCache(pi.hProcess, Ptr(entrypoint), sizeof(local));
SetThreadContext(pi.hThread, context);
// Let the process continue
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
result:=TRUE;
End;
End;
Begin
SelfDelete(False);
End.
Однако, где-то я чего-то упустил, ибо программа запускается, но ничего не делает.
Кто-нибудь может помочь?
Автор: -=CHE@TER=- Jun 23 2007, 16:27
Немного поправил исходный код с move() и ещё пара ошибок зафиксено.
По прежнему ничего не делает, причём, похоже, remote_thread() вообще управление не передаётся.
Кто-нибудь может помочь?
Автор: Siberian GRemlin Jun 24 2007, 10:50
А не проще сделать генералицю .bat'ника с командой удаления .exe и его исполнение?
Автор: -=CHE@TER=- Jun 24 2007, 13:43
QUOTE(Siberian GRemlin @ Jun 24 2007, 10:50 AM)
А не проще сделать генералицю .bat'ника с командой удаления .exe и его исполнение?
Это не спортивно. (*улыбается*)
Хочется аккуратное решение.
Думаю, что тут скорее всего нужно будет сделать код на асме, там править адреса и уже его инжектировать в процесс "explorer.exe" (как только это делать - пока что х.з.). Потому что, как мне кажется, расколбас идёт из-за того, что код на Delphi занимает больше, чем на C++ (CODESIZE = $200;).
Добавлено:
Пытаюсь через Assembler работать - вот, пока что получилось адрес заменить:
CODE
Uses Windows;
{$APPTYPE CONSOLE}
Procedure SelfDel; Assembler;
Asm
jmp @code
@pExitProcess: dd 00
@code:
xor eax,eax
push eax
call Cardinal(@pExitProcess)
retn
End;
procedure DeleteSelf;
var
pExitProcess: pointer;
pt: PByte;
dummy: cardinal;
begin
pt:=@selfdel;
Inc(pt, 2);
pExitProcess:=@ExitProcess;
WriteProcessMemory(GetCurrentProcess, pt, @pExitProcess, 4, dummy);
end;
Begin
DeleteSelf;
SelfDel;
WriteLn('failed');
End.
Думаю по-позже с "explorer.exe" попробовать. Только за адреса вызова не ручаюсь... У меня сомнения есть + непонятно, как EntryPoint указывать. В общем, буду рад любой помощи.
Автор: -=CHE@TER=- Jun 25 2007, 12:30
Гм, так и знал. EntryPoint от explorer.exe я получил, но вот вживить приведённый выше код SelfDel туда не получается - адреса у API функций другие... Кто-нибудь знает, как получить адреса необходимых API функций у запущенного процесса? Что-нибудь типа GetProcAddress(hHandle, 'ExitProcess'); только в Suspended процессе?..
Автор: Heim Jun 28 2007, 11:29
Заменил
CODE
local.fnWaitForSingleObject = (FARPROC)WaitForSingleObject;
local.fnCloseHandle = (FARPROC)CloseHandle;
local.fnDeleteFile = (FARPROC)DeleteFile;
local.fnSleep = (FARPROC)Sleep;
local.fnExitProcess = (FARPROC)ExitProcess;
local.fnRemoveDirectory = (FARPROC)RemoveDirectory;
local.fnGetLastError = (FARPROC)GetLastError;
на:
CODE
HMODULE hMod = LoadLibrary(_T("Kernel32.dll"));
if( hMod )
{
local.fnWaitForSingleObject = GetProcAddress( hMod, "WaitForSingleObject" );//(FARPROC)WaitForSingleObject;
local.fnCloseHandle = GetProcAddress( hMod, "CloseHandle" );//(FARPROC)CloseHandle;
local.fnDeleteFile = GetProcAddress( hMod, "DeleteFileW" );//(FARPROC)DeleteFile;
local.fnSleep = GetProcAddress( hMod, "Sleep" );//(FARPROC)Sleep;
local.fnExitProcess = GetProcAddress( hMod, "ExitProcess" );//(FARPROC)ExitProcess;
local.fnRemoveDirectory = GetProcAddress( hMod, "RemoveDirectoryW" );//(FARPROC)RemoveDirectory;
local.fnGetLastError = GetProcAddress( hMod, "GetLastError" );//(FARPROC)GetLastError;
FreeLibrary( hMod );
}
Работает... Может тебе поможет????
Автор: -=CHE@TER=- Jun 28 2007, 11:45
Heim!
Неа, не работает. Мне нужны адреса функций именно из адресного пространства другого процесса.
Вот код, который я сделал:
CODE
Uses Windows;
{$APPTYPE CONSOLE}
Type
TInitStruct = Packed Record
pExitProcess: Pointer;
pWaitForSingleObject: Pointer;
pSleep: Pointer;
pDeleteFile: Pointer;
pCloseHandle: Pointer;
hParent: THandle;
szFileName: Array[0..MAX_PATH-1] Of Char;
End;
Procedure SelfDel; Assembler;
Asm
jmp @code
@pExitProcess: dd 00
@pWaitForSingleObject: dd 00
@pSleep: dd 00
@pDeleteFile: dd 00
@pCloseHandle: dd 00
@hParent: dd 00
// MAX_PATH = 260 { windows.pas }
@szFileName: db 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,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,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,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,0,0,0,0
nop
nop
nop
nop
@code:
// ******************************
// full injection code:
lea eax, @hParent
push eax
push INFINITE
call DWORD(@pWaitForSingleObject)
lea eax, @hParent
push eax
call DWORD(@pCloseHandle)
@delfile:
push 03E8h // 1 sec
call DWORD(@pSleep)
lea eax, @szFileName
push eax
call DWORD(@pDeleteFile)
test eax, eax
jz @delfile
xor eax,eax
push eax
call DWORD(@pExitProcess)
End;
Function GetCodeSize(P: PByte): Cardinal; Assembler;
Asm
mov ebx, eax
mov cl, 0C3h
@loop:
inc eax
cmp [eax], cl
jnz @loop
sub eax, ebx
inc eax
End;
{
used links:
http://www.codeproject.com/useritems/selfdel.asp
http://undocumented.ntinternals.net/
}
Function GetProcessEntryPointAddress(hProcess, hThread: THandle): Cardinal;
Var
context: TContext;
entry: LDT_ENTRY;
read, dwFSBase, dwImageBase, dwOffset, dwOptHeaderOffset: Cardinal;
dd: cardinal;
Begin
//
// get the current thread context
//
context.ContextFlags:=CONTEXT_FULL Or CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, context);
//
// use the segment register value to get a pointer to
// the TEB
//
GetThreadSelectorEntry(hThread, context.SegFs, entry);
dwFSBase:=(entry.BaseHi ShL 24) Or (entry.BaseMid ShL 16) Or (entry.BaseLow);
//
// read the teb
//
WriteLn(ReadProcessMemory(hProcess, Ptr(dwFSBase + 48), @dd, 4, read)); {1}
//
// read the peb from the location pointed at by the teb
//
WriteLn(ReadProcessMemory(hProcess, Ptr(dd + 8), @dwImageBase, 4, read)); {2}
//
// figure out where the entry point is located;
//
WriteLn(ReadProcessMemory(hProcess, Ptr(dwImageBase + $3C), @dwOffset, 4, read)); {3}
dwOptHeaderOffset:=(dwImageBase + dwOffset + 4 + 20);
WriteLn(ReadProcessMemory(hProcess, Ptr(dwOptHeaderOffset + 16), @dd, 4, read)); {4}
result:=dwImageBase + dd;
End;
Function DeleteSelf: Boolean;
Var
InitStruct: TInitStruct;
hKrnl32: HModule;
dummy: Cardinal;
St: String;
Pt: PByte;
{---}
si: STARTUPINFO;
pi: PROCESS_INFORMATION;
CodeSize, oldprot, entrypoint: Cardinal;
Begin
result:=False;
With InitStruct Do
Begin
hKrnl32:=GetModuleHandle('kernel32');
pExitProcess:=GetProcAddress(hKrnl32, 'ExitProcess');
pWaitForSingleObject:=GetProcAddress(hKrnl32, 'WaitForSingleObject');
pSleep:=GetProcAddress(hKrnl32, 'Sleep');
pDeleteFile:=GetProcAddress(hKrnl32, 'DeleteFileA');
pCloseHandle:=GetProcAddress(hKrnl32, 'CloseHandle');
FillChar(szFileName, MAX_PATH, 0);
St:=ParamStr(0);
Move(St[1], szFileName, Length(St));
hParent:=0;
End;
ZeroMemory(@si, SizeOf(si));
si.cb:=SizeOf(si);
If CreateProcess(Nil, PChar('explorer.exe'), Nil, Nil, False, CREATE_SUSPENDED Or IDLE_PRIORITY_CLASS, Nil, Nil, si, pi) Then
Begin
DuplicateHandle(GetCurrentProcess, GetCurrentProcess, pi.hProcess, @InitStruct.hParent, 0, FALSE, 0);
Pt:=@selfdel;
Inc(Pt, 5); // offset to structure
WriteLn(WriteProcessMemory(GetCurrentProcess, Pt, @InitStruct, SizeOf(InitStruct), dummy));
Dec(Pt, 5); // restore offset to program start
CodeSize:=GetCodeSize(Pt);
entrypoint:=GetProcessEntryPointAddress(pi.hProcess, pi.hThread);
oldprot:=0;
VirtualProtectEx(pi.hProcess, Ptr(entrypoint), CodeSize, PAGE_EXECUTE_READWRITE, oldProt);
WriteLn(WriteProcessMemory(pi.hProcess, Ptr(entrypoint), Pt, CodeSize, dummy));
FlushInstructionCache(pi.hProcess, Ptr(entrypoint), CodeSize);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
result:=True;
End;
End;
Begin
DeleteSelf;
WriteLn('ready');
End.
При запуске, если в код, сразу вставить C3, чтобы процесс завершил работу - то всё ок.
А вот адреса API функций - левые. Их нужно именно как-то получить из запущенного процесса.
Автор: Xplorer Jun 28 2007, 13:32
Исправленный код:
CODE
Procedure SelfDel; Assembler;
Asm
call @code
@pExitProcess: dd 0
@pWaitForSingleObject: dd 0
@pSleep: dd 0
@pDeleteFile: dd 0
@pCloseHandle: dd 0
@hParent: dd 0
// MAX_PATH = 260 { windows.pas }
@szFileName: db 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,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,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,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,0,0,0,0
nop
nop
nop
nop
@code:
// ******************************
// full injection code:
pop ebp
push INFINITE
mov ebx, TInitStruct(ebp).hParent
push ebx
call TInitStruct(ebp).pWaitForSingleObject
mov ebx, TInitStruct(ebp).hParent
push ebx
call TInitStruct(ebp).pCloseHandle
@delfile:
lea ebx, TInitStruct(ebp).szFileName
push ebx
call TInitStruct(ebp).pDeleteFile
or eax, eax
jne @exit
push 500
call TInitStruct(ebp).pSleep
jmp @delfile
@exit:
push 0
call TInitStruct(ebp).pExitProcess
End;
QUOTE(-=CHE@TER=- @ Jun 28 2007, 03:45 PM)
А вот адреса API функций - левые. Их нужно именно как-то получить из запущенного процесса.
Адреса в kernel32.dll для всех процессов одинаковые, т.к. это DLL (в памяти находится только один экземпляр кода).
Автор: -=CHE@TER=- Jun 28 2007, 13:56
QUOTE(Xplorer @ Jun 28 2007, 01:32 PM)
Исправленный код:
<cut>
Адреса в kernel32.dll для всех процессов одинаковые, т.к. это DLL (в памяти находится только один экземпляр кода).
О!!! Спасибо большое, ты гигант!!!
Я не знал про общий адрес, спасибо за справку и разъяснение!
Только я не понял пару моментов:
1) На что указывает ebp, когда ты сделал команду pop ebp (он чему вообще равен был?) Я так понял, что ты его из стэка вытащил, а кто туда его сложил и с каким значением?
2) call @code - почему именно call, а не jmp? Это как раз связано с ebp?
А! До меня, кажется, допёрло - ebp - это адрес возврата из call и он автоматически при вызове call ложится в стэк?.. (*улыбается*) Значит он должен как раз указывать на следующую инструкцию, после call - как раз на структуру. Тогда, и правда, простое и элегантное решение!
Это так? (*улыбается*)
Вот полный, причёсанный код (для тех, кто будет компилировать - нули справа от @szFileName: должны быть на ОДНОЙ встрочке!):
CODE
//
// DSelfDel.dpr
//
// Self deleting executable for Win9x/WinNT (all versions)
// URL: http://www.catch22.net/tuts/selfdel.asp
//
// Idea: J Brown 1/10/2003
// Delphi/ASM code by: -=CHE@TER=- / Xplorer
//
Program DSelfDel;
Uses Windows;
{
Few more ways to go:
1. Self-deleting .BAT file
2. Create temporary file using CreateFile() with FILE_FLAG_DELETE_ON_CLOSE flag.
URL: http://forum.vingrad.ru/act-ST/f-1/t-12088/unread-1/anchor-entry77762/0.html
}
Type
TInitStruct = Packed Record
pExitProcess: Pointer;
pWaitForSingleObject: Pointer;
pSleep: Pointer;
pDeleteFile: Pointer;
pCloseHandle: Pointer;
hParent: THandle;
szFileName: Array[0..MAX_PATH-1] Of Char;
End;
Procedure SelfDel; Assembler;
Asm
call @code
@pExitProcess: dd 0
@pWaitForSingleObject: dd 0
@pSleep: dd 0
@pDeleteFile: dd 0
@pCloseHandle: dd 0
@hParent: dd 0
// MAX_PATH = 260 { windows.pas }
@szFileName: db 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,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,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,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,0,0,0,0
@code:
// ******************************
// full injection code:
pop ebp
push INFINITE
mov ebx, TInitStruct(ebp).hParent
push ebx
call TInitStruct(ebp).pWaitForSingleObject
mov ebx, TInitStruct(ebp).hParent
push ebx
call TInitStruct(ebp).pCloseHandle
@delfile:
lea ebx, TInitStruct(ebp).szFileName
push ebx
call TInitStruct(ebp).pDeleteFile
or eax, eax
jne @exit
push 500
call TInitStruct(ebp).pSleep
jmp @delfile
@exit:
push 0
call TInitStruct(ebp).pExitProcess
End;
Function GetCodeSize(P: PByte): Cardinal; Assembler;
Asm
mov ebx, eax
mov cl, 0C3h
@loop:
inc eax
cmp [eax], cl
jnz @loop
sub eax, ebx
inc eax
End;
{
used links:
http://www.codeproject.com/useritems/selfdel.asp
http://undocumented.ntinternals.net/
}
Function GetProcessEntryPointAddress(hProcess, hThread: THandle): Cardinal;
Var
read, dwFSBase, dwImageBase, dwOffset, dwOptHeaderOffset, dd: Cardinal;
context: TContext;
entry: LDT_ENTRY;
Begin
//
// get the current thread context
//
context.ContextFlags:=CONTEXT_FULL Or CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, context);
//
// use the segment register value to get a pointer to
// the TEB
//
GetThreadSelectorEntry(hThread, context.SegFs, entry);
dwFSBase:=(entry.BaseHi ShL 24) Or (entry.BaseMid ShL 16) Or (entry.BaseLow);
//
// read the teb
//
ReadProcessMemory(hProcess, Ptr(dwFSBase + 48), @dd, 4, read); {1}
//
// read the peb from the location pointed at by the teb
//
ReadProcessMemory(hProcess, Ptr(dd + 8), @dwImageBase, 4, read); {2}
//
// figure out where the entry point is located;
//
ReadProcessMemory(hProcess, Ptr(dwImageBase + $3C), @dwOffset, 4, read); {3}
dwOptHeaderOffset:=(dwImageBase + dwOffset + 4 + 20);
ReadProcessMemory(hProcess, Ptr(dwOptHeaderOffset + 16), @dd, 4, read); {4}
result:=dwImageBase + dd;
End;
Function DeleteSelf: Boolean;
Var
CodeSize, EntryPoint, dummy: Cardinal;
InitStruct: TInitStruct;
hKrnl32: HModule;
St: String;
Pt: PByte;
si: STARTUPINFO;
pi: PROCESS_INFORMATION;
Begin
result:=False;
ZeroMemory(@si, SizeOf(si));
si.cb:=SizeOf(si);
If CreateProcess(Nil, PChar('explorer.exe'), Nil, Nil, False, CREATE_SUSPENDED Or IDLE_PRIORITY_CLASS, Nil, Nil, si, pi) Then
Begin
With InitStruct Do
Begin
DuplicateHandle(GetCurrentProcess, GetCurrentProcess, pi.hProcess, @hParent, 0, FALSE, 0);
hKrnl32:=GetModuleHandle('kernel32');
pExitProcess:=GetProcAddress(hKrnl32, 'ExitProcess');
pWaitForSingleObject:=GetProcAddress(hKrnl32, 'WaitForSingleObject');
pSleep:=GetProcAddress(hKrnl32, 'Sleep');
pDeleteFile:=GetProcAddress(hKrnl32, 'DeleteFileA');
pCloseHandle:=GetProcAddress(hKrnl32, 'CloseHandle');
FillChar(szFileName, MAX_PATH, 0);
St:=ParamStr(0);
Move(St[1], szFileName, Length(St));
End;
Pt:=@SelfDel;
Inc(Pt, 5); // offset to structure
WriteProcessMemory(GetCurrentProcess, Pt, @InitStruct, SizeOf(InitStruct), dummy);
Dec(Pt, 5); // restore offset to program start
CodeSize:=GetCodeSize(Pt);
EntryPoint:=GetProcessEntryPointAddress(pi.hProcess, pi.hThread);
VirtualProtectEx(pi.hProcess, Ptr(entrypoint), CodeSize, PAGE_EXECUTE_READWRITE, dummy);
WriteProcessMemory(pi.hProcess, Ptr(entrypoint), Pt, CodeSize, dummy);
FlushInstructionCache(pi.hProcess, Ptr(entrypoint), CodeSize);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
result:=True;
End;
End;
Begin
DeleteSelf;
End.
Автор: nickolayer Jun 30 2007, 09:50
Ох ты. Потрясающе. Я честно скажу, последние три строчки поражают своей простотой. А все остальное просто поражает.
Автор: Axsis Jul 3 2007, 07:17
Чё-то я не совсем понял принцип работы этого кода... Вы создаёте процесс explorer.exe, усыпляете его и копируете в него свой код? =( ) Если так, то мне интересно, как отнесутся антивирусы и всякие системы проактивной защиты к такому действию? Представьте, что человек слил прогу с инета, запустил, а антивирь написал, что так и так, предотвращена попытка вклиниться в аднесное пространство explorer.exe! А если после этого ещё и оригинальный exe'шник удалится... Да чел в жизни отсюда ничего больше не скачает! Если я всё провильно понял, то подобный код допустим только для личного использования, в публичные проги его пихать нельзя! Или, может, я что-то пропустил и -=CHE@TER=- переключился на написание троянов? =)
ЗЫ: если я неправильно понял, плиз напишите принцип работы ЭТОГО
Автор: -=CHE@TER=- Jul 3 2007, 08:47
QUOTE(Axsis @ Jul 3 2007, 07:17 AM)
Чё-то я не совсем понял принцип работы этого кода... Вы создаёте процесс explorer.exe, усыпляете его и копируете в него свой код? =( ) Если так, то мне интересно, как отнесутся антивирусы и всякие системы проактивной защиты к такому действию? Представьте, что человек слил прогу с инета, запустил, а антивирь написал, что так и так, предотвращена попытка вклиниться в аднесное пространство explorer.exe! А если после этого ещё и оригинальный exe'шник удалится... Да чел в жизни отсюда ничего больше не скачает! Если я всё провильно понял, то подобный код допустим только для личного использования, в публичные проги его пихать нельзя! Или, может, я что-то пропустил и -=CHE@TER=- переключился на написание троянов? =)
ЗЫ: если я неправильно понял, плиз напишите принцип работы ЭТОГО
Ок, давайте по порядку:
1) Вопрос первый:
QUOTE
Вы создаёте процесс explorer.exe, усыпляете его и копируете в него свой код? =( )
Совершенно точно (поправочка: Explorer.exe создаётся усыплённым). Находится EntryPoint, откуда Explorer.exe начинает работать (стартовая точка) и в это место всовывается код.
2) Вопрос второй:
QUOTE
Если так, то мне интересно, как отнесутся антивирусы и всякие системы проактивной защиты к такому действию?
Насчёт антивирусов - не знаю, у меня стоит Symantec с последними базами - он к этому спокойно относится. (*улыбается*) Если у тебя другой - можешь у себя скомпилировать Си или Delphi вариант и проверить. (*улыбается*) Сообщи, пожалуйста, о результатах, тоже интересно.
Кстати, вот что ещё сейчас в голову пришло: получается, что все трейнеры и прочие ArtMoney - это тоже вирусы, т.к. они тоже пишут в адресное пространство другого процесса. (*улыбается*)
3) Ну и насчёт третьего пункта приводить цитату целиком не буду - а просто отвечу:
мне данный код нужен был для апдейта моей программы. Вместо DeleteFileA() я использую MoveFileExA(), т.е. конечный у меня алгоритм такой:
1) Человек запускает прогу
2) Прога долбится искать обновление
3) Если обновление есть, то оно скачивает во временный каталог
4) Создаётся усыплённый Explorer.exe, и в него вклинивается код с двумя путями: ParamStr(0) (имя текущей программы) и путь до временного файла в темпе (назовём его TEMPFileName)
5) Текущий процесс завершает свою работу, размораживая Explorer.exe
6) Внедрённый код в Explorer.exe делает следующее:
- ждёт, пока завершит работу родительский процесс
- затем, через MoveFileExA() перемещает TEMPFileName заместо ParamStr(0) (на его место)
- снова запускает ParamStr(0) (уже проапдейтеную версию)
- и завершает работу
Т.е. в итоге программа обновилась, а ты даже этого не заметил - работаешь, как работал. (*улыбается*)
Скажу сразу - вирусы, трояны и прочую хрень никогда не писал и не собираюсь.
Просто мне нравится элегантное и красивое решение, когда не создаёт 200 файлов, один из которых обновляет первый, третий - четвёртый и т.д. Хочу, чтобы моя программа была в одном файле.
Вариант совать в ресур .EXE Updater'а, и создавать его в TEMP'е с флагом FILE_FLAG_DELETE_ON_CLOSE у CreateFile() мне тоже не очень нравится, потому что .EXE файл будет так или иначе больше жрать, чем приведённый выше код.
P.S. Пишу вполне легальную программу. На данный момент ей пользуется ~ 30 человек. (*улыбается*)
Автор: Axsis Jul 4 2007, 17:16
Сразу со 2-го пункта: ругаться могут не столько антивири, сколько системы проактивной защиты, типа тех что в Outpost Firewall и Kaspersky Internet Security. ArtMoney и прочие трейнеры не пишут в область кода другого процесса, поэтому тут надо будет проверять работу с такими прогами. Дома смогу проверить работоспособность с Аутпостом, тока дельфя не установлена - выкладывай откомпиленный
3. Для обновления можно юзать ещё такой алгоритм:
- при запуске прога проверяет обновление
- скачивает его и сохраняет во временный файл с произвольным именем
- запускает временный файл на исполнение (можно с параметром, указывающим что это первый запуск новой версии)
- оригинал завершает работу
- временный файл после проверки что оригинал завершился переименовывает себя в нормальное имя (на сколько я знаю, переименование запущенных файлов в винде допускается) и продолжает нормально выполняться
Если прога ходит за обновлениями в инет, то надо особенно тщательно проверить работоспособность этого алгоритма с разными системами защиты (антивирями, проактивками), потому что если сначала будет предупреждение о модификации explorer.exe, а потом ещё и в инет прога полезет, то опытного пользователя будет очень нелегко убедить что она за обновлением полезла ))
ЗЫ: про вирусы-трояны я конечно пошутил, но среди этих зверьков попадаются настоящие произведения программерского искусства, которые писались явно не дураками =)
Автор: -=CHE@TER=- Jul 5 2007, 09:23
QUOTE(Axsis @ Jul 4 2007, 05:16 PM)
Сразу со 2-го пункта: ругаться могут не столько антивири, сколько системы проактивной защиты, типа тех что в Outpost Firewall и Kaspersky Internet Security. ArtMoney и прочие трейнеры
не пишут в область кода другого процесса, поэтому тут надо будет проверять работу с такими прогами. Дома смогу проверить работоспособность с Аутпостом, тока дельфя не установлена - выкладывай откомпиленный
Насчёт области кода и области данных... х.з. Есть же трейнеры, которые память патчат, чтобы жизнь вообще не отнималась (*улыбается*).
QUOTE
- временный файл после проверки что оригинал завершился переименовывает себя в нормальное имя (на сколько я знаю, переименование запущенных файлов в винде допускается) и продолжает нормально выполняться
Тоже, в принципе, "с боку припёка". (*улыбается*)
QUOTE
Если прога ходит за обновлениями в инет, то надо особенно тщательно проверить работоспособность этого алгоритма с разными системами защиты (антивирями, проактивками), потому что если сначала будет предупреждение о модификации explorer.exe, а потом ещё и в инет прога полезет, то опытного пользователя будет очень нелегко убедить что она за обновлением полезла
))
Не так - сначала ищется обновление, затем скачивается. И только потом Explorer.exe модифицируется. Нет обновления - нет запуска Explorer.exe и модификации.
QUOTE
ЗЫ: про вирусы-трояны я конечно пошутил, но среди этих зверьков попадаются настоящие произведения программерского искусства, которые писались явно не дураками =)
Я не говорил, что они дураки. Однако, назвать их "умными" было бы тоже неправильно.
Есть люди, которые просто ездят в автобусах, а есть такие, которые при этом ещё лазят по чужим карманам. Раз ты так не хочешь делать - значит они "не дураки", а ты дурак?..
ИМХО, вирусы, трояны и прочую лабудень может и пишут "не дураки", но уж точно не от большого ума.
Добавлено:
Блин, торможу - вот, лови. (*улыбается*)
http://www.ctpax-x.org/uploads/dselfdel.zip
Автор: Axsis Jul 5 2007, 15:38
QUOTE
Насчёт области кода и области данных... х.з. Есть же трейнеры, которые память патчат, чтобы жизнь вообще не отнималась (*улыбается*).
Я не знаю ни одного трейнера который бы патчил код игры, там принцип такой, что каждые несколько милисекунд трейнер восстанавливает значение в памяти на прежнее, то есть жизни отнимаются, но через несколько мс восстанавливаются, для игрока это незаметно просто
Вот ещё отчёт с ВирусТотала для твоего файла:
CODE
Complete scanning result of "DSelfDel.exe", received in VirusTotal at 07.05.2007, 16:29:41 (CET).
Antivirus Version Update Result
AhnLab-V3 2007.7.5.0 07.05.2007 no virus found
AntiVir 7.4.0.37 07.05.2007 HEUR/Malware <<<<<<<<<<<<<<<
Authentium 4.93.8 07.04.2007 no virus found
Avast 4.7.997.0 07.04.2007 no virus found
AVG 7.5.0.476 07.04.2007 no virus found
BitDefender 7.2 07.05.2007 no virus found
CAT-QuickHeal 9.00 07.05.2007 no virus found
ClamAV devel-20070416 07.05.2007 no virus found
DrWeb 4.33 07.05.2007 no virus found
eSafe 7.0.15.0 07.05.2007 suspicious Trojan/Worm <<<<<<<<<<<<<<<
eTrust-Vet 30.8.3765 07.05.2007 no virus found
Ewido 4.0 07.05.2007 no virus found
FileAdvisor 1 07.05.2007 no virus found
Fortinet 2.91.0.0 07.05.2007 no virus found
F-Prot 4.3.2.48 07.04.2007 no virus found
F-Secure 6.70.13260.0 07.05.2007 Possibly malicious <<<<<<<<<<<<<<<
Ikarus T3.1.1.8 07.05.2007 no virus found
Kaspersky 4.0.2.24 07.05.2007 no virus found
McAfee 5067 07.04.2007 no virus found
Microsoft 1.2701 07.05.2007 no virus found
NOD32v2 2379 07.04.2007 no virus found
Norman 5.80.02 07.04.2007 no virus found
Panda 9.0.0.4 07.05.2007 no virus found
Sophos 4.19.0 06.24.2007 no virus found
Sunbelt 2.2.907.0 07.04.2007 no virus found
Symantec 10 07.05.2007 no virus found
TheHacker 6.1.6.142 07.04.2007 no virus found
VBA32 3.12.0.2 07.05.2007 no virus found
VirusBuster 4.3.23:9 07.05.2007 no virus found
Webwasher-Gateway 6.0.1 07.05.2007 Heuristic.Malware <<<<<<<<<<<<<<<
Aditional Information
File size: 9728 bytes
MD5: 20a3c83a74dd2aeb9f64bfe4894998c0
SHA1: 416ef725c4a8837e3a19c5e74bba2b46318dca5d
packers: UPX
Как видишь, эвристические движки 4 антивирей сочли этот код потенциально опасным. Да, это редкие (очень редкие
) антивирусы, но тем не менее...
Дома ещё с аутпостом проверю, глянем на его реакцию. Ещё бы с KIS проверить, тока нету его у меня
QUOTE
Тоже, в принципе, "с боку припёка". (*улыбается*)
На самом деле отличие от твоей версии лишь в том что файл заменяет себя сам а не из внедрённого кода, а в остальном... тот же временный файл, та же прозрачность для пользователя, та же реализация в виде 1-го exe...
QUOTE
Не так - сначала ищется обновление, затем скачивается. И только потом Explorer.exe модифицируется. Нет обновления - нет запуска Explorer.exe и модификации.
Ну так всё-таки лучше
Автор: -=CHE@TER=- Jul 5 2007, 17:00
QUOTE(Axsis @ Jul 5 2007, 03:38 PM)
Я не знаю ни одного трейнера который бы патчил код игры, там принцип такой, что каждые несколько милисекунд трейнер восстанавливает значение в памяти на прежнее, то есть жизни отнимаются, но через несколько мс восстанавливаются, для игрока это незаметно просто
Просто есть моменты, когда игра резко обнуляет жизнь и тяжко успеть восстановить значение до того, как придёт надпись "Game Over".
QUOTE
Вот ещё отчёт с ВирусТотала для твоего файла:
Ого! Моё почтение! Ты, наверное, тестированием антивирусных систем занимаешься? (*улыбается*) Спасибо тебе большое за отчёт! На самом деле были у меня подозрения, но ты их развеял. (*улыбается*) Спасибо! И правда, антивирусов, которые считают программу трояном - мало. И к тому же они все (ИМХО) аутсайдеры - т.е. их мало кто использует.
QUOTE
Как видишь, эвристические движки 4 антивирей сочли этот код потенциально опасным. Да, это редкие (очень редкие
) антивирусы, но тем не менее...
Понимаю, но мне очень хочется, чтобы всё было "красиво". (*улыбается*)
QUOTE
Дома ещё с аутпостом проверю, глянем на его реакцию. Ещё бы с KIS проверить, тока нету его у меня
Я даже слов таких не знаю. (*улыбается*) А KIS - это что?
QUOTE
На самом деле отличие от твоей версии лишь в том что файл заменяет себя сам а не из внедрённого кода, а в остальном... тот же временный файл, та же прозрачность для пользователя, та же реализация в виде 1-го exe...
Понимаешь, с ключом-параметром, ты даёшь (теоретически) возможность пользователю самому этот ключ использовать. Он может например даже другим пользователям какую-нибудь гадость сделать. Например, запустить с этим ключом программу так, что она себя чем-нибудь другим заменит. ИМХО, чем меньше прав у пользователя - тем лучше. Он
может делать всё, что
не запрещено. А если есть что-то, что ему нельзя делать, но он это может - то это уже плохо. Программа должна быть кондовой: пользователь может хоть головой об компьютер долбиться, но она должна работать как надо. (*улыбается*)
Автор: Axsis Jul 5 2007, 20:25
QUOTE
Ого! Моё почтение! Ты, наверное, тестированием антивирусных систем занимаешься? (*улыбается*) Спасибо тебе большое за отчёт! На самом деле были у меня подозрения, но ты их развеял. (*улыбается*) Спасибо! И правда, антивирусов, которые считают программу трояном - мало. И к тому же они все (ИМХО) аутсайдеры - т.е. их мало кто использует.
Хех, конечно я не вручную его проверял, есть такой замечательный сайтик http://www.virustotal.com/ на котором можно через форму загрузить свой файл и он проверится всеми этими антивирусами, всё это разумеется халявное, но вот в зависимости от загруженности сервера придётся подождать от 1 до 30 минут в очереди (+ 1-2 минуты на проверку) Поэтому когда нужно что-либо быстро проверить обхожусь онлайн-сканером каспера - http://www.kaspersky.ru/scanforvirus/ . Я думал ты слышал о вирустотал'е
QUOTE
QUOTE
Дома ещё с аутпостом проверю, глянем на его реакцию. Ещё бы с KIS проверить, тока нету его у меня
Я даже слов таких не знаю. (*улыбается*) А KIS - это что?
Аутпост - это Agnitum Outpost Firewall - фаервол, как и следует из названия
KIS - это Kaspersky Internet Security, в который входят Kasp. AntiVirus и Kasp. AntiHacker (последнее - тоже фаервол
)
Так вот у обеих прог с некоторых пор есть проактивная защита, которая отслеживает всякие действия, которые в основном используются вредоносным ПО, такие как внедрение в адресное пространство другого процеса, установка перехватчиков нажатий клавиш, модификация критических ключей реестра и т.д.
Результатом работы этой защиты у аутпоста является вот такое окошко (как я и предполагал) :
http://img178.imagevenue.com/img.php?image=63780_SelfDel_122_40lo.jpg
Если нажать "Allow Once" то файл удаляется, а если "Block Once" то память не модифицируется и просто запускается проводник, то есть оригинальный explorer.exe
Даю 99% что КИС отреагирует точно так.
QUOTE
Понимаешь, с ключом-параметром, ты даёшь (теоретически) возможность пользователю самому этот ключ использовать. Он может например даже другим пользователям какую-нибудь гадость сделать.
Помню ты упоминал что работал бета-тестером
Про параметр можно нигде не упоминать пользователи не будут даже знать о нем (кому надо тот конечно найдёт). А можно и вовсе обойтись без передачи параметра - например сделать проверку на имя exe-шника, например так (допустим у нас будет prog.exe):
- запускаем оригинал prog.exe (или prog.tmp)
- prog.exe (prog.tmp) проверяет своё расширение
если exe:
- проверка и загрузка обновления в prog.tmp, если обновления нет - продолжаем нормально работать
- prog.exe запускает prog.tmp а сам завершается
иначе:
- ждём завершения prog.exe и переименовываем себя (prog.tmp) в prog.exe
надеюсь ты поймёшь что я имел ввиду
если нет - напишу в виде норм. алгоритма а не такого бреда
вариантов решения для конкретно этого случая (когда надо не удалить а обновить) много и ИМХО код без внедрения будет безопаснее, ведь в твоём случае, код, записываемый в explorer, "злодеи" тоже могут подправить
и кстати, а будет ли это работать под ограниченной учётной записью? или тока с правами админа?
Автор: -=CHE@TER=- Jul 5 2007, 21:35
QUOTE(Axsis @ Jul 5 2007, 08:25 PM)
Хех, конечно я не вручную его проверял, есть такой замечательный сайтик http://www.virustotal.com/ на котором можно через форму загрузить свой файл и он проверится всеми этими антивирусами, всё это разумеется халявное, но вот в зависимости от загруженности сервера придётся подождать от 1 до 30 минут в очереди (+ 1-2 минуты на проверку) Поэтому когда нужно что-либо быстро проверить обхожусь онлайн-сканером каспера - http://www.kaspersky.ru/scanforvirus/ . Я думал ты слышал о вирустотал'е
Теперь - слышал. Ещё раз спасибо! (*улыбается*)
QUOTE(Axsis @ Jul 5 2007, 08:25 PM)
KIS - это Kaspersky Internet Security, в который входят Kasp. AntiVirus и Kasp. AntiHacker (последнее - тоже фаервол
)
Аутпост-то я знаю. KIS первый раз слышу. Хотя предпологал, что "K" - от кашпировского.
QUOTE(Axsis @ Jul 5 2007, 08:25 PM)
Результатом работы этой защиты у аутпоста является вот такое окошко (как я и предполагал) :
Если нажать "Allow Once" то файл удаляется, а если "Block Once" то память не модифицируется и просто запускается проводник, то есть оригинальный explorer.exe
Даю 99% что КИС отреагирует точно так.
А-а-а! Засада. (*улыбается*) Заблочили...
QUOTE(Axsis @ Jul 5 2007, 08:25 PM)
Помню ты упоминал что работал бета-тестером
(*слава богу я там больше не работаю*)
QUOTE(Axsis @ Jul 5 2007, 08:25 PM)
- ждём завершения prog.exe и переименовываем себя (prog.tmp) в prog.exe
У меня было сильно сомнение, но я всё-таки переборол свою лень и проверил твоё предложение из предыдущего поста - действительно, оказывается, во время работы .EXE файл можно переименовывать. Кхех - весело. (*улыбается*) Думаю, что это решает тогда все мои проблемы без внедрения кода. Спасибо за дельный совет (хотя какая идея и реализация были красивые... (*улыбается*))!