Delphi版内存共享


直接上代码(这里列出C++和Delphi的代码),Delphi代码中包含导入及导出文件和函数列表,PE结构可参阅资料,很多很详细,需要注意的是,本例中是映射到内存,不是通过PE装载器装入的,所以对于节的RVA地址需要转换成为文件偏移地址。

** 一.原理

 

通过使用“内存映射文件”,实现内存共享

Delphi代码

二.主要操作

 

共享内存结构:

[delphi] view
plaincopy

PShareMem = ^TShareMem;
  TShareMem = Record
    id:string[10];
    name:string[20];
    age:Integer;
  end;

 

基本变量:

  1. unit Unit1;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
  7.   Dialogs, StdCtrls, ComCtrls;  
  8.   
  9. type  
  10.   //导入表元素结构  
  11.   TImageImportDiscriptor = packed record  
  12.     OriginalFirstThunk: DWORD;  
  13.     DataTimpStamp: DWORD;  
  14.     ForwardChain: DWORD;  
  15.     DLLName: DWORD;  
  16.     FirstThunk: DWORD;  
  17.   end;  
  18.   PImageImportDiscriptor = ^TImageImportDiscriptor;  
  19.   //导出表元素结构  
  20.   PImageExportDirectory = ^TImageExportDirectory;  
  21.   TImageExportDirectory = packed record  
  22.     Characteristics: DWORD;  
  23.     TimeDateStamp: DWORD;  
  24.     MajorVersion: WORD;  
  25.     MinorVersion: WORD;  
  26.     Name: DWORD;  
  27.     Base: DWORD;  
  28.     NumberOfFunctions: DWORD;  
  29.     NumberOfNames: DWORD;  
  30.     AddressOfFunctions: DWORD;  
  31.     AddressOfNames: DWORD;  
  32.     AddressOfNameOrdinals: DWORD;  
  33.   end;  
  34.   //函数名结构  
  35.   TImportByName = packed record  
  36.     proHint: Word;  
  37.     proName: array [0..1] of char;  
  38.   end;  
  39.   PImportByName = ^TImportByName;  
  40.     
  41.   TForm1 = class(TForm)  
  42.     OpenDialog1: TOpenDialog;  
  43.     Button1: TButton;  
  44.     TreeView1: TTreeView;  
  45.     Label1: TLabel;  
  46.     procedure Button1Click(Sender: TObject);  
  47.   private  
  48.     { Private declarations }  
  49.     procedure GetList(filename:string);  
  50.     {导入列表}  
  51.     procedure GetImportList(pBaseAddress:Pointer;ntHeader:PImageNtHeaders);  
  52.     {导出列表}  
  53.     procedure GetExportList(pBaseAddress:Pointer;ntHeader:PImageNtHeaders);  
  54.   public  
  55.     { Public declarations }  
  56.   end;  
  57.   
  58. var  
  59.   Form1: TForm1;  
  60.   
  61. implementation  
  62.   
  63. {$R *.dfm}  
  64.   
  65. { TForm1 }  
  66.   
  67. procedure TForm1.GetList(filename: string);  
  68. var  
  69.   fileHandle:THandle;  
  70.   fileMap:THandle;  
  71.   pBaseAddress:Pointer;  
  72.   dosHeader: PImageDosHeader;  
  73.   ntHeader: PImageNtHeaders;  
  74. begin  
  75.   TreeView1.Items.Clear;  
  76.   try  
  77.     //打开文件  
  78.     fileHandle := CreateFile(PChar(filename),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);  
  79.     if fileHandle = INVALID_HANDLE_VALUE then  
  80.     begin  
  81.       ShowMessage(‘文件打开失败!’);  
  82.       Exit;  
  83.     end;  
  84.     //创建内存映射  
  85.     fileMap := CreateFileMapping(fileHandle,nil,PAGE_READONLY,0,0,nil);  
  86.     if fileMap = 0 then  
  87.     begin  
  88.       ShowMessage(‘创建内存映射失败!’);  
  89.       Exit;  
  90.     end;  
  91.     //映射到当前进程,pBaseAddress是基址  
  92.     pBaseAddress := MapViewOfFile(fileMap,FILE_MAP_READ,0,0,0);  
  93.     if pBaseAddress = nil then  
  94.     begin  
  95.       ShowMessage(‘获取地址失败!’);  
  96.       Exit;  
  97.     end;  
  98.     //获取Dos信息头部结构数据  
  99.     dosHeader := pImageDosHeader(LongInt(pBaseAddress));  
  100.     //判断Dos标识  
  101.     if dosHeader.e_magic <> IMAGE_DOS_SIGNATURE then  
  102.     begin  
  103.       ShowMessage(‘不可识别的文件格式!’);  
  104.       Exit;  
  105.     end;  
  106.     //获取NT信息头部结构数据,IsBadReadPtr判断指针是否可读,ntHeader.Signature是NT标识  
  107.     ntHeader := pImageNtHeaders(LongInt(pBaseAddress)+dosHeader._lfanew);  
  108.     if (IsBadReadPtr(ntHeader,SizeOf(TImageNtHeaders))) or  
  109.        (ntHeader.Signature <> IMAGE_NT_SIGNATURE) then  
  110.     begin  
  111.        ShowMessage(‘不是有效地Win32程序!’);  
  112.        Exit;  
  113.     end;  
  114.     GetImportList(pBaseAddress,ntHeader);  
  115.     GetExportList(pBaseAddress,ntHeader);  
  116.   finally  
  117.     UnmapViewOfFile(pBaseAddress);  
  118.     CloseHandle(fileMap);  
  119.     CloseHandle(fileHandle);  
  120.   end;  
  121. end;  
  122.   
  123. procedure TForm1.Button1Click(Sender: TObject);  
  124. begin  
  125.   if OpenDialog1.Execute then  
  126.   begin  
  127.     Label1.Caption := ‘以下是’+OpenDialog1.FileName+’的导入及导出表’;  
  128.     GetList(OpenDialog1.FileName);  
  129.   end;  
  130. end;  
  131.   
  132.   
  133. procedure TForm1.GetExportList(pBaseAddress: Pointer; ntHeader: PImageNtHeaders);  
  134. var  
  135.   imageEntry: PImageExportDirectory;  
  136.   sectionHeader: PImageSectionHeader;  
  137.   importbyname: PImportByName;  
  138.   proEntry:PDWORD;  
  139.   proTemp:PWORD;  
  140.   rva,frva: DWORD;  
  141.   dllname: string;  
  142.   i,j:integer;  
  143.   node:TTreeNode;  
  144.   s:string;  
  145.   pname:PChar;  
  146. begin  
  147.   rva := ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;  
  148.   if rva = 0 then Exit;  
  149.   //定位到第一个节的地址  
  150.   sectionHeader := PImageSectionHeader(LongInt(ntHeader)+SizeOf(TImageNtHeaders));  
  151.   //ntHeader^.FileHeader.NumberOfSections为节的数量,此处循环,找到引入表的节  
  152.   for i := 0 to ntHeader^.FileHeader.NumberOfSections – 1 do  
  153.   begin  
  154.     //IMAGE_DIRECTORY_ENTRY_IMPORT,引入表,检查rva是否落在节内  
  155.     if ( rva >= LongInt(sectionHeader.VirtualAddress)) and (rva<LongInt(sectionHeader.VirtualAddress+sectionHeader.Misc.VirtualSize)) then  
  156.     begin  
  157.       Break;  
  158. 澳门新葡亰手机版,    end;  
  159.     //没找到,那么增加SizeOf(TImageSectionHeader)字节数,指向下一个节  
  160.     Inc(sectionHeader);  
  161.   end;  
  162.   node := TreeView1.Items.Add(nil,’导出函数表’);  
  163.   frva := sectionHeader.VirtualAddress – sectionHeader.PointerToRawData;  
  164.   //导出表入口  
  165.   imageEntry := PImageExportDirectory(LongInt(pBaseAddress)+rva-frva);  
  166.   proEntry := PDWord(LongInt(pBaseAddress)+imageEntry.AddressOfFunctions-frva);  
  167.   pname := PChar(LongInt(pBaseAddress)+imageEntry.Name-frva);  
  168.   for i := 0 to imageEntry.NumberOfFunctions – 1 do  
  169.   begin  
  170.     if proEntry^ = 0 then Continue;  
  171.     proTemp := PWORD(LongInt(pBaseAddress)+LongInt(imageEntry.AddressOfNameOrdinals)-frva);  
  172.     for j := 0 to imageEntry.NumberOfNames –  1 do  
  173.     begin  
  174.       if proTemp^ = i then  
  175.       begin  
  176.         s := ”;  
  177.         while True do  
  178.         begin  
  179.           if pname^=#0 then Break;  
  180.           Inc(pname);  
  181.         end;  
  182.         while True do  
  183.         begin  
  184.           if (pname-1)^=#0 then  
  185.           begin  
  186.             s:=Format(‘%s’, [pname]);  
  187.             Break;  
  188.           end;  
  189.           Inc(pname);  
  190.         end;  
  191.       end;  
  192.       Inc(proTemp);  
  193.     end;  
  194.     TreeView1.Items.AddChild(node,s);  
  195.     Inc(proEntry);  
  196.   end;  
  197. end;  
  198.   
  199. procedure TForm1.GetImportList(pBaseAddress: Pointer; ntHeader: PImageNtHeaders);  
  200. var  
  201.   imageEntry: PImageImportDiscriptor;  
  202.   sectionHeader: PImageSectionHeader;  
  203.   importbyname: PImportByName;  
  204.   proEntry:PDWORD;  
  205.   rva,frva: DWORD;  
  206.   dllname: string;  
  207.   i:integer;  
  208.   node:TTreeNode;  
  209. begin  
  210.   rva := ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;  
  211.   if rva = 0 then Exit;  
  212.   //定位到第一个节的地址  
  213.   sectionHeader := PImageSectionHeader(LongInt(ntHeader)+SizeOf(TImageNtHeaders));  
  214.   //ntHeader^.FileHeader.NumberOfSections为节的数量,此处循环,找到引入表的节  
  215.   for i := 0 to ntHeader^.FileHeader.NumberOfSections – 1 do  
  216.   begin  
  217.     //IMAGE_DIRECTORY_ENTRY_IMPORT,引入表,检查rva是否落在节内  
  218.     if ( rva >= LongInt(sectionHeader.VirtualAddress)) and (rva<LongInt(sectionHeader.VirtualAddress+sectionHeader.Misc.VirtualSize)) then  
  219.     begin  
  220.       Break;  
  221.     end;  
  222.     //没找到,那么增加SizeOf(TImageSectionHeader)字节数,指向下一个节  
  223.     Inc(sectionHeader);  
  224.   end;  
  225.   frva := sectionHeader.VirtualAddress – sectionHeader.PointerToRawData;  
  226.   TreeView1.Items.Add(nil,’导入函数表’);  
  227.   //引入表入口  
  228.   imageEntry := PImageImportDiscriptor(LongInt(pBaseAddress)+rva-frva);  
  229.   //加载的DLL的名称,这里是RVA地址,需要转换成文件偏移地址,因为我们不是通过PE加载器加载,而是映射到内存  
  230.   while imageEntry.DLLName <> 0 do  
  231.   begin  
  232.     dllname := PChar(LongInt(pBaseAddress)+imageEntry.DLLName-frva);  
  233.     node := TreeView1.Items.AddChild(TreeView1.Items[0],dllname);  
  234.     if imageEntry.OriginalFirstThunk <> 0 then  
  235.       proEntry := PDWord(LongInt(pBaseAddress)+imageEntry.OriginalFirstThunk-frva)  
  236.     else  
  237.       proEntry := PDWord(LongInt(pBaseAddress)+imageEntry.FirstThunk-frva);  
  238.     while proEntry^ <> 0 do  
  239.     begin  
  240.       if (proEntry^ and $80000000) <> 0 then  
  241.         TreeView1.Items.AddChild(node,Format(‘函数编号:%-15d’,[proEntry^ and $7FFFFFFF]))  
  242.       else  
  243.       begin  
  244.         importbyname := PImportByName(LongInt(pBaseAddress)+proEntry^-frva);  
  245.         TreeView1.Items.AddChild(node,Format(‘函数名称:%-15s’,[importbyname.proName]));  
  246.       end;  
  247.       Inc(proEntry);  
  248.     end;  
  249.     //继续读取  
  250.     Inc(imageEntry);  
  251.   end;  
  252. end;  
  253.   
  254. end.  

    shareMemName:string; //共享内存名
    fileHandle : THandle;//内存映射文件句柄
    pUserInfoShareMem : PShareMem;//指向共享内存的指针
a)写入程序

 

1)创建“内存映射文件”

 

begin
   //创建“内存映射文件”
   fileHandle:=CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(TShareMem), PChar(shareMemName));
   if fileHandle <> 0 then
   begin
     Self.Memo1.Lines.Add(‘已成功创建内存映射文件!’);
   end;
end;

C++代码

2)建立映射关系

 

//将“内存映射文件”与“应用程序地址空间”建立映射关系
  pUserInfoShareMem:=MapViewOfFile(fileHandle,FILE_MAP_ALL_ACCESS,0,0,sizeof(TShareMem));
  if pUserInfoShareMem <> nil then
  begin
     Self.Memo1.Lines.Add(‘已成功建立映射关系!’);
  end;

[cpp] view
plaincopy

 3)写入信息

 

   pUserInfoShareMem.id:=’8888′;
   pUserInfoShareMem.name:=’Terry’;
   pUserInfoShareMem.age:=25;
   Self.Memo1.Lines.Add(‘已向共享内存中写入用户信息!’);
4)解除映射关系

  1. #include<string>  
  2. #include<windows.h>  
  3.   
  4.   
  5. void GetImportDllList(void)  
  6. {  
  7.     IMAGE_IMPORT_DESCRIPTOR* importEntry = 0;  
  8.     IMAGE_SECTION_HEADER* sectionHeader = 0;  
  9.     char* filename = “c://test.exe”;  
  10.     //打开文件  
  11.     HANDLE hfile = CreateFileA(filename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);  
  12.     if (hfile == INVALID_HANDLE_VALUE)  
  13.     {  
  14.         printf(“%s”,”文件打开失败!”);  
  15.         return;  
  16.     }  
  17.     //创建内存映射  
  18.     HANDLE hmap = CreateFileMapping(hfile,NULL,PAGE_READONLY,0,0,NULL);  
  19.     if (hmap == 0)  
  20.     {  
  21.         printf(“%s”,”创建内存映射失败!”);  
  22.         return;  
  23.     }  
  24.     //映射到当前进程,pBaseAddress是基址  
  25.     HANDLE pBaseAddress = MapViewOfFile(hmap,FILE_MAP_READ,0,0,0);  
  26.     if (!pBaseAddress)  
  27.     {  
  28.         printf(“%s”,”获取地址失败!”);  
  29.         return;  
  30.     }  
  31.     //获取Dos信息头部结构数据  
  32.     IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)pBaseAddress;  
  33.     //判断Dos标识  
  34.     if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)  
  35.     {  
  36.         printf(“%s”,”不可识别的文件格式!”);  
  37.         return;  
  38.     }  
  39.     //获取NT信息头部结构数据,IsBadReadPtr判断指针是否可读,ntHeader->Signature是NT标识  
  40.     IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)((DWORD)pBaseAddress+dosHeader->e_lfanew);  
  41.     if (ntHeader->Signature != IMAGE_NT_SIGNATURE)  
  42.     {  
  43.         printf(“%s”,”不是有效地Win32程序!”);  
  44.         return;  
  45.     }  
  46.     //定位到第一个节的地址  
  47.     sectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)ntHeader+sizeof(IMAGE_NT_HEADERS));  
  48.     //ntHeader->FileHeader.NumberOfSections为节的数量,此处循环,找到引入表的节  
  49.     for (int i=0;i<ntHeader->FileHeader.NumberOfSections;i++)  
  50.     {  
  51.         //IMAGE_DIRECTORY_ENTRY_IMPORT,引入表  
  52.         if (sectionHeader->VirtualAddress == ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)  
  53.         {  
  54.             break;  
  55.         }  
  56.         //没找到,那么增加SizeOf(IMAGE_SECTION_HEADER)字节数,指向下一个节  
  57.         sectionHeader++;  
  58.     }  
  59.     //引入表入口  
  60.     importEntry = (IMAGE_IMPORT_DESCRIPTOR*)((DWORD)pBaseAddress+sectionHeader->PointerToRawData);  
  61.     printf(“下面是%s的引入文件/n”,filename);  
  62.     //加载的DLL的名称,这里是RVA地址,需要转换成文件偏移地址,因为我们不是通过PE加载器加载,而是映射到内存  
  63.     while (importEntry->Name != 0)  
  64.     {  
  65.         DWORD offset = (DWORD)pBaseAddress+importEntry->Name-(sectionHeader->VirtualAddress – sectionHeader->PointerToRawData);  
  66.         char* s = (char*)offset;  
  67.         printf(“%s/n”,s);  
  68.         //继续读取  
  69.         importEntry++;  
  70.     }  
  71.     UnmapViewOfFile(pBaseAddress);  
  72.     CloseHandle(hmap);  
  73.     CloseHandle(hfile);  
  74. }  
  75.   
  76. int main()  
  77. {   
  78.     GetImportDllList();  
  79.     return 0;  
  80. }  

  //解除“内存映射文件”与“应用程序地址空间”的映射关系
  if pUserInfoShareMem<> nil then      UnmapViewOfFile(pUserInfoShareMem);
  Self.Memo1.Lines.Add(‘已成功解除映射关系!’);
5)关闭“内存映射文件”

 

  //关闭内存映射文件
  if fileHandle<> 0 then      CloseHandle(fileHandle);
  Self.Memo1.Lines.Add(‘已成功关闭内存映射文件!’);

 

b)读取程序

 

1)打开“内存映射文件”

澳门新葡亰手机版 1澳门新葡亰手机版 2

 fileHandle:=OpenFileMapping(FILE_MAP_ALL_ACCESS,false,pchar(shareMemName));
  if self.FileHandle <> 0 then   begin     Self.Memo1.Lines.Add(‘已成功打开内存映射文件!’)
  end;
2)建立映射关系

pUserInfoShareMem:= MapViewOfFile(self.FileHandle,windows.FILE_MAP_ALL_ACCESS,0,0,sizeof(TShareMem));
   if pUserInfoShareMem <> nil then
   begin
     Self.Memo1.Lines.Add(‘已成功建立映射关系!’);
   end;