在DLL程序中使用Process32First / Next [英] Using Process32First/Next inside DLL procedure

查看:103
本文介绍了在DLL程序中使用Process32First / Next的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下过程:

 程序MyMainThread.MapProc; 
var
句柄:THandle;
PID:dword;
Struct:TProcessEntry32;
进程:TStringList;
begin
句柄:= CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS,0);
Struct.dwSize:= Sizeof(TProcessEntry32);
Process32First(Handle,Struct);
进程:= TStringList.Create;
repeat
Processes.Add(Struct.szExeFile);
Processes.SaveToFile('C:\Log.txt');
PID:= Struct.th32ProcessID;
PIDHandle:= OpenProcess(PROCESS_QUERY_INFORMATION或PROCESS_VM_OPERATION或
PROCESS_VM_READ,false,PID);
CloseHandle(PIDHandle);
until(not Process32Next(Handle,Struct));
Processes.Free;
结束

如您所见,我将运行的进程保存在C:\Log.txt中,在.exe文件内部工作很好。现在我试图在一个.DLL文件中实现这一点,其概念是:DLL将被加载,它将有一个EntryPoint调用Thread.Create ...该Thread将使用SetTimer来运行过程MapProc每10秒将运行的进程保存在C:\Log.txt中。代码是:

 库Project1; 

使用
Windows,
SysUtils,
类,
注册表,
EncdDecd,
TLHelp32,
IdHTTP ;

{$ R * .res}
type
MyMainThread = Class(TThread)
var
DestDir,ContactHost:String;
发送:TStringList;
PIDHandle:THandle; //需要公开,因为我们在MapProc / CatchYa中使用
private
procedure MapProc;
程序MapMemory(ProcessName:string);
程序CreateMessagePump;
protected
构造函数创建;
程序执行;覆盖;
结束

构造函数MyMainThread.Create;
begin
继承Create(false);
FreeOnTerminate:= true;
优先级:= tpNormal;
结束

程序MyMainThread.Execute;
begin
而不是终止do
begin
SetTimer(0,0,10000,@ MyMainThread.MapProc); //设置定时器10秒调用MapProc
CreateMessagePump; //我们在DLL里面,所以我想我们需要Message Pump来​​定时工作
终止;
结束
结束


程序MyMainThread.MapProc;
var
句柄:THandle;
PID:dword;
Struct:TProcessEntry32;
进程:TStringList;
begin
句柄:= CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS,0);
Struct.dwSize:= Sizeof(TProcessEntry32);
Process32First(Handle,Struct);
过程:= TStringList.Create;
repeat
Processes.Add(Struct.szExeFile);
Processes.SaveToFile('C:\Log.txt');
PID:= Struct.th32ProcessID;
PIDHandle:= OpenProcess(PROCESS_QUERY_INFORMATION或PROCESS_VM_OPERATION或
PROCESS_VM_READ,false,PID);
如果POS(Struct.szExeFile,ExeName)= 0然后
MapMemory(Struct.szExeFile); //程序调用进行验证,但是甚至没有调用
CloseHandle(PIDHandle);
until(not Process32Next(Handle,Struct));
Processes.Free;
结束


程序MyMainThread.CreateMessagePump;
var
AppMsg:TMsg;
begin
而GetMessage(AppMsg,0,0,0)do
begin
TranslateMessage(AppMsg);
DispatchMessage(AppMsg);
结束
//如果需要退出此过程,请使用PostQuitMessage(0);
结束


程序EntryPoint(原因:整数);
begin
如果Reason = DLL_PROCESS_ATTACH然后
begin
MyMainThread.Create;
end
else
如果Reason = DLL_PROCESS_DETACH然后
begin
MessageBox(0,'DLL De-Injected','DLL De-Injected',0);
结束
结束

begin
DLLProc:= @EntryPoint;
EntryPoint(DLL_PROCESS_ATTACH);
结束。

但是当运行这个时,我在Log.txt文件中只有行:[System Process]



EXE托管DLL是:

  unit Unit1; 

接口

使用
Windows,消息,SysUtils,变体,类,图形,控件,窗体,
对话框,StdCtrls;

type
TForm1 = class(TForm)
Button1:TButton;
procedure Button1Click(Sender:TObject);
private
{私人声明}
public
{公开声明}
end;

var
Form1:TForm1;


执行

{$ R * .dfm}



程序TForm1.Button1Click(发件人:TObject);
var
HD:THandle;
begin
HD:= LoadLibrary('C:\Project1.dll');
结束

结束。


解决方案

你的代码失败的原因是你不为 SetTimer 函数使用适当的回调。根据文档应该有一个签名,如

  procedure(hwnd:HWND; uMsg:UINT; idEvent:UINT_PTR; dwTime:DWORD); STDCALL; 

您的不兼容回调 - 这是一个类方法 - 导致代码认为 Self 生活在一个完全任意的内存地址,因为类方法有一个隐式的Self参数,但是winapi没有这方面的知识。现在当代码尝试写入一个无效的地址 - PIDHandle时,假设应该有一个类字段,AV被提出,并且由于该异常不被处理,其余的代码不被执行 - 同样如David的答案。



您的解决方案是使用适当的回调。要访问类成员,可以使用全局变量。不使用全局变量将需要一些黑客代码(Google for MethodToProcedure fi)



样本可能如下所示:

  threadvar 
MyThread:MyMainThread;

procedure TimerProc(hwnd:HWND; uMsg:UINT; idEvent:UINT_PTR; dwTime:DWORD);
stdcall;
var
句柄:THandle;
PID:dword;
Struct:TProcessEntry32;
进程:TStringList;
begin
句柄:= CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS,0);
Struct.dwSize:= Sizeof(TProcessEntry32);
Process32First(Handle,Struct);
进程:= TStringList.Create;
repeat
Processes.Add(Struct.szExeFile);
Processes.SaveToFile('C:\Temp\Log3.txt');
PID:= Struct.th32ProcessID;
MyThread.PIDHandle:= OpenProcess(PROCESS_QUERY_INFORMATION或PROCESS_VM_OPERATION或
PROCESS_VM_READ,false,PID);
如果POS(Struct.szExeFile,ExeName)= 0,那么
MyThread.MapMemory(Struct.szExeFile);
CloseHandle(MyThread.PIDHandle);
until(not Process32Next(Handle,Struct));
Processes.Free;
结束

程序MyMainThread.Execute;
begin
而不是终止do
begin
MyThread:= Self;
SetTimer(0,0,10000,@TimerProc);
CreateMessagePump;
终止;
结束
结束为了拿大卫的建议,不要被'@'操作员殴打,我们应该首先重新声明一下这个@ SetTimer 函数正确使用回调。看起来像:

  threadvar 
MyThread:MyMainThread;

procedure TimerProc(hwnd:HWND; uMsg:UINT; idEvent:UINT_PTR; dwTime:DWORD);
stdcall;
var
..
begin
..
end;

type
TFnTimerProc = procedure(hwnd:HWND; uMsg:UINT; idEvent:UIntPtr;
dwTime:DWORD); STDCALL;

函数SetTimer(hWnd:HWND; nIDEvent:UIntPtr; uElapse:UINT;
lpTimerFunc:TFNTimerProc):UINT; STDCALL;外部用户32;

程序MyMainThread.Execute;
begin
MyThread:= Self;
SetTimer(0,0,10000,TimerProc);
CreateMessagePump;
结束


I have the following procedure:

procedure MyMainThread.MapProc;
var
  Handle: THandle;
  PID: dword;
  Struct: TProcessEntry32;
  Processes: TStringList;
begin
  Handle:= CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
  Struct.dwSize:=Sizeof(TProcessEntry32);
  Process32First(Handle, Struct);
  Processes:= TStringList.Create;
  repeat
    Processes.Add(Struct.szExeFile);
    Processes.SaveToFile('C:\Log.txt');
    PID:= Struct.th32ProcessID;
    PIDHandle:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or
      PROCESS_VM_READ, false, PID);
    CloseHandle(PIDHandle);
  until (not Process32Next(Handle,Struct));
  Processes.Free;
end;

As you can see, I save the running processes inside C:\Log.txt, and this works nice when inside an .exe file. Now I'm trying to implement this inside a .DLL file, and the concept is: The DLL will be loaded, and it will have an EntryPoint calling a Thread.Create... This Thread will use a SetTimer to run the procedure MapProc every 10 seconds to save the running processes in C:\Log.txt. The code is:

library Project1;

uses
  Windows,
  SysUtils,
  Classes,
  Registry,
  EncdDecd,
  TLHelp32,
  IdHTTP;

{$R *.res}
type
  MyMainThread = Class(TThread)
  var
    DestDir, ContactHost: String;
    Sent: TStringList;
    PIDHandle: THandle; //need to be public because we use in MapProc / CatchYa
  private
    procedure MapProc;
    procedure MapMemory(ProcessName: string);
    procedure CreateMessagePump;
  protected
    constructor Create;
    procedure Execute; override;
  end;

constructor MyMainThread.Create;
begin
  inherited Create(false);
  FreeOnTerminate:= true;
  Priority:= tpNormal;
end;

procedure MyMainThread.Execute;
begin
  while not Terminated do
    begin
      SetTimer(0, 0, 10000, @MyMainThread.MapProc); //setting timer 10 seconds calling MapProc
      CreateMessagePump; //we are inside DLL so I think we need Message Pump to timer work
      Terminate;
    end;
end;


procedure MyMainThread.MapProc;
var
  Handle: THandle;
  PID: dword;
  Struct: TProcessEntry32;
  Processes: TStringList;
begin
  Handle:= CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
  Struct.dwSize:=Sizeof(TProcessEntry32);
  Process32First(Handle, Struct);
  Processes:= TStringList.Create;
  repeat
    Processes.Add(Struct.szExeFile);
    Processes.SaveToFile('C:\Log.txt');
    PID:= Struct.th32ProcessID;
    PIDHandle:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or
      PROCESS_VM_READ, false, PID);
    if POS(Struct.szExeFile, ExeName) = 0 then
      MapMemory(Struct.szExeFile); //procedure called for verification purposes, but it's not even getting called
    CloseHandle(PIDHandle);
  until (not Process32Next(Handle,Struct));
  Processes.Free;
end;


procedure MyMainThread.CreateMessagePump;
var
  AppMsg: TMsg;
begin
  while GetMessage(AppMsg, 0, 0, 0) do
    begin
      TranslateMessage(AppMsg);
      DispatchMessage(AppMsg);
    end;
  //if needed to quit this procedure use PostQuitMessage(0);
end;


procedure EntryPoint(Reason: integer);
begin
  if Reason = DLL_PROCESS_ATTACH then
    begin
      MyMainThread.Create;
    end
  else
  if Reason = DLL_PROCESS_DETACH then
    begin
      MessageBox(0, 'DLL De-Injected', 'DLL De-Injected', 0);
    end;
end;

begin
  DLLProc:= @EntryPoint;
  EntryPoint(DLL_PROCESS_ATTACH);
end.

But when running this, I get in the Log.txt file only the line: [System Process]

The exe hosting DLL is:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;


implementation

{$R *.dfm}



procedure TForm1.Button1Click(Sender: TObject);
var
  HD: THandle;
begin
  HD:= LoadLibrary('C:\Project1.dll');
end;

end.

解决方案

The reason that your code fails is that you're not using a proper callback for the SetTimer function. As per the documentation that should have a signature like

procedure (hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR; dwTime: DWORD); stdcall;

Your incompatible callback - which is a class method - causes the code to think the Self lives at a completely arbitrary memory address, as class methods has an implicit Self parameter but winapi has no knowledge of that. Now when the code tries to write to an invalid address - 'PIDHandle', assuming there should be a class field, an AV is raised and since the exception is not handled the rest of the code is not executed - also as explained in David's answer.

Your solution is to use a proper callback. To access class members you can use a global variable. Not using a global variable would require some hacky code (google for MethodToProcedure f.i.)

A sample could be like:

threadvar
  MyThread: MyMainThread;

procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR; dwTime: DWORD);
  stdcall;
var
  Handle: THandle;
  PID: dword;
  Struct: TProcessEntry32;
  Processes: TStringList;
begin
  Handle:= CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
  Struct.dwSize:=Sizeof(TProcessEntry32);
  Process32First(Handle, Struct);
  Processes:= TStringList.Create;
  repeat
    Processes.Add(Struct.szExeFile);
    Processes.SaveToFile('C:\Temp\Log3.txt');
    PID:= Struct.th32ProcessID;
    MyThread.PIDHandle:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or
      PROCESS_VM_READ, false, PID);
    if POS(Struct.szExeFile, ExeName) = 0 then
      MyThread.MapMemory(Struct.szExeFile);
    CloseHandle(MyThread.PIDHandle);
  until (not Process32Next(Handle,Struct));
  Processes.Free;
end;

procedure MyMainThread.Execute;
begin
  while not Terminated do
    begin
      MyThread := Self;
      SetTimer(0, 0, 10000, @TimerProc);
      CreateMessagePump;
      Terminate;
    end;
end;

To take David's advice, not to get beaten by the '@' operator, we should first redeclare the SetTimer function to use the callback correctly. That would look something like:

threadvar
  MyThread: MyMainThread;

procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR; dwTime: DWORD);
  stdcall;
var
  ..
begin
  ..
end;

type
  TFnTimerProc = procedure (hwnd: HWND; uMsg: UINT; idEvent: UIntPtr;
      dwTime: DWORD); stdcall;

function SetTimer(hWnd: HWND; nIDEvent: UIntPtr; uElapse: UINT;
  lpTimerFunc: TFNTimerProc): UINT; stdcall; external user32;

procedure MyMainThread.Execute;
begin
  MyThread := Self;
  SetTimer(0, 0, 10000, TimerProc);
  CreateMessagePump;
end;

这篇关于在DLL程序中使用Process32First / Next的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆