CnPack Forum » 技术板块灌水区 » 内存指针问题,希望高人指点,感激不尽!


2007-2-3 01:29 精灵猪
内存指针问题,希望高人指点,感激不尽!

var
P:Pointer;
s:string;
begin
P := Pointer($006FC6B4);
s:=pchar(p);
end;
这里的s可以得到$006FC6B4里的字符串数据,可是如果006FC6B4这里是个内存地址 比如
08 F3 D6 08 我如何才能得到这个地址?
-----------------------------------
另外比如搜索内存比如我要搜索的内存数据是
0C 00 00 00 54 92 4D 07 48 34 49 07 0C 00 00 00
如何得到内存的地址?
-----------------------------------
如果想把 0C 00 00 00 54 92 4D 07 48 34 49 07 0C 00 00 00
的数据改为 08 00 00 00 08 00 00 00 08 3C 49 07 00 00 00 00
应该如何操作
----------------------------------
这3个问题都是操作进程自身内存的 所以最好不要用到 ReadProcessMemory 和 WriteProcessMemory
这3个问题困饶小弟一段时间了,一直没有解决 希望达人能给点指点 或者资料

2007-2-3 12:03 Passion
给你移到这个区吧。

2007-2-3 12:05 Passion
再指一次差不多吧。
P := Pointer($006FC6B4);
P := Pointer(PInteger(P)^);
s:=pchar(p);

2007-2-4 18:11 精灵猪
Passion大哥果然一句话解决了问题 不过内存搜索的问题应该如何解决? 这个问题我很长时间没搞明白了

2007-2-5 09:12 Passion
Win32下每个进程的地址空间独立4G,也就是00000000~7FFFFFFF,所以从原理上来说只要从Pointer(0)到Pointer(7FFFFFFF)来个大循环就行。
当然有很多地址空间不存在,访问时会出Exception,得屏蔽掉。另外os的dll占去的2g也可以不考虑。

更有效率的法子是先获得自身进程的内存块列表,再搜。

2007-2-5 09:40 kendling
牛!

2007-2-5 10:13 skyjacker
阿,刚写完就有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实现上面的功能呢?

牵扯的东西不少啊,有些概念自己也很混沌,仅供大家参考吧。

2007-2-5 17:03 kendling
我只是在灌水。。。:L

2007-2-5 19:09 精灵猪
感谢Passion 大哥和skyjacker 大哥的讲解 比较多 我一一测试一下 昨天
Passion大哥介绍的方法很好 不过今天我发现了如果用pchar(p);这种方法读内存数据虽然可以读到 但是会造成 程序退出的时候出错 错误提示是内存不能读 (其实已经读到了)
我的程序是把一个DLL以注入的方式进入其他进程 所以应该属于目标进程对自身的操作 现在还不清楚为什么会出现这个问题 我继续研究 能认识Passion 大哥和skyjacker我感到很荣幸 谢谢你们的支持

2007-2-5 19:26 精灵猪
仔细阅读了一下skyjacker 大哥的帖
首先说明目标进程我手里没代码 无法修改 我是通过DLL注入进去操作的 $006FC6B4 这个地址是我用OD调试后确定的地址 Pointer(PInteger(P)^) 没有造成非法访问 不过到pchar(p)的时候出现了内存错 这点我现在也没搞懂 理论上DLL已经算是进程本身了不应该出错才是
另外我要搜索的应该是data段的数据
我是想在启动的时候搜索内存 0C 00 00 00 54 92 4D 07 48 34 49 07 0C 00 00 00
得到0C开始的地址

不知道这样写2位大哥可否看懂

2007-2-5 21:24 kendling
其实你这程序是做什么的?怎么搞得这么复杂?

2007-2-5 22:14 skyjacker
互相学习!
进程注射......
听过,没用过。:o

2007-2-5 22:53 Passion
是同一进程也有不同内存段的读写权限不同的。比如有些代码段就不可写,有些堆栈段就不可执行。随意访问一个地址而出exception应该算比较正常的事才是。

2007-2-6 00:53 kendling
对哇,巴哈姆特好像对进程注入比较有研究。

页: [1]


Powered by Discuz! Archiver 5.0.0  © 2001-2006 Comsenz Inc.