背景
当我们注入一个进程,通过函数地址进行call时经常会遇到这样的一个问题。对方程序每周四会自动更新。更新后之前的函数地址就变化了,然后需要重新找地址。所以,我就使用了一个动态查询的方式。
第一步:先为需要call的函数生成特征码。特征码其实就是反汇编后,去掉地址,只留操作符。
function make_search(v: dword; ms: TMemoryStream; maxsize: integer): string;
var
offset: dword;
isnot_ret: boolean;
description: string;
begin
ms.Clear;
Result := '';
offset := v;
isnot_ret := true;
while isnot_ret do
begin
isnot_ret := pbyte(offset)^ <> $C3;
disassemble_find(offset, description, ms);
if Result <> '' then
Result := Result + #13#10;
Result := Result + description;
if ms.Size > 1024 then
Break;
end;
end;
disassemble_find 函数的行数太多了。
function disassemble_find(var offset: dword; var description: string; ms_find: TMemoryStream): string; overload;
var
memory: TMemory;
actualread: dword;
startoffset: dword;
tempresult: string;
tempst: string;
wordptr: ^word;
dwordptr: ^dword;
dwordptr2: ^dword;
singleptr: ^single;
doubleptr: ^double;
extenedptr: ^extended;
int64ptr: ^int64;
i, j: integer;
prefix: TPrefix;
prefix2: TPrefix;
isprefix: boolean;
last: dword;
foundit: boolean;
search_size: integer;
str_tmp: string;
begin
search_size := 0;
result := inttohex(offset, 8) + ' - ';
isprefix := true;
prefix := [$F0, $F2, $F3, $2E, $36, $3E, $26, $64, $65, $66, $67];
prefix2 := [];
//forced 16-bit
if mode16 then
prefix2 := prefix2 + [$66];
startoffset := offset;
readmemory(pointer(offset), addr(memory), 24, actualread);
/
if actualread > 0 then
begin
//I HATE THESE... (I propably will not add them all, but I'll see how far I get)
while isprefix do
begin
inc(offset);
if memory[0] in prefix then
begin
result := result + inttohexs(memory[0], 2) + ' ';
isprefix := true;
inc(startoffset);
prefix2 := prefix2 + [memory[0]];
ReadMemory(pointer(offset), addr(memory), 24, actualread);
end
else
isprefix := false;
end;
if $F0 in prefix2 then tempresult := 'lock ';
if $F2 in prefix2 then tempresult := tempresult + 'repne ';
if $F3 in prefix2 then tempresult := tempresult + 'repe ';
case memory[0] of //opcode
$00:
begin
description := 'Add';
tempresult := tempresult + 'add ' + MODRM(memory, prefix2, 1, 2, last) + r8(memory[1]);
inc(offset, last - 1);
end;
$01:
begin
description := 'Add';
if $66 in prefix2 then
tempresult := tempresult + 'ADD ' + MODRM(memory, prefix2, 1, 1, last) + r16(memory[1])
else
tempresult := tempresult + 'ADD ' + MODRM(memory, prefix2, 1, 0, last) + r32(memory[1]);
inc(offset, last - 1);
end;
$02:
begin
description := 'Add';
tempresult := tempresult + 'ADD ' + r8(memory[1]) + ',' + MODRM(memory, prefix2, 1, 2, last);
tempresult := copy(tempresult, 0, length(tempresult) - 1);
inc(offset, last - 1);
end;
$03:
begin
description := 'Add';
if $66 in prefix2 then
tempresult := tempresult + 'ADD ' + r16(memory[1]) + ',' + MODRM(memory, prefix2, 1, 1, last)
else
tempresult := tempresult + 'ADD ' + r32(memory[1]) + ',' + MODRM(memory, prefix2, 1, 0, last);
tempresult := copy(tempresult, 0, length(tempresult) - 1);
inc(offset, last - 1);
end;
$04:
begin
description := 'Add x to y';
tempresult := tempresult + 'ADD AL,' + inttohexs(memory[1], 2);
inc(offset);
end;
$05:
begin
description := 'Add x to y';
wordptr := @memory[1];
dwordptr := @memory[1];
if $66 in prefix2 then
begin
tempresult := tempresult + 'ADD AX,' + inttohexs(wordptr^, 4);
inc(offset, 2);
end
else
begin
tempresult := tempresult + 'ADD EAX,' + inttohexs(dwordptr^, 8);
inc(offset, 4);
end;
end;
第二步:查询函数地址
function search_address(ms_search: TMemoryStream; sl: tstringlist): string;
var
idx, i: integer;
ms: TMemoryStream;
Mem: TMemoryBasicInformation;
dwProtect, temp: cardinal;
msize: dword;
canread, willVirtualProtect: boolean;
procedure dosearch;
var
count, idx, i, l: integer;
s: string;
ms_old: TMemoryStream;
ms: TMemoryStream;
begin
ms_search.Position := 0;
ms_search.Read(count, sizeof(ms_search));
for i := 1 to count do
begin
ms := TMemoryStream.Create;
ms_old := TMemoryStream.Create;
ms_search.Read(l, sizeof(l));
setlength(s, l);
ms_search.Read(s[1], l);
ms_search.Read(l, sizeof(l));
ms_old.CopyFrom(ms_search, l);
ms_search.Read(l, sizeof(l));
ms.CopyFrom(ms_search, l);
//if SameText('Py_GetVersion', s) then
begin
idx := search_ms(integer(Mem.BaseAddress), Mem.RegionSize, ms_old);
if idx < 0 then
begin
idx := search_ms(integer(Mem.BaseAddress), Mem.RegionSize, ms);
end;
if idx >= 0 then
begin
sl.AddObject(s, pointer(idx));
if Result <> '' then
Result := Result + #13#10;
Result := Result + s + '=' + IntToStr(idx - integer(Mem.BaseAddress));
end;
end;
FreeAndNil(ms);
FreeAndNil(ms_old);
end;
end;
begin
Result := '';
idx := -1;
if length(Mems) <= 0 then
u_mem.test;
for i := 0 to high(Mems) do
begin
if SameText('mhmain.dll', GetItemFilePath(Mems[i], Modules)) then
if Mems[i].RegionSize > 1024 * 1024 * 4 then
begin
Mem := Mems[i];
idx := i;
Break;
end;
end;
if idx < 0 then
exit;
canread := not IsBadReadPtr(mem.BaseAddress, mem.RegionSize);
if not canread then
begin
if VirtualProtect(mem.BaseAddress, mem.RegionSize, PAGE_EXECUTE_READWRITE, @dwProtect) then
begin
canread := true;
willVirtualProtect := true;
end;
end;
if canread then
begin
dosearch;
end;
if willVirtualProtect then
VirtualProtect(mem.BaseAddress, mem.RegionSize, dwProtect, @temp);
end;
注意
同一个函数可能查到多个地址,但是可以根据多个地址进行校验。命中的范围最集中的那个范围中的地址,一般都是正确的地址。