吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 375|回复: 9
收起左侧

[求助] 如何得到内存中的CrackMe.exe字符串的 FOA(文件偏移地址)

[复制链接]
冥界3大法王 发表于 2025-3-13 11:03
事与愿违啊,拿到的地址是RVA(相对偏移地址) 而不是我要的 FOA(文件偏移地址)
image.png


image.png



[Delphi] 纯文本查看 复制代码
unit Unit33;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, PsAPI, TlHelp32;

type
  TForm33 = class(TForm)
    Memo1: TMemo;
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form33: TForm33;

implementation

{$R *.dfm}



// 将 RVA 转换为文件偏移地址
function RVAToFileOffset(RVA: DWORD; BaseAddress: Pointer): DWORD;
var
  DosHeader: PImageDosHeader;
  NtHeaders: PImageNtHeaders;
  SectionHeader: PImageSectionHeader;
  i: Integer;
begin
  Result := 0;
  DosHeader := PImageDosHeader(BaseAddress);
  if DosHeader^.e_magic <> IMAGE_DOS_SIGNATURE then
    Exit;

  NtHeaders := PImageNtHeaders(PByte(BaseAddress) + DosHeader^._lfanew);
  if NtHeaders^.Signature <> IMAGE_NT_SIGNATURE then
    Exit;

  SectionHeader := PImageSectionHeader(PByte(NtHeaders) + SizeOf(TImageNtHeaders));
  for i := 0 to NtHeaders^.FileHeader.NumberOfSections - 1 do
  begin
    if (RVA >= SectionHeader^.VirtualAddress) and (RVA < SectionHeader^.VirtualAddress + SectionHeader^.SizeOfRawData) then
    begin
      Result := RVA - SectionHeader^.VirtualAddress + SectionHeader^.PointerToRawData;
      Exit;
    end;
    Inc(SectionHeader);
  end;
end;

// 搜索字符串 "Good work!" 并返回所有文件偏移地址
procedure SearchStringInProcessMemory(ProcessID: DWORD; const SearchString: string; Results: TStrings);
var
  hProcess: THandle;
  ML: MODULEINFO; // 定义 MODULEINFO 结构体
  hModules: array[0..0] of HMODULE; // 定义一个 HMODULE 数组
  BytesRead: DWORD; // 修正为 DWORD
  Buffer: array of Byte;
  i: Integer;
  SearchBytes: TBytes;
  Found: Boolean;
  BaseAddress: Pointer;
  FileOffset: NativeUInt;
begin
  // 打开目标进程
  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, ProcessID);
  if hProcess = 0 then
  begin
    ShowMessage('无法打开进程!');
    Exit;
  end;
  try
    // 获取主模块的基地址和大小
    if EnumProcessModules(hProcess, @hModules[0], SizeOf(hModules), BytesRead) then
    begin
      if GetModuleInformation(hProcess, hModules[0], @ML, SizeOf(ML)) then
      begin
        BaseAddress := ML.lpBaseOfDll;
        SetLength(Buffer, ML.SizeOfImage);
        // 读取进程内存
        if ReadProcessMemory(hProcess, BaseAddress, @Buffer[0], ML.SizeOfImage, FileOffset) then
        begin
          // 将搜索字符串转换为字节数组
          SearchBytes := TEncoding.ANSI.GetBytes(SearchString);

          // 在内存中搜索字符串
          for i := 0 to Length(Buffer) - Length(SearchBytes) do
          begin
            Found := True;
            for var j := 0 to Length(SearchBytes) - 1 do
            begin
              if Buffer[i + j] <> SearchBytes[j] then
              begin
                Found := False;
                Break;
              end;
            end;
            // 如果找到字符串,计算文件偏移地址并添加到结果中
            if Found then
            begin
              FileOffset := NativeUInt(BaseAddress) + i - NativeUInt(ML.lpBaseOfDll);
              Results.Add(Format('找到字符串,相对虚拟地址: 0x%x', [FileOffset]));
            end;
          end;
        end
        else
        begin
          ShowMessage('无法读取进程内存!');
        end;
      end
      else
      begin
        ShowMessage('无法获取模块信息!');
      end;
    end
    else
    begin
      ShowMessage('无法枚举进程模块!');
    end;
  finally
    CloseHandle(hProcess);
  end;
end;

// 辅助函数:根据进程名称查找进程 ID
function FindProcessID(const ProcessName: string): DWORD;
var
  hSnapshot: THandle;
  ProcessEntry: TProcessEntry32;
begin
  Result := 0;
  hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if hSnapshot <> INVALID_HANDLE_VALUE then
  begin
    try
      ProcessEntry.dwSize := SizeOf(TProcessEntry32);
      if Process32First(hSnapshot, ProcessEntry) then
      begin
        repeat
          if SameText(ProcessEntry.szExeFile, ProcessName) then
          begin
            Result := ProcessEntry.th32ProcessID;
            ShowMessage(Format('找到进程 %s,进程ID: %d', [ProcessName, Result])); // 调试信息
            Break;
          end;
        until not Process32Next(hSnapshot, ProcessEntry);
      end;
    finally
      CloseHandle(hSnapshot);
    end;
  end;
end;

procedure TForm33.Button1Click(Sender: TObject);
var
  ProcessID: DWORD;
  FileOffset: DWORD;
begin
  // 获取 CrackMe.exe 的进程 ID
  ProcessID := FindProcessID('CrackMe.exe');
  if ProcessID = 0 then
  begin
    ShowMessage('未找到 CrackMe.exe 进程!');
    Exit;
  end;
  // 搜索字符串 "Good work!" 并显示结果
  SearchStringInProcessMemory(ProcessID, Edit1.Text, Memo1.Lines);

  FileOffset := RVAToFileOffset(ProcessID, Pointer(Cardinal(4010000)));   =====>这里不会表达
  ShowMessage(Format('RVA: 0x%d, 文件偏移地址: 0x%x', [ProcessID, FileOffset]));  ====>这里程序异常
end;

procedure TForm33.Button2Click(Sender: TObject);
var
  ProcessID: DWORD;
  hProcess: THandle;
  hModules: array[0..0] of HMODULE;
  BytesRead: DWORD;
  ML: MODULEINFO;
  BaseAddress: Pointer;
  RVA: DWORD;
  FileOffset: DWORD;
begin
  // 获取 CrackMe.exe 的进程 ID
  ProcessID := FindProcessID('CrackMe.exe');
  if ProcessID = 0 then
  begin
    ShowMessage('未找到 CrackMe.exe 进程!');
    Exit;
  end;

  // 打开目标进程
  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, ProcessID);
  if hProcess = 0 then
  begin
    ShowMessage('无法打开进程!错误代码: ' + IntToStr(GetLastError));
    Exit;
  end;
  try
    // 获取主模块的基地址和大小
    if EnumProcessModules(hProcess, @hModules[0], SizeOf(hModules), BytesRead) then
    begin
      if GetModuleInformation(hProcess, hModules[0], @ML, SizeOf(ML)) then
      begin
        BaseAddress := ML.lpBaseOfDll;
        Memo1.Lines.Add(Format('模块基地址: 0x%x', [NativeUInt(BaseAddress)])); // 这个是对的!


        FileOffset := RVAToFileOffset(ProcessID, BaseAddress);
        ShowMessage(Format('RVA: 0x%d, 文件偏移地址: 0x%x', [ProcessID, FileOffset])); //==> 这里得到的结果不对!
      end
      else
      begin
        ShowMessage('无法获取模块信息!错误代码: ' + IntToStr(GetLastError));
      end;
    end
    else
    begin
      ShowMessage('无法枚举进程模块!错误代码: ' + IntToStr(GetLastError));
    end;
  finally
    CloseHandle(hProcess);
  end;
end;



end.

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 冥界3大法王 发表于 2025-3-13 11:12
我不要VA ,一般偏移地址不靠谱会ALSR变化;
我也不要RVA,这个主要的作用是脱壳时相对定位;
只有FOA才是真理。
TobyLee 发表于 2025-3-13 12:01
那估计需要改节了,有可能节在文件中只有0x200 加载到内存中是0x1000文件里没有那个数据。这个是我的理解。
 楼主| 冥界3大法王 发表于 2025-3-13 12:15
TobyLee 发表于 2025-3-13 12:01
那估计需要改节了,有可能节在文件中只有0x200 加载到内存中是0x1000文件里没有那个数据。这个是我的理解。

[Delphi] 纯文本查看 复制代码
unit Unit2;

interface

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

type
  TForm2 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

//得到进程名称的
function GetProcessIDByName(const ProcessName: string): DWORD;
var
  hSnapshot: THandle;
  pe32: TProcessEntry32;
begin
  Result := 0;
  hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if hSnapshot <> INVALID_HANDLE_VALUE then
    try
      pe32.dwSize := SizeOf(TProcessEntry32);
      if Process32First(hSnapshot, pe32) then
      begin
        repeat
          if LowerCase(pe32.szExeFile) = LowerCase(ProcessName) then
          begin
            Result := pe32.th32ProcessID;
            Break;
          end;
        until not Process32Next(hSnapshot, pe32);
      end;
    finally
      CloseHandle(hSnapshot);
    end;
end;

function VAtoFOA(hProcess: THandle; VA: Pointer): DWORD;
type
  TImageDosHeader = packed record
    e_magic: Word;
    // ... 其他字段省略,只需要e_lfanew
    e_lfanew: LongWord;
  end;

  TImageNtHeaders = packed record
    Signature: DWORD;
    FileHeader: TImageFileHeader;
    OptionalHeader: TImageOptionalHeader;
  end;

  TImageSectionHeader = packed record
    Name: array [0 .. 7] of AnsiChar;
    VirtualSize: DWORD;
    VirtualAddress: DWORD;
    SizeOfRawData: DWORD;
    PointerToRawData: DWORD;
    // ... 其他字段省略
  end;
var
  DosHeader: TImageDosHeader;
  NtHeaders: TImageNtHeaders;
  SectionHeaders: array of TImageSectionHeader;
  i: Integer;
  BaseAddress: Pointer;
  BytesRead: SIZE_T;
begin
  Result := 0;

  // 读取DOS头
  if not ReadProcessMemory(hProcess, BaseAddress, @DosHeader, SizeOf(DosHeader),
    BytesRead) then
    Exit;

  // 读取NT头
  if not ReadProcessMemory(hProcess,
    Pointer(DWORD(BaseAddress) + DosHeader.e_lfanew), @NtHeaders,
    SizeOf(NtHeaders), BytesRead) then
    Exit;

  // 读取节表
  SetLength(SectionHeaders, NtHeaders.FileHeader.NumberOfSections);
  for i := 0 to NtHeaders.FileHeader.NumberOfSections - 1 do
  begin
    if not ReadProcessMemory(hProcess,
      Pointer(DWORD(BaseAddress) + DosHeader.e_lfanew + SizeOf(NtHeaders) + i *
      SizeOf(TImageSectionHeader)), @SectionHeaders[i],
      SizeOf(TImageSectionHeader), BytesRead) then
      Exit;
  end;

  // 查找对应的节
  for i := 0 to High(SectionHeaders) do
  begin
    if (DWORD(VA) >= SectionHeaders[i].VirtualAddress) and
      (DWORD(VA) < SectionHeaders[i].VirtualAddress + SectionHeaders[i]
      .VirtualSize) then
    begin
      Result := DWORD(VA) - SectionHeaders[i].VirtualAddress + SectionHeaders[i]
        .PointerToRawData;
      Exit;
    end;
  end;
end;

procedure TForm2.Button1Click(Sender: TObject);
const
  TARGET_STRING = 'No luck there';
var
  hProcess: THandle;
  ProcessID: DWORD;
  mbi: TMemoryBasicInformation;
  dwAddress: DWORD;
  Buffer: array [0 .. 4095] of AnsiChar;
  BytesRead: SIZE_T;
  Found: Boolean;
  FOA: DWORD;
begin
  ProcessID := GetProcessIDByName('CrackMe.exe');
  if ProcessID = 0 then
  begin
    ShowMessage('进程未找到');
  end
  else
  begin
    ShowMessage('PID:' + ProcessID.ToString);
  end;

  hProcess := OpenProcess(PROCESS_VM_READ or PROCESS_QUERY_INFORMATION, False,
    ProcessID);
  if hProcess = 0 then
  begin
    ShowMessage('无法打开进程');
  end
  else
  begin
    ShowMessage('I am here!');
  end;

  try
    dwAddress := 0;
    Found := False;
    while VirtualQueryEx(hProcess, Pointer(dwAddress), mbi, SizeOf(mbi))
      = SizeOf(mbi) do
    begin
      if (mbi.State = MEM_COMMIT) and
        (mbi.Protect = PAGE_READONLY or PAGE_READWRITE) then
      begin
        if ReadProcessMemory(hProcess, mbi.BaseAddress, @Buffer, SizeOf(Buffer),
          BytesRead) then
        begin
          if Pos(TARGET_STRING, Buffer) > 0 then
          begin
            dwAddress := DWORD(mbi.BaseAddress) +
              Pos(TARGET_STRING, Buffer) - 1;
            Found := True;
            Break;
          end;
        end;
      end;
      dwAddress := DWORD(mbi.BaseAddress) + mbi.RegionSize;
    end;

    if Found then
    begin
      FOA := VAtoFOA(hProcess, Pointer(dwAddress));
      Edit1.Text := Format('0x%.8x', [FOA]);
    end
    else
    begin
      ShowMessage('字符串未找到');
    end;
  finally
    CloseHandle(hProcess);
  end;
end;

procedure TForm2.Button2Click(Sender: TObject);
const
  TARGET_STRING = 'No luck there';
var
  hProcess: THandle;
  ProcessID: DWORD;
  mbi: TMemoryBasicInformation;
  dwAddress: DWORD;
  Buffer: array [0 .. 4095] of AnsiChar;
  BytesRead: SIZE_T;
  Found: Boolean;
  FOA: DWORD;
begin
  ProcessID := GetProcessIDByName('CrackMe.exe');
  if ProcessID = 0 then
  begin
    ShowMessage('进程未找到');
    Exit;
  end
  else
  begin
    ShowMessage('PID:' + ProcessID.ToString);
  end;

  hProcess := OpenProcess(PROCESS_VM_READ or PROCESS_QUERY_INFORMATION, False,
    ProcessID);
  if hProcess = 0 then
  begin
    ShowMessage('无法打开进程');
    Exit;
  end
  else
  begin
    ShowMessage('成功打开进程');
  end;

  try
    dwAddress := 0;
    Found := False;
    while VirtualQueryEx(hProcess, Pointer(dwAddress), mbi, SizeOf(mbi))
      = SizeOf(mbi) do
    begin
      if (mbi.State = MEM_COMMIT) and
        ((mbi.Protect = PAGE_READONLY) or (mbi.Protect = PAGE_READWRITE)) then
      begin
        if ReadProcessMemory(hProcess, mbi.BaseAddress, @Buffer, SizeOf(Buffer),
          BytesRead) then
        begin
          if Pos(TARGET_STRING, Buffer) > 0 then
          begin
            dwAddress := DWORD(mbi.BaseAddress) +
              Pos(TARGET_STRING, Buffer) - 1;
            Found := True;
            Break;
          end;
        end
        else
        begin
          ShowMessage(Format('无法读取内存区域 0x%x,错误代码: %d', [DWORD(mbi.BaseAddress),
            GetLastError]));
        end;
      end;
      dwAddress := DWORD(mbi.BaseAddress) + mbi.RegionSize;
    end;

    if Found then
    begin
      FOA := VAtoFOA(hProcess, Pointer(dwAddress));
      Edit1.Text := Format('0x%.8x', [FOA]);
    end
    else
    begin
      ShowMessage('字符串未找到');
    end;
  finally
    CloseHandle(hProcess);
  end;
end;

end.

上面随便搜索都能找到, 这个有节的来了。。。却找不到了。。。
头像被屏蔽
52blah 发表于 2025-3-13 12:51
提示: 该帖被管理员或版主屏蔽
lies2014 发表于 2025-3-13 12:53
本帖最后由 lies2014 于 2025-3-13 12:59 编辑

不用重复造轮子,多看别人的代码,这是 pathletboy 的 DllHijacker 中扒出的代码,可以获取正确的 FOA
[Delphi] 纯文本查看 复制代码
function RvaToRaw(const ImageSectionHeader: PImageSectionHeader; Rva: Cardinal): Cardinal;
var
  Temp: PImageSectionHeader;
begin
  Temp := ImageSectionHeader;
  while Temp.VirtualAddress <> 0 do
  begin
    if (Rva >= Temp.VirtualAddress) and (Rva < (Temp.VirtualAddress + Temp.Misc.VirtualSize)) then
    begin
      Result := Temp.PointerToRawData + Rva - Temp.VirtualAddress;
      Break;
    end;
    inc(Temp);
  end;
end;

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
冥界3大法王 + 3 + 1 超级谢谢@Thanks!超级谢谢@Thanks!超级谢谢@Thanks!

查看全部评分

lies2014 发表于 2025-3-13 12:54
52blah 发表于 2025-3-13 12:51
deepseek说
根据字符串在内存中的RVA(Relative Virtual Address)计算其在PE文件中的FOA(File Offset Ad ...

这种 AI 回复不如不回
52blah 发表于 2025-3-13 13:32
lies2014 发表于 2025-3-13 12:54
这种 AI 回复不如不回

还可以,openrouter的ds和官方ds回的差别挺大,md格式显示不友好而已
TobyLee 发表于 2025-3-13 15:25
冥界3大法王 发表于 2025-3-13 12:15
[mw_shl_code=delphi,true]unit Unit2;

interface

6楼看起没毛病啊,你这个回复也看起没错呢,应该不用自己定义结构体吧
m_h 发表于 2025-3-14 15:59
隔壁的 https://bbs.kanxue.com/thread-285914.htm 这个帖子也可以看看。!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-3-25 08:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表