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.