Sử dụng biểu mẫu thông tin CONTEXT
bạn có thể tìm phần chức năng và bù đắp trong hình ảnh PE. Ví dụ, bạn có thể sử dụng thông tin này để lấy tên hàm từ tệp .map được tạo bởi trình liên kết.
Nhận CONTEXT
struct. Bạn quan tâm đến thành viên truy cập chương trình. Vì CONTEXT
phụ thuộc vào nền tảng, bạn phải tự mình tìm ra. Bạn đã làm điều đó khi bạn khởi tạo, ví dụ STACKFRAME64.AddrPC.Offset = CONTEXT.Rip
cho x64 Windows. Bây giờ chúng tôi bắt đầu ngăn xếp đi bộ và sử dụng STACKFRAME64.AddrPC.Offset
, được điền bởi StaclkWalk64
làm điểm bắt đầu của chúng tôi.
Bạn cần dịch nó sang Địa chỉ ảo tương đối (RVA) bằng địa chỉ cơ sở phân bổ: RVA = STACKFRAME64.AddrPC.Offset - AllocationBase
. Bạn có thể nhận được AllocationBase
sử dụng VirtualQuery
.
Khi bạn có điều đó, bạn cần phải tìm phần RVA này nằm ở đâu và trừ địa chỉ bắt đầu phần khỏi nó để lấy SectionOffset: SectionOffset = RVA - SectionBase = STACKFRAME64.AddrPC.Offset - AllocationBase - SectionBase
. Để thực hiện điều đó, bạn cần truy cập cấu trúc tiêu đề hình ảnh PE (IMAGE_DOS_HEADER, IMAGE_NT_HEADER, IMAGE_SECTION_HEADER) để lấy số phần trong PE và địa chỉ xuất phát/kết thúc của chúng. Nó khá đơn giản.
Vậy đó. Bây giờ bạn có số phần và bù đắp trong hình ảnh PE. Chức năng bù đắp là bù đắp cao nhất nhỏ hơn SectionOffset trong tập tin .map.
Tôi có thể đăng mã sau, nếu bạn muốn.
EDIT: Mã để in function address
(chúng tôi giả định x64 CPU generic):
#include <iostream>
#include <windows.h>
#include <dbghelp.h>
void GenerateReport(void)
{
::CONTEXT lContext;
::ZeroMemory(&lContext, sizeof(::CONTEXT));
::RtlCaptureContext(&lContext);
::STACKFRAME64 lFrameStack;
::ZeroMemory(&lFrameStack, sizeof(::STACKFRAME64));
lFrameStack.AddrPC.Offset = lContext.Rip;
lFrameStack.AddrFrame.Offset = lContext.Rbp;
lFrameStack.AddrStack.Offset = lContext.Rsp;
lFrameStack.AddrPC.Mode = lFrameStack.AddrFrame.Mode = lFrameStack.AddrStack.Mode = AddrModeFlat;
::DWORD lTypeMachine = IMAGE_FILE_MACHINE_AMD64;
for(auto i = ::DWORD(); i < 32; i++)
{
if(!::StackWalk64(lTypeMachine, ::GetCurrentProcess(), ::GetCurrentThread(), &lFrameStack, lTypeMachine == IMAGE_FILE_MACHINE_I386 ? 0 : &lContext,
nullptr, &::SymFunctionTableAccess64, &::SymGetModuleBase64, nullptr))
{
break;
}
if(lFrameStack.AddrPC.Offset != 0)
{
::MEMORY_BASIC_INFORMATION lInfoMemory;
::VirtualQuery((::PVOID)lFrameStack.AddrPC.Offset, &lInfoMemory, sizeof(lInfoMemory));
::DWORD64 lBaseAllocation = reinterpret_cast<::DWORD64>(lInfoMemory.AllocationBase);
::TCHAR lNameModule[ 1024 ];
::GetModuleFileName(reinterpret_cast<::HMODULE>(lBaseAllocation), lNameModule, 1024);
PIMAGE_DOS_HEADER lHeaderDOS = reinterpret_cast<PIMAGE_DOS_HEADER>(lBaseAllocation);
PIMAGE_NT_HEADERS lHeaderNT = reinterpret_cast<PIMAGE_NT_HEADERS>(lBaseAllocation + lHeaderDOS->e_lfanew);
PIMAGE_SECTION_HEADER lHeaderSection = IMAGE_FIRST_SECTION(lHeaderNT);
::DWORD64 lRVA = lFrameStack.AddrPC.Offset - lBaseAllocation;
::DWORD64 lNumberSection = ::DWORD64();
::DWORD64 lOffsetSection = ::DWORD64();
for(auto lCnt = ::DWORD64(); lCnt < lHeaderNT->FileHeader.NumberOfSections; lCnt++, lHeaderSection++)
{
::DWORD64 lSectionBase = lHeaderSection->VirtualAddress;
::DWORD64 lSectionEnd = lSectionBase + max(lHeaderSection->SizeOfRawData, lHeaderSection->Misc.VirtualSize);
if((lRVA >= lSectionBase) && (lRVA <= lSectionEnd))
{
lNumberSection = lCnt + 1;
lOffsetSection = lRVA - lSectionBase;
break;
}
}
std::cout << lNameModule << " : 000" << lNumberSection << " : " << reinterpret_cast< void * >(lOffsetSection) << std::endl;
}
else
{
break;
}
}
}
void Run(void);
void Run(void)
{
GenerateReport();
std::cout << "------------------" << std::endl;
}
int main(void)
{
::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
::SymInitialize(::GetCurrentProcess(), 0, 1);
try
{
Run();
}
catch(...)
{
}
::SymCleanup(::GetCurrentProcess());
return (0);
}
Thông báo, gọi chồng chúng tôi là (từ trong ra ngoài) GenerateReport()->Run()->main()
. đầu ra Chương trình (trên máy tính của tôi, con đường là tuyệt đối):
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000002F8D
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 00000000000031EB
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000003253
D:\Work\C++\Source\Application\Prototype.Console\Prototype.Console.exe : 0001 : 0000000000007947
C:\Windows\system32\kernel32.dll : 0001 : 000000000001552D
C:\Windows\SYSTEM32\ntdll.dll : 0001 : 000000000002B521
------------------
Bây giờ, cuộc gọi stack về địa chỉ là (từ trong ra ngoài) 00002F8D->000031EB->00003253->00007947->0001552D->0002B521
. So sánh ba offsets đầu tiên nội dung .map
file:
...
0001:00002f40 [email protected]@YAXXZ 0000000140003f40 f FMain.obj
0001:000031e0 [email protected]@YAXXZ 00000001400041e0 f FMain.obj
0001:00003220 main 0000000140004220 f FMain.obj
...
nơi 00002f40
là gần nhất nhỏ hơn bù đắp cho 00002F8D
và vân vân. Cuối cùng ba địa chỉ tham khảo chức năng CRT/hệ điều hành mà gọi main
(_tmainCRTstartup
vv) - chúng ta nên bỏ qua chúng ...
Vì vậy, chúng ta có thể thấy rằng chúng tôi có thể phục hồi vết đống với sự giúp đỡ của .map
tập tin.Để tạo ra stack trace cho ném exception, tất cả những gì bạn phải làm là đặt mã GenerateReport()
vào constructor ngoại lệ (trên thực tế, GenerateReport()
này được lấy từ mã constructor class ngoại lệ tùy chỉnh của tôi (một phần của nó)).
Kiểm tra triển khai của tôi: http://www.dima.to/blog/?p=13 – Alexandru
Theo nhận xét của bạn trong blog, việc triển khai của bạn yêu cầu pdb-s. –