吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 478|回复: 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] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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-5-21 08:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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