最新文章
热门文章
新华字典词典2009注册码序列号破 
使用VC自己动手编写加壳程序(1)— 
黑鹰VIP破解视频教程(好东西) 
使用VC自己动手编写加壳程序(3)— 
使用VC自己动手编写加壳程序(5)— 
使用VC自己动手编写加壳程序(6)— 
使用VC自己动手编写加壳程序(4)— 
使用VC自己动手编写加壳程序(2)— 
天草破解班视频教程 初级中级高级 
ASPack 2.001 -> Alexey Solodov 
当前位置:李露的博客 >> 黑客学堂 >> 浏览文章
[Begin09PE]第23课 错误排查与程序优化 解决输入表问题
更新日期:2009年12月28日  来源:本站原创  作者:天漏客   访问次数:次  【字体:

主要内容:

1、    sprintf、wsprintf、_stprintf、swprintf问题。
2、    参数sizeof问题。In bytes ,or in characters。
3、    显示资源数据读取数据的优化问题。
4、    输入表崩溃问题(文件数据读取名称失败) 下节课讲
5、    资源类别名称_MBCS环境下只显示一个字符问题。

源码:

 下载信息  [文件大小:457.09 KB 下载次数: 次]
点击下载文件:Begin09PEInfo_23

 




首先说明一下:
从这节课开始,不再继续解析二进制资源,因为本来最初的安排就没有细化到解析资源。如果以后有需要,可以继续完善解析。
从这节课以后,简单的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;
 

发表评论】【告诉好友】【打印此文】【收藏此文】【关闭窗口
上一篇:[Begin09PE]第22课 解析资源(3)加速器(3) 下一篇:[Begin09PE]第24课 解决输入表崩溃问题

Copyright 2006-2012 Powered by LiLu.NAME,李露的博客 All Rights Reserved.
E-Mail:lilu.name#gamil.com(注意是gmail,自己改) QQ:285252760
苏ICP备08016526号