PDB下载

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4018)
#include <Windows.h>
#include <WinInet.h>
#pragma comment(lib,"Wininet.lib")

#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <memory>

/**
 * 下载url网络文件到本地
 * url : 网络程序路径
 * path : 保存到本地的路径
 * 成功返回0,失败返回负数
 */
int
download_http(
	std::string url,
	std::string path)
{
	// 初始化
	HINTERNET it_open = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0);
	if (it_open == NULL) return -1;

	// 打开url
	HINTERNET it_url = InternetOpenUrlA(it_open, url.c_str(), 0, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_PRAGMA_NOCACHE, 0);
	if (it_url == NULL)
	{
		InternetCloseHandle(it_open);
		return -2;
	}

	// 查询程序大小
	DWORD file_size = 0, buf_size = sizeof(DWORD);
	BOOL is_valid_url = HttpQueryInfo(it_url, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH, &file_size, &buf_size, 0);
	if (is_valid_url == FALSE || file_size == 0)
	{
		InternetCloseHandle(it_url);
		InternetCloseHandle(it_open);
		return -3;
	}

	// 创建本地文件
	HANDLE h = CreateFileA(path.c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
	if (h == INVALID_HANDLE_VALUE)
	{
		InternetCloseHandle(it_url);
		InternetCloseHandle(it_open);
		return -4;
	}

	// 开始循环读取网络程序数据
	const unsigned int max_tcp = 4000;
	unsigned int total = 0;
	unsigned int last_value = 0;
	unsigned char buffer[max_tcp]{ 0 };
	memset(buffer, 0, max_tcp);
	while (InternetReadFile(it_url, buffer, max_tcp, &buf_size))
	{
		if (buf_size)
		{
			WriteFile(h, buffer, max_tcp, &buf_size, 0);
			total += buf_size;

			unsigned int value = (unsigned int)((double)total / (double)file_size * (double)100.0f);
			if (value != last_value)
			{
				last_value = value;
				printf("%d%% \n", value);
			}
		}
		else break;
	}

	InternetCloseHandle(it_url);
	InternetCloseHandle(it_open);
	return 0;
}

/*
* 获取指定微软官方程序的pdb下载路径
* path : 程序路径
* url : 返回url下载路径
* 成功返回0,失败返回负数
*/
int
get_pdb_url(
	std::string path,
	std::string& url)
{
	// 打开文件
	HANDLE h = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	if (h == INVALID_HANDLE_VALUE) return -1;

	// 获取文件大小并申请空间
	DWORD size = GetFileSize(h, 0);
	std::shared_ptr<unsigned char> buffer(new unsigned char[size]);
	if (buffer == nullptr)
	{
		CloseHandle(h);
		return -2;
	}

	// 将文件数据读出
	DWORD bytes = 0;
	DWORD ret = ReadFile(h, buffer.get(), size, &bytes, 0);
	if (ret == FALSE || bytes != size)
	{
		CloseHandle(h);
		return -3;
	}

	// 关闭句柄
	CloseHandle(h);

	// 验证dos标识
	PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)buffer.get();
	if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -4;

	// 验证nt标识
	PIMAGE_NT_HEADERS32 nt32 = (PIMAGE_NT_HEADERS32)(dos->e_lfanew + buffer.get());
	if (nt32->Signature != IMAGE_NT_SIGNATURE) return -5;

	// 读取基本信息
	PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt32);
	DWORD sec_count = nt32->FileHeader.NumberOfSections;
	DWORD debug_rva = nt32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
	DWORD debug_size = nt32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;

	// 64和32位不同
	PIMAGE_NT_HEADERS64 nt64 = (PIMAGE_NT_HEADERS64)(dos->e_lfanew + buffer.get());
	if (nt64->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
	{
		sec = IMAGE_FIRST_SECTION(nt64);
		sec_count = nt64->FileHeader.NumberOfSections;
		debug_rva = nt64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
		debug_size = nt64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
	}

	// 程序没有调试数据信息
	if (debug_rva == 0 || debug_size == 0) return -6;

	// 相对虚拟地址转化文件偏移
	auto rva_to_file = [](PIMAGE_SECTION_HEADER sec, int count, int rva) -> int
	{
		for (int i = 0; i < count; i++)
			if (sec[i].VirtualAddress < rva && sec[i].VirtualAddress + sec[i].Misc.VirtualSize > rva)
				return rva - sec[i].VirtualAddress + sec[i].PointerToRawData;
		return 0;
	};

	// 取得调试数据的文件偏移
	int debug_file_offset = rva_to_file(sec, sec_count, debug_rva);
	if (debug_file_offset == 0) return -7;

	// 调试信息结构体
	typedef struct _debug_information_
	{
		unsigned long signature;
		GUID guid;
		unsigned long age;
		char pdb[1];
	}debug_information, * pdebug_information;

	// 取得第一个调试结构指针
	PIMAGE_DEBUG_DIRECTORY debug_dir = (PIMAGE_DEBUG_DIRECTORY)(debug_file_offset + buffer.get());
	for (int i = 0; i * sizeof(IMAGE_DEBUG_DIRECTORY) < debug_size; i++)
	{
		// 类型要求
		if (debug_dir[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) continue;
		pdebug_information info = (pdebug_information)(buffer.get() + debug_dir[i].PointerToRawData);

		// 格式化网络url路径
		std::stringstream sym;
		sym << info->pdb << "/";
		sym << std::setfill('0') << std::setw(8) << std::hex << info->guid.Data1 << std::setw(4) << std::hex << info->guid.Data2 << std::setw(4) << std::hex << info->guid.Data3;
		for (const auto i : info->guid.Data4) sym << std::setw(2) << std::hex << +i;
		sym << "1/" << info->pdb;
		url = "http://msdl.microsoft.com/download/symbols/" + sym.str();

		// 返回成功
		return 0;
	}

	// 返回失败
	return -99;
}

int main(int argc, char* argv[])
{
	if (argc != 3)
	{
		printf("程序路径 pdb保存路径\n");
		return -1;
	}

	std::string url;
	int ret = get_pdb_url(argv[1], url);
	if (ret == 0)
	{
		std::cout << url << std::endl;
		download_http(url, argv[2]);
	}
	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值