主要内容:
1、 sprintf、wsprintf、_stprintf、swprintf问题。
2、 参数sizeof问题。In bytes ,or in characters。
3、 显示资源数据读取数据的优化问题。
4、 输入表崩溃问题(文件数据读取名称失败) 下节课讲
5、 资源类别名称_MBCS环境下只显示一个字符问题。
源码:
首先说明一下:
从这节课开始,不再继续解析二进制资源,因为本来最初的安排就没有细化到解析资源。如果以后有需要,可以继续完善解析。
从这节课以后,简单的PE信息查看器就编写完毕了。以后有机会,可能会优化,或者增强功能。所以这节课是一个bug修复和程序优化的课程。
搞清楚这几个函数的定义。
#ifdef _UNICODE
#define _stprintf swprintf
#else
#define _stprintf sprint
#ifdef UNICODE
#define wsprintf wsprintfW
#else
#define wsprintf wsprintfA
#endif // !UNICODE
我们一直用的是wsprintf,实际上可以用_stprinft。
我们可以写程序验证下两者有没有区别。
为什么要把这个拿出来讲?
因为我一直以为sprinft函数的UNICODE形式是wsprinft,所以我在UNICODE的环境下都用的wsprinft。虽然误打误撞,代码没有问题,但是这种想法是严重的错误。
Sprinft的UNICODE形式swsprinft,这个w在s的后面,而不是前面。
那么为什么用wsprinft,程序也没有错误呢?
Sprintf用于ASCII下,swprintf用于UNICODE下,这个很明显。那么wsprintf呢?
这个wsprinft即可以用于ASCII下,也可以用于UNICODE下,它与前面两个最大的区别就是wsprinft不支持浮点数格式化,即不支持%f,而且有1024个字节的限制,可以看MSDN。它是Windows版的格式化函数。
Wsprinft又可以分为wsprintfA和wsprinftW,分别用于两种不同的环境下。
所以总结起来,我们使用wsprinft是没有错误的。
int main(int argc, char* argv[])
{
printf("Hello World!\n");
char szBuff[10] = {0};
wchar_t wzBuff[10] = {0};
wsprintf(szBuff,"test");
swprintf(wzBuff,L"test");
printf(szBuff);
wprintf(wzBuff);
return 0;
}
Sizeof问题。
首先查找一下代码中的sizeof,看看哪些地方用到了sizeof。
第一个:
if (DragQueryFile(hDropInfo,0,tzFileName,sizeof(tzFileName)))
{
m_strFilePath = tzFileName;
UpdateData(FALSE);
}
看MSDN:
cch
Size, in characters, of the lpszFile buffer.
长度值是字符数,而不是in bytes。
有什么区别?我们实地测试下。
DragQueryFile(hDropInfo,0,tzFileName,sizeof(tzFileName))
DragQueryFile(hDropInfo,0,tzFileName,MAX_PATH)
最后一个参数不同,一个是内存大小,一个是长度。当然,在ASCII环境下是一样的。但我们现在在UNICODE下,那么正确的写法究竟是哪个?我们调试。
可以看到,当参数是sizeof(tzFileName)时,就有可能出现溢出。
真正的参数应该是MAX_PATH,长度值,而不是内存大小。in characters的意思。
显示资源数据读取数据的优化问题
我们当前的做法,是直接从文件里面读取资源数据。但是资源数据实际上已经读取到内存中去了,我们就没有必要再次读取了。
安全起见,我们要判断一下。要判断一个范围,就要读取的数据起始部分是否在内存中,还有要读取的数据的结束部分是否在内存中。
把资源大小的变量改为成员变量。
dwResSize → m_dwResSize;
资源类别,MBCS下只显示一个字符。
char *pResBuff = NULL;
char *pResBuffNew = NULL;
//首先判断要读取的数据是否在内存中。
if (dwOffset > m_dwOffsetRes && (dwOffset + dwSize) < (m_dwOffsetRes + m_dwResSize))
{
pResBuff = (char *)((DWORD)m_pResBuff + dwOffset - m_dwOffsetRes);
}
else
{
//分配内存,存放资源数据
pResBuffNew = new char[dwSize];
if (pResBuffNew == NULL)
{
return;
}
memset(pResBuffNew,0,dwSize);
pResBuff = pResBuffNew;
//读取资源
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwReadLen = 0;
hFile = CreateFile(m_strFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(_T("读取文件失败!"));
return;
}
SetFilePointer(hFile,dwOffset,NULL,FILE_BEGIN);
ReadFile(hFile,pResBuffNew,dwSize,&dwReadLen,NULL);
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
输入表崩溃问题(文件数据读取名称失败)
这个问题我们以前演示过,显示输入表时,程序崩溃。崩溃的原因,上次解释过。现在我们再演示一次,并解释。
修复:
将输入表大小变量改为成员变量,m_dwImportSize。
判断输入表数据是否在内存中。同上。
对DLL名称:
//判断输入表name的数据是否在缓存中
if (pImport->Name > m_dwImportRVA && (pImport->Name + 100) < (m_dwImportRVA + m_dwImportSize))
{
strBuff = (char*)((DWORD)m_pImportBuff + pImport->Name - m_dwImportRVA );
}
else
{
//读取文件
HANDLE hFile = INVALID_HANDLE_VALUE;
hFile = CreateFile(strFilename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
continue;
}
else
{
//将RVA转换为偏移
dwNameOffset = RVAtoOffset((PIMAGE_SECTION_HEADER)m_pSecionBuff,m_wSectionNum,pImport->Name);
SetFilePointer(hFile,dwNameOffset,NULL,FILE_BEGIN);
pNameBuff = new char[256];
memset(pNameBuff,0,256);
ReadFile(hFile,pNameBuff,256,&dwReadLen,NULL);
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
strBuff = (char *)pNameBuff;
delete []pNameBuff;
pNameBuff = NULL;
}
}
对函数名称:
有个问题,我们无法确定到底有多少个。怎么做呢?判断,加循环读取。
void CDialogImport::OnItemchangedListImport(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
if ((pNMListView->uChanged & LVIF_STATE) && (pNMListView->uNewState & LVIS_SELECTED))
{
//
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)m_pImportBuff;
pImport += pNMListView->iItem;
DWORD dwThunk = pImport->OriginalFirstThunk;
IMAGE_THUNK_DATA *pThunkData = NULL;
DWORD dwThunkOffset = 0;
DWORD dwReadLen = 0;
// //如果IMAGE_THUNK_DATA数据已经读取到内存,就直接引用
// if (dwThunk > m_dwImportRVA && dwThunk < (m_dwImportRVA + m_dwImportSize))
// {
// pThunkData = (IMAGE_THUNK_DATA*)((DWORD)m_pImportBuff + dwThunk - m_dwImportRVA);
// }
// else
// {
//否则要读取
//删除原始数据
m_ListFunctions.DeleteAllItems();
HANDLE hFile = INVALID_HANDLE_VALUE;
hFile = CreateFile(m_strFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return;
}
//转换为文件偏移
dwThunkOffset = RVAtoOffset((PIMAGE_SECTION_HEADER)m_pSecionBuff,m_wSectionNum,dwThunk);
const int BuffSize = 256;
char pThunkBuff[BuffSize] = {0};
CONTINUTETHUNK:
//读取THUNK DATA数据
SetFilePointer(hFile,dwThunkOffset,NULL,FILE_BEGIN);
memset(pThunkBuff,0,sizeof(pThunkBuff));
ReadFile(hFile,pThunkBuff,BuffSize,&dwReadLen,NULL);
pThunkData = (IMAGE_THUNK_DATA*)pThunkBuff;
//循环进行显示函数
int iIndexItem = 0 ; //行数索引
int i = 0; //THUNK DATA个数
TCHAR tzBuff[20] = {0};
CString strName;
//解析
while(pThunkData->u1.AddressOfData && i < BuffSize/sizeof(IMAGE_THUNK_DATA))
{
//THUNK虚拟地址
wsprintf(tzBuff,_T("%08X"),dwThunk);
iIndexItem = m_ListFunctions.InsertItem(i,tzBuff);
//THUNK的文件偏移
wsprintf(tzBuff,_T("%08X"),dwThunkOffset);
m_ListFunctions.SetItemText(iIndexItem,1,tzBuff);
//THUNK的值
wsprintf(tzBuff,_T("%08X"),pThunkData->u1.Ordinal);
m_ListFunctions.SetItemText(iIndexItem,2,tzBuff);
if (HIWORD(pThunkData->u1.Ordinal) & (0x8000))
{
//以序号方式输入
m_ListFunctions.SetItemText(iIndexItem,3,_T("-"));
wsprintf(tzBuff,_T("Ord:%08X"),IMAGE_ORDINAL(pThunkData->u1.Ordinal));
m_ListFunctions.SetItemText(iIndexItem,4,tzBuff);
}
else
{
//以名字输入
//定位文件,读取函数名字
DWORD dwOffsetImpByName = RVAtoOffset((PIMAGE_SECTION_HEADER)m_pSecionBuff,m_wSectionNum,(DWORD)pThunkData->u1.AddressOfData);
char pByNameBuff[256] = {0};
//再次读取函数名
SetFilePointer(hFile,dwOffsetImpByName,NULL,FILE_BEGIN);
ReadFile(hFile,pByNameBuff,256,&dwReadLen,NULL);
PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)pByNameBuff;
if (pByName)
{
wsprintf(tzBuff,_T("%04X"),pByName->Hint);
m_ListFunctions.SetItemText(iIndexItem,3,tzBuff);
strName = (char *)pByName->Name;
m_ListFunctions.SetItemText(iIndexItem,4,strName);
}
else
{
m_ListFunctions.SetItemText(iIndexItem,3,_T("-"));
wsprintf(tzBuff,_T("MemAddr:%08X"),(DWORD)pThunkData->u1.Function);
m_ListFunctions.SetItemText(iIndexItem,4,tzBuff);
}
}
dwThunk += 4;
dwThunkOffset += 4;
i++;
pThunkData++;
}
//如果缓存里面的都读取完毕了,就继续读取下面的数据。
if (i == BuffSize / sizeof(IMAGE_THUNK_DATA))
{
//dwThunkOffset += BuffSize;
goto CONTINUTETHUNK;
}
//关闭文件
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
*pResult = 0;
}
资源名称MBCS下编译出现的问题.
strResType = (LPCWSTR)(LPCTSTR)pResDirStringU->NameString;
strResName = (LPCWSTR)(LPCTSTR)pResNameStringU->NameString;
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;