今天懒得干活,把google到的Debug_for_Free.c(现在google到的链接好像连不过去了,可以用google快照看到)转换&改进了一下,或许有人用的到.

unit Unit1;

(********************************)
(*       Debug for Free         *)
(*     Works on Win2k ONLY!     *)
(*                              *)
(*Coverted from Debug_for_Free.c*)
(*                              *)
(*  Covert & Improved by tt.t   *)
(*                              *)
(*        Dec. 17, 2005         *)
(********************************)

(* 
   作用: Win2k下的DebugSetProcessKillOnExit(FALSE)
   原理: Win2k下调试器结束时,Smss.exe会用NtTerminateThread和NtTerminateProcess
         强行将被调试进程结束,只要nop掉相应代码就可取得和xp的DebugSetProcessKillOnExit(FALSE)
         一样的结果,即调试器结束,被调试程序继续运行.但这种方法并未将被调试程序的调
         试信息去掉, 因此调试器结束后,被调试程序无法被其他调试器附加.
   难点: 基本没有,只是找到Patch的位置比较麻烦,这里采用了查找硬编码的方法.
   注意: 只能用于Win2k系统!这种方法在Win2k + sp3, sp4中文系统上测试通过,
         但仍有可能会造成系统重启等后果,使用前注意!纯属无聊之做,出问题别找我.
   测试方法: 用任意ring3调试器载入任意程序,并运行.在任务管理器中结束调试器进程,
             如调试器关闭而被调试程序仍正常运行则说明成功.不可直接关闭调试器来测试,
             因为调试器正常退出时,可能会主动关闭被调试程序.
*)

interface

uses
  Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses TLHelp32, JwaWinNt, JwaWinBase, JwaWinType, JwaWinError;

function GetModuleFileNameExA(hProcess: THandle; hModule: HMODULE; lpFilename: PChar;
                              nSize: DWord): integer;stdcall;external 'PSAPI.DLL';
const
  Len_NtTerminateThread = 14; //code length
  HC_NtTerminateThread = $01000468;//HardCode: $68 04 00 01
  Len_NtTerminateProcess = 10; //code length
  HC_NtTerminateProcess = $2473FF57;//HardCode: $57 FF 73 24

var
  Ptr_NtTerminateThread, Ptr_NtTerminateProcess: Cardinal;
  //Patch的AV.
  //在我测试的机器上2个的AV是一样的,分别是$48584331和$485843BE.
  hSmss: Cardinal;
  OldProtect: Cardinal;
  NopBuf: Array [0..20] of Byte;   //Nop * 20
  NtTerminateThreadBuf: Array [0..Len_NtTerminateThread - 1] of Byte;
  //保存patch前的内容
  NtTerminateProcessBuf: Array [0..Len_NtTerminateProcess - 1] of Byte; 
  //保存patch前的内容
  i: Cardinal;

function EnabledDebugPrivilege(const bEnabled: Boolean):Boolean;  //提升权限
var
  hToken: THandle;
  tp: TOKEN_PRIVILEGES;
  a: DWORD;
const
  SE_DEBUG_NAME = 'SeDebugPrivilege';
begin
  Result:=False;
  if (OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, hToken)) then
  begin
    tp.PrivilegeCount :=1;
    LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid);
    if bEnabled then
      tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
    else
      tp.Privileges[0].Attributes := 0;
    a:=0;
    AdjustTokenPrivileges(hToken, False, @tp, SizeOf(tp), nil, @a);
    Result:= GetLastError = ERROR_SUCCESS;
    CloseHandle(hToken);
  end;
end;

function DoFix: Bool;   //Patch

  function Fix(Ptr: Pointer; var DesBuf: Array of Byte; Len: integer): Bool;
  var
    Buf: Array [0..20] of Byte;
  begin                
    result := true;
    result := result and ReadProcessMemory(hSmss, Ptr, @Buf[0], Len, @i);
    if Ord(Buf[0]) <> $90 then
    begin
      CopyMemory(@DesBuf[0], @Buf[0], Len);
      result := result and WriteProcessMemory(hSmss, Ptr, @NopBuf[0], Len, @i);
    end
    else
      result := false;
  end;

begin
  result := true;
  result := result and VirtualProtectEx(hSmss, Pointer(Ptr_NtTerminateThread), 128,
                                        PAGE_EXECUTE_READWRITE, @OldProtect);
  result := result and Fix(Pointer(Ptr_NtTerminateThread), NtTerminateThreadBuf,
                           Len_NtTerminateThread);
  result := result and Fix(Pointer(Ptr_NtTerminateProcess), NtTerminateProcessBuf,
                           Len_NtTerminateProcess);
  result := result and VirtualProtectEx(hSmss, Pointer(Ptr_NtTerminateThread), 128,
                                        OldProtect, @OldProtect);
end;

function UndoFix: Bool;

  function Fix(Ptr: Pointer; SrcBuf: Array of Byte; Len: integer): Bool;
  var
    Buf: Array [0..20] of Byte;
  begin                
    result := true;
    result := result and ReadProcessMemory(hSmss, Ptr, @Buf[0], Len, @i);
    if Ord(Buf[0]) = $90 then
    begin
      result := result and WriteProcessMemory(hSmss, Ptr, @SrcBuf[0], Len, @i);
    end
    else
      result := false;
  end;

begin             
  if (Ord(NtTerminateThreadBuf[0]) = 0) or (Ord(NtTerminateProcessBuf[0]) = 0) then
  begin
    result := false;
    exit;
  end;
  result := true;
  result := result and VirtualProtectEx(hSmss, Pointer(Ptr_NtTerminateThread), 256,
                                        PAGE_EXECUTE_READWRITE, @OldProtect);
  result := result and Fix(Pointer(Ptr_NtTerminateThread), NtTerminateThreadBuf,
                           Len_NtTerminateThread);
  result := result and Fix(Pointer(Ptr_NtTerminateProcess), NtTerminateProcessBuf,
                           Len_NtTerminateProcess);
  result := result and VirtualProtectEx(hSmss, Pointer(Ptr_NtTerminateThread), 256,
                                        OldProtect, @OldProtect);
end;

function Search4Hardcode: Bool;  //取得Patch位置的AV.
var
  TmpSmss: TMemoryStream;  
  PFileHeader: PImageFileHeader;
  POptionalHeader32: PImageOptionalHeader32;
  PSectionHeader: PImageSectionHeader;
  l: integer;
  SysDir: PChar;
  Ptr: PByte;
  f1, f2: Bool;
begin      
  result := false;
  l := GetSystemDirectory(nil, 0);
  GetMem(SysDir, l);
  GetSystemDirectory(SysDir, l);
  TmpSmss := TMemoryStream.Create;
  try
    TmpSmss.LoadFromFile(SysDir + '\SMSS.EXE');
    PFileHeader := PImageFileHeader(Longint(TmpSmss.Memory) +
                   PImageDosHeader(TmpSmss.Memory)^.e_lfanew + 4);
    POptionalHeader32 := PImageOptionalHeader32(DWORD(PFileHeader) +
                         IMAGE_SIZEOF_FILE_HEADER);
    PSectionHeader := PImageSectionHeader(DWORD(POptionalHeader32) +
                      PFileHeader^.SizeOfOptionalHeader);
    f1 := false;
    f2 := false;
    for i := 0 to PFileHeader^.NumberOfSections - 1  do  //遍历各个Section
    begin
      Ptr := PByte(Cardinal(TmpSmss.Memory) + PSectionHeader^.PointerToRawData);
      for i := 0 to PSectionHeader^.SizeOfRawData - 1 do
      //在Section中查找需Patch的Hardcode
      begin
        if Ptr^ = LoByte(LoWord(HC_NtTerminateThread)) then  //第一Byte相同?
        begin
          if PDWORD(Ptr)^ = HC_NtTerminateThread then        //整个DWORD相同?
          begin
            Ptr_NtTerminateThread := POptionalHeader32^.ImageBase +
                                     PSectionHeader^.VirtualAddress + i;
            //计算得到Patch位置的AV.
            //Smss.exe的基址一般就是其PE中指定的ImageBase.
            //所以内存中Smss.exe需要Patch的位置就其在PE文件中的AV.
            f1 := true;
          end;
        end
        else if Ptr^ = LoByte(LoWord(HC_NtTerminateProcess)) then
          if PDWORD(Ptr)^ = HC_NtTerminateProcess then
          begin
            Ptr_NtTerminateProcess := POptionalHeader32^.ImageBase +
                                      PSectionHeader^.VirtualAddress + i;
            f2 := true;
          end;
        if f1 and f2 then Break;   //2处Patch都已找到.
        inc(Ptr);
      end;   
      if f1 and f2 then
      begin
        result := true;
        Break;
      end;
      inc(PSectionHeader);
    end;
  finally
    TmpSmss.Free;
    FreeMem(SysDir);
  end;
end;

function FindSmss(var Handle: Cardinal): Bool;
//获取Smss.exe的ProcessId,并打开得到Process Handle.
const
  SmssPath = '\SYSTEMROOT\SYSTEM32\SMSS.EXE';
var
  lppe: TProcessEntry32;
  sHandle:cardinal;
  Found: bool;
  hp: cardinal;
  ExePath: PChar;
begin
  result := false;
  GetMem(ExePath, MAX_PATH);
  sHandle := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
  Found := Process32First(sHandle, lppe);
  while found do
  begin
    hp := OpenProcess(PROCESS_ALL_ACCESS, false, lppe.th32ProcessID);
    GetModuleFileNameExA(hp, 0, ExePath, MAX_PATH);   //取完全路径
    if UpperCase(ExePath) = SmssPath then
    begin
      Handle := hp;
      result := true;
      Break;
    end;
    Found := Process32Next(sHandle, lppe);
    CloseHandle(hp);
  end;
  CloseHandle(sHandle);
  FreeMem(ExePath);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  if GetVersion and 255 <> 5 then
  begin
    Showmessage('Works on Win2k Only!!');
    Halt;
  end;
  Search4Hardcode;                  //取得Patch位置的AV.
  EnabledDebugPrivilege(true);      //提升权限
  FillMemory(@NopBuf[0], 20, $90);
  ZeroMemory(@NtTerminateThreadBuf[0], Len_NtTerminateThread);
  ZeroMemory(@NtTerminateProcessBuf[0], Len_NtTerminateProcess);
  if not FindSmss(hSmss) then
  //获取Smss.exe的ProcessId,并打开得到Process Handle.
    showmessage('Smss.exe not found?!');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin            
  UndoFix;
  CloseHandle(hSmss);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not DoFix then      //Patch
    showmessage('Do fix failed!')
  else
    Label1.Caption := 'Patched!';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if not UndoFix then   //恢复
    showmessage('Do UndoFix failed!')
  else
    Label1.Caption := 'Not Patched!';
end;

end.
=========================================================================
object Form1: TForm1
  Left = 192
  Top = 107
  BorderIcons = [biSystemMenu, biMinimize]
  BorderStyle = bsSingle
  Caption = 'Form1'
  ClientHeight = 140
  ClientWidth = 239
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 8
    Top = 16
    Width = 151
    Height = 29
    Caption = 'Not Patched!'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -24
    Font.Name = 'MS Sans Serif'
    Font.Style = [fsBold]
    ParentFont = False
  end
  object Button1: TButton
    Left = 8
    Top = 56
    Width = 225
    Height = 41
    Caption = 'DoFix'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 8
    Top = 96
    Width = 225
    Height = 41
    Caption = 'UndoFix'
    TabOrder = 1
    OnClick = Button2Click
  end
end