首页
社区
课程
招聘
[旧帖] [邀请码已发放][原创]枚举内核文件未导出变量,函数.带源码 0.00雪花
发表于: 2011-7-9 11:44 1762

[旧帖] [邀请码已发放][原创]枚举内核文件未导出变量,函数.带源码 0.00雪花

2011-7-9 11:44
1762
本来是昨天发的帖子,不过有些匆忙,后来自己测试的时候又发现了几处bug,
今天改了改,重新发出来吧.
此工具将在指定目录下搜索符号文件,
如果没有对应的符号文件将自动连接微软符号库进行下载

注:本工具只在win7下测试通过,xp下运行无效,
由于时间关系没有进行过多测试与修正
请确保系统system32目录下存在symsrv.dll,dbghelp.dll等必须文件,
这些dll可以在ddk下的Debuggers目录下找到,也可以单独下载windows调试工具箱


废话少说,上源码
这里用了我写的两个自定义单元,封装了对PE的一些常见操作和文件映射,应该没什么理解上的难度 就不发了
最后希望能获得个邀请码,谢谢


unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, InitComponent, FileCtrl, ShellAPI, JwaImageHlp,
   skyMap, skyPE;

type
  TForm1 = class(TForm)
    ListView: TListView;
    edtSymbolPath: TEdit;
    btnSelectDir: TButton;
    stat1: TStatusBar;
    btnFilter: TButton;

    procedure FormCreate(Sender: TObject);
    procedure stat1Resize(Sender: TObject);
    procedure btnSelectDirClick(Sender: TObject);

    procedure WMDROPFILES(var msg:TMessage);message WM_DROPFILES;
    procedure FormDestroy(Sender: TObject);
    procedure btnFilterClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation
var
  hProc:THandle;
  ansiFileName:array [0..MAX_PATH-1] of Char;    //拖放的文件名
  busy:Boolean;

{$R *.dfm}

//过滤已导出符号
procedure TForm1.btnFilterClick(Sender: TObject);
var
  pBase:PByte;
  pExportDir:PImageExportDirectory;
  dwNumberOfNames,i,j,dw,dwNamesAddress:DWORD;
  pFuncName:PAnsiChar;
begin
  if ListView.Items.Count = 0 then Exit;
  if busy then
  begin
    ShowMessage('请等待枚举操作完成');
    Exit;
  end;

  if not FileExists(ansiFileName) then
  begin
    ShowMessage('未找到源文件');
    Exit;
  end;

  //映射文件
  pBase:=MapFile(ansiFileName);
  if pBase = nil then
  begin
    ShowMessage('打开源文件失败');
  end;

  pExportDir:=GetModuleExportDirectory(Cardinal(pBase));
  if pExportDir = nil then
  begin
    UnMapFile(pBase);
    Exit;
  end;

  //以名称导出函数数目
  dwNumberOfNames:=pExportDir.NumberOfNames;
  //以名称导出函数数组地址
  dwNamesAddress:=GetModuleRawFromRVA(Cardinal(pBase),Cardinal(pExportDir.AddressOfNames));

  for i := 0 to dwNumberOfNames - 1 do
  begin
    stat1.Panels[1].Text:=Format('符号项数:%d',[listview.Items.Count]);
    stat1.Update;
    //根据RVA值得到文件映射的线性地址
    dw:=GetModuleRawFromRVA(Cardinal(pBase),PDWORD(dwNamesAddress+i*sizeof(DWORD))^);
    //取得导出函数名
    pFuncName:=PAnsiChar(dw);
    for j := 0 to ListView.Items.Count - 1 do
      if Trim(ListView.Items[j].Caption) = Trim(string(pFuncName)) then
      begin
        ListView.Items[j].Delete;
        ListView.Update;
        Break;
      end;
  end;

  UnMapFile(pBase);
end;

procedure TForm1.btnSelectDirClick(Sender: TObject);
var
  selectedDir:string;
begin
  if not SelectDirectory('选择symbol存储路径','',selectedDir) then Exit;

  edtSymbolPath.Text:=selectedDir;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  //初始化界面控件属性
  InitComponents;
  //接受拖放
  DragAcceptFiles(Handle,True);

  hProc:=GetCurrentProcess;
  //初始化符号服务
  if not SymInitialize(hProc,nil,False) then      //第三个参数为false表示不加载进程中所有模块符号表
  begin
    ShowMessage('初始化符号服务异常');
    Halt(0);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SymCleanup(hProc);
  DragAcceptFiles(Handle,False);
end;

procedure TForm1.stat1Resize(Sender: TObject);
begin
  stat1.Panels[0].Width:=form1.Width - 400;
  stat1.Panels[1].Width:=150;
end;

function FindFileInPathCallBack(FileName:PAnsiChar; Context:Pointer):Boolean;stdcall;
begin
  //返回true表示继续枚举
  result:=True;
end;


//枚举符号回调例程
function EnumSymbolsCallback(pSymInfo:PSYMBOL_INFO; SymbolSize:DWORD; Context:Pointer):Bool;stdcall;
var
  listItem:TListItem;
  pName:array of AnsiChar;
begin
  //返回true将继续枚举,false将停止枚举
  Result:=True;

  listItem:=Form1.ListView.Items.Add;

  //MaxNameLen为0的话表示符号无名称
  if pSymInfo.MaxNameLen<>0 then
  begin
    SetLength(pName,psyminfo.NameLen+1);
    CopyMemory(pName,@pSymInfo.Name,Length(pName));
    listItem.Caption:=string(PAnsiChar(pName));
  end else
    listItem.Caption:='<无名称>';

  //pSymInfo.Address为符号当前所在的VA,也就是PE镜像被载入内存,此时该符号的线性地址
  //pSyminfo.ModBase为PE镜像的基址
  //二者相减得到符号的RVA
  listItem.SubItems.Add(Format('0x%.8x',[pSymInfo.Address - pSyminfo.ModBase]));
  //符号RVA+PE镜像的默认装载基址得到符号的默认VA,
  //这里是通过传递进来的PE镜像映射基址来取得默认装载基址的
  listItem.SubItems.Add(Format('0x%.8x',[pSymInfo.Address - pSyminfo.ModBase + GetModuleDefaultBase(cardinal(context))]));
  //取符号的文件偏移
  listItem.SubItems.Add(Format('0x%.8x',[GetModuleRawFromRVA(Cardinal(context),pSymInfo.Address - pSyminfo.ModBase) - cardinal(context)]));
  //PE镜像默认装载基址
  listItem.SubItems.Add(Format('0x%.8x',[GetModuleDefaultBase(cardinal(context))]));

  Form1.stat1.Panels[1].Text:=Format('符号项数:%d',[form1.ListView.Items.Count]);

  Application.ProcessMessages;
end;

procedure TForm1.WMDROPFILES(var msg: TMessage);
var
  FileName      :array [0..MAX_PATH-1] of Char;
  foundFile     :array [0..MAX_PATH] of AnsiChar;
  searchPath    :AnsiString;
  pBase         :PByte;
  pCV70         :PCVInfoPDB70;
  dwModuleBase  :DWORD;
begin
  if DragQueryFile(msg.WParam,$FFFFFFFF,nil,0) > 1 then
  begin
    MessageDlg('不接受多个文件!',mtinformation,[mbok],0);
    DragFinish(msg.WParam);
    Exit;
  end;

  //取拖放文件名
  DragQueryFile(msg.WParam,0,FileName,MAX_PATH);

  if not FileExists(FileName) then Exit;

  if (edtSymbolPath.Text='') or (not DirectoryExists(edtSymbolPath.Text)) then
    searchPath:=AnsiString('SRV*C:\symbols\*http://msdl.microsoft.com/download/symbols')
  else                            //确保以路径分隔符结尾
    searchPath:=AnsiString('SRV*'+IncludeTrailingPathDelimiter(edtSymbolPath.Text)+
                 '*http://msdl.microsoft.com/download/symbols');

  //设置符号搜索路径
  if not SymSetSearchPath(hProc,PAnsiChar(searchPath)) then
  begin
    ShowMessage('设置符号文件搜索路径出错');
    DragFinish(msg.WParam);
    Exit;
  end;

  //映射拖入的文件
  pBase:=MapFile(FileName);
  if pBase = nil then
  begin
    ShowMessage('打开文件失败');
    DragFinish(msg.WParam);
    Exit;
  end;

  //判断是否为有效PE
  if not IsPEImage(Cardinal(pBase)) then
  begin
    ShowMessage('你拖入了非有效win32 PE 文件');
    UnMapFile(pBase);
    DragFinish(msg.WParam);
    exit;
  end;

  //检测CVInfoPDB70结构,就是利用这个结构中的信息来作为索引在微软符号库进行搜索的
  pCV70:=GetModuleCV70Infomation(Cardinal(pBase));
  if pCV70 = nil then
  begin
    ShowMessage('未检测到文件存在调试信息');
    UnMapFile(pBase);
    DragFinish(msg.WParam);
    Exit;
  end;

  if pCV70.CvSignature <> CV_INFO_PDB70_SIGNATURE then
  begin
    ShowMessage('无效的调试信息');
    UnMapFile(pBase);
    DragFinish(msg.WParam);
    Exit;
  end;

  stat1.Panels[0].Text:=FileName;
  stat1.Update;
  ListView.Clear;

  //开始搜索符号文件,当本地目录未发现时将连接微软符号库
  SymFindFileInPath(hProc,nil,PAnsiChar(AnsiString(pCV70.FileName)),
     @pCV70.Signature,pcv70.Age,0,SSRVOPT_GUIDPTR,foundFile,@FindFileInPathCallBack,nil);

  //load文件
  //因为映射文件的视图与磁盘上一样而在这里必须用loadlibrary函数将文件载入内存
  //符号文件将对应于"载入"形式的PE镜像,而非简单的映射形式
  dwModuleBase:=LoadLibraryEx(FileName,0,DONT_RESOLVE_DLL_REFERENCES);
  if dwModuleBase = 0 then
  begin
    ShowMessage(Format('LoadLibrary调用出错 错误码:0x%.8x',[GetLastError]));
    DragFinish(msg.WParam);
    UnMapFile(pBase);
    Exit;
  end;

  //载入符号文件,在这里返回值就是PE镜像的装载基址
  dwModuleBase:=SymLoadModule(hProc,0,PAnsiChar(ExtractFileName(string(FileName))),nil,dwModuleBase,0);

  busy:=True;
  //开始枚举,在这里映射<而非载入>基址被当做参数传递给回调例程
  SymEnumSymbols(hProc,dwModuleBase,'*!*',@EnumSymbolsCallback,pBase);

  ansiFileName:='';
  MoveMemory(@ansiFileName,@FileName,MAX_PATH);

  busy:=False;

  SymUnloadModule(hProc,dwModuleBase);
  UnMapFile(pBase);
  FreeLibrary(dwModuleBase);
  DragFinish(msg.WParam);
end;

end.


忘了说明一下,CVInfoPDB70这个结构要在调试信息表中取得
PImageDebugDirectory = ^TImageDebugDirectory;
_IMAGE_DEBUG_DIRECTORY = packed record
  Characteristics: DWORD;
  TimeDateStamp: DWORD;
  MajorVersion: Word;
  MinorVersion: Word;
  _Type: DWORD;
  SizeOfData: DWORD;
  AddressOfRawData: DWORD;
  PointerToRawData: DWORD;
end;


PointerToRawData就指向CVInfoPDB70结构,要注意这里的这个双字是文件偏移量,而不是RVA

const

CV_INFO_PDB70_SIGNATURE = $53445352;

type

CV_INFO_PDB70 = record
  CvSignature   : DWORD;   //结构标记'RSDS' 倒序字节为 53 44 53 52
  Signature     : TGUID;
  Age           : DWORD;
  FileName      : array [0..0] of AnsiChar;
end;
TCVInfoPDB70 = CV_INFO_PDB70;
PCVInfoPDB70 = ^TCVInfoPDB70;

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
2
有原理说明和源码示例就更好了 :)
2011-7-9 12:44
0
雪    币: 3107
活跃值: (1249)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
3
MS自带symchk.exe
2011-7-9 13:10
0
游客
登录 | 注册 方可回帖
返回
//