阿,刚写完就有passion,小冬的回复了,偶慢了点。
To 精灵猪
有几个问题请教:
1、关于$006FC6B4里的字符串数据
这说明$006FC6B4是经过编译后生成的全局变量S的表示。
比如 S :='ABCD'。
"
P := Pointer($006FC6B4);
P := Pointer(PInteger(P)^);
s:=pchar(p);
"
这也正是程序内部的真实执行。
问题是你是如何得知$006FC6B4这个位置的。如果再添加了一些其他的全局变量,
这个位置很有可能改变。
关键是,如果并不知道$006FC6B4是一种string的全局变量,那么Pointer(PInteger(P)^)或pchar(p)有可能
出现非法内存访问。出现 mov [0000000],x 应该算是比较正常的情况。
你要将这个功能应用到何处呢?有什么实际意义呢?
假如说我们通过某种方法可以检测出是string全局变量,我参考了内存泄露检查工具MemProf.pas,
这是我之前用于测试程序时修改的:
for I := 0 to CurrFree - 1 do //分析未被释放的内存
begin
BlockSize := PDWORD(DWORD(ObjList[I]) - 4)^; //内存起始地址的前面4个字节存放的是此内存块的大小
Write(OutFile, I: 4, ') ', IntToHex(Cardinal(ObjList[I]), 16), '~', IntToHex(Cardinal(ObjList[I]) + Cardinal(BlockSize), 16), ' - '); //申请的内存起始地址(增大方向)
Write(OutFile, BlockSize: 4, '($' + IntToHex(BlockSize, 4) + ')字节', ' - ');
try
Item := TObject(ObjList[I]);
try
case PTypeInfo(Item.ClassInfo).Kind of
tkClass:
begin
try
ptd := GetTypeData(PTypeInfo(Item.ClassInfo));
ppi := GetPropInfo(PTypeInfo(Item.ClassInfo), 'Name'); { 如果是TComponent }
if ppi <> nil then
begin
write(OutFile, GetStrProp(Item, ppi));
write(OutFile, 'ClassName=[' + Item.className + ']--ppi(已命名) : ');
Write(OutFile, ' (', ptd.ClassType.InstanceSize, ' 字节) - In ', ptd.UnitName, '.pas');
end
else
begin
write(OutFile, 'ClassName=[' + Item.className + ']--ppi(未命名) : '); //比如TStringList lst
Write(OutFile, ' (', ptd.ClassType.InstanceSize, ' 字节) - In ', ptd.UnitName, '.pas');
end;
except
MyOP('[GetTypeData等意外]');
end;
end; //end tkClass
tkInteger:
begin
MyOP('[tkInteger]');
end;
tkChar:
begin
MyOP('[tkChar]');
end;
tkEnumeration:
begin
MyOP('[tkEnumeration]');
end;
tkFloat:
begin
MyOP('[tkFloat]');
end;
tkString:
begin
MyOP('[tkString]');
end;
tkSet:
begin
MyOP('[tkSet]');
end;
tkMethod:
begin
MyOP('[tkMethod]');
end;
tkWChar:
begin
MyOP('[tkWChar]');
end;
tkLString:
begin
MyOP('[tkLString]');
end;
tkWString:
begin
MyOP('[tkWString]');
end;
tkVariant:
begin
MyOP('[tkVariant]');
end;
tkArray:
begin
MyOP('[tkArray]');
end;
tkRecord:
begin
MyOP('[tkRecord]');
end;
tkInterface:
begin
MyOP('[tkInterface]');
end;
tkInt64:
begin
MyOP('[tkInt64]');
end;
tkDynArray:
begin
MyOP('[tkDynArray]');
end;
tkUnknown:
begin
MyOP('[tkUnKnown]');
end
else //case else
begin
MyOP('不是已知类型');
end;
end; //case end
except on E: Exception do
begin
MyOP('ClassName=[' + Item.ClassName + '] PTypeInfo.Kind异常');
MyOP(' 异常信息:' + E.message);
end;
end;
except
on E: Exception do
begin
MyOP('TObject(ObjList[I])异常:不是对象 '); //比如lst.add('A')
MyOP(' 异常信息:' + E.message);
end;
end;
虽然有对全局变量的分析(字符串局部变量的情况未知),但是我对PTypeInfo不了解,因此我有点怀疑
类似于tkFloat:
begin
MyOP('[tkFloat]');
end;
或
tkString:
begin
MyOP('[tkString]');
end;
是不是能够在同一指针层次检测出来呢?
如果不是类似于普通的TStringList泄露,其他的大部分是"TObject(ObjList[I])异常:不是对象"的结果。
2、关于搜索内存比如我要搜索的内存数据
你要搜索的是Data,还是Code呢?
怎样才能使正在运行的程序可以接受程序的修改呢。
如果是Data完全可以在程序中控制阿。如果由搜索控制,如何弄清倒是是数据还是指针?
如果是Code,也可以在程序中控制阿。如果由搜索控制,我在网上搜到了如下代码,文章如下(加//是我的注释),可供讨论:
一个使用异常处理转移,且修改程序代码的例子
0040CEDD B838FD4800 MOV EAX,0048FD38 ;设置异常处理程序 //函数指针,比如MyFunc();
0040CEE2 50 PUSH EAX //入栈MyFunc
0040CEE3 64FF3500000000 PUSH DWORD PTR FS:[00000000] //入栈原始异常处理起始 SEH
0040CEEA 64892500000000 MOV FS:[00000000],ESP //FS:[00000000]还是指向最原始的异常,MyFunc在ESP+4处
0040CEF1 33C0 XOR EAX,EAX
0040CEF3 8908 MOV [EAX],ECX ---->产生内存访问异常 //异常处理转移
0040CEF5 50 45 43 6F 6D 70 61 63-74 32 00 66 5F D9 C2 EC PECompact2.f_... //一种压缩壳,不了解
。
=================异常处理程序==========================================
0048FD38 B83FEB48F0 MOV EAX,F048EB3F //F048EB3F是什么?初步猜测为系统函数地址
0048FD3D 8D881C120010 LEA ECX,[EAX+1000121C] //ECX=10048FD5B 溢出了,因此 ECX=$0048FD5B
0048FD43 894101 MOV [ECX+01],EAX //ECX地址加一处存放F048EB3F
0048FD46 8B542404 MOV EDX,[ESP+04] //进入函数后,堆栈肯定发生了变化,现在不知道是什么了?
0048FD4A 8B520C MOV EDX,[EDX+0C] //算作一种间接寻址吧
0048FD4D C602E9 MOV BYTE PTR [EDX],E9 ;修改指令代码 //EDX还是一个地址。E9是jmp吗?很可能
0048FD50 83C205 ADD EDX,05 //EDX := EDX + 5 //E9 X1 X2 X3 X4 X5
0048FD53 2BCA SUB ECX,EDX //ECX := ECX - EDX
048FD55 894AFC MOV [EDX-04],ECX ;修改指令代码 //继续往下面的地址修改:
0048FD58 33C0 XOR EAX,EAX //EDX-04 又回到E9,因此,修改后[EDX]内存为 E9 ECX
0048FD5A C3 RET
0048FD5B B83FEB48F0 MOV EAX,F048EB3F //F048EB3F
0048FD60 648F0500000000 POP DWORD PTR FS:[00000000] //恢复最原始的SEH
0048FD67 83C404 ADD ESP,04 //返回到MyFunc入栈前(猜测)
上面那条指令修改成了下面指令
0040CEF3 E9632E0800 JMP 0048FD5B---------->转移 //ECX 原来是0048FD5B阿
//根据上面的程序流程画个简要的内存图
EAX=F048EB3F
ECX=0048FD5B
[0048FD5C] := $F048EB3F
EDX 猜测为当前代码段中的地址
ECX := 0048FD5B - EDX ,再将ECX存入[EDX-$04] 也就是说ECX=$0048FD5B
因此 SUB ECX,EDX中 EDX:=0;
而 MOV BYTE PTR [EDX],E9 ,此时EDX=$0040CEF3
ADD EDX,05; EDX=40CEF8
SUB ECX,EDX; ECX := 0048FD5B - 40CEF8 = 82E63
而最后的结果ECX应该为0048FD5B,看来哪个地方分析的有问题。有时间也找个PECompact2看看吧。
通过看上面这段代码,需要继续了解的是:
1、使CODE变为可修改
2、在Delphi中如何实现汇编的 seh代码。
通常的try except 生成了类似的汇编代码,但是用Delphi内嵌汇编如何实现呢?
3、上面的EAX=F048EB3F 以及 EDX ECX如何得到呢,如何用Delphi实现上面的功能呢?
牵扯的东西不少啊,有些概念自己也很混沌,仅供大家参考吧。