IPB

Добро пожаловать, гость ( Вход | Регистрация )

> Самоудаляющийся .EXE файл, Self-Deleting Executable
-=CHE@TER=-
Jun 23 2007, 10:13
Сообщение #1


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,355
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Вот, есть такая статья: Self-Deleting Executables.
Она рассказывает о нелёгких поисках способа удалить программно .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.

Однако, где-то я чего-то упустил, ибо программа запускается, но ничего не делает.
Кто-нибудь может помочь?
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
 
Reply to this topicStart new topic
Ответов
Xplorer
Jun 28 2007, 13:32
Сообщение #2


Advanced Member
***

Группа: CTPAX-X
Сообщений: 52
Регистрация: 4-February 08
Пользователь №: 8
Спасибо сказали: 30 раз(а)



Исправленный код:
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 (в памяти находится только один экземпляр кода).
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Jun 28 2007, 13:56
Сообщение #3


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,355
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



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.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post

Сообщения в этой теме
-=CHE@TER=-   Самоудаляющийся .EXE файл   Jun 23 2007, 10:13
-=CHE@TER=-   Немного поправил исходный код с move() и ещё пара ...   Jun 23 2007, 16:27
Siberian GRemlin   А не проще сделать генералицю .bat'ника с кома...   Jun 24 2007, 10:50
-=CHE@TER=-   А не проще сделать генералицю .bat'ника с кома...   Jun 24 2007, 13:43
-=CHE@TER=-   Гм, так и знал. EntryPoint от explorer.exe я получ...   Jun 25 2007, 12:30
Heim   Заменил local.fnWaitForSingleObject = (FAR...   Jun 28 2007, 11:29
-=CHE@TER=-   Heim! Неа, не работает. Мне нужны адреса функц...   Jun 28 2007, 11:45
Xplorer   Исправленный код: Procedure SelfDel; Assembler; As...   Jun 28 2007, 13:32
-=CHE@TER=-   Исправленный код: <cut> Адреса в kernel32.d...   Jun 28 2007, 13:56
nickolayer   Ох ты. Потрясающе. Я честно скажу, последние три с...   Jun 30 2007, 09:50
Axsis   Чё-то я не совсем понял принцип работы этого кода....   Jul 3 2007, 07:17
-=CHE@TER=-   Чё-то я не совсем понял принцип работы этого кода....   Jul 3 2007, 08:47
Axsis   Сразу со 2-го пункта: ругаться могут не столько ан...   Jul 4 2007, 17:16
-=CHE@TER=-   Сразу со 2-го пункта: ругаться могут не столько ан...   Jul 5 2007, 09:23
Axsis   Я не знаю ни одного трейнера который бы патчил ко...   Jul 5 2007, 15:38
-=CHE@TER=-   Я не знаю ни одного трейнера который бы патчил код...   Jul 5 2007, 17:00
Axsis   Хех, конечно я не вручную его проверял, есть тако...   Jul 5 2007, 20:25
-=CHE@TER=-   Хех, конечно я не вручную его проверял, есть такой...   Jul 5 2007, 21:35


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0 -

 



Упрощённая версия Сейчас: 23rd May 2024 - 12:34