找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 3716|回复: 7

【C语言】初识PE

[复制链接]

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
发表于 2019-2-20 01:12:34 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 watermelon 于 2019-2-20 01:18 编辑

元宵节快乐!前两天我的vs2013出了点毛病,实际上不影响什么使用,但是我总是感觉很不爽,使用它的“修复”功能来进行修复,结果发现没有什么用,我就卸载准备重装,卸载它以后我要重装wdk8.1。当天晚上发现虚拟机也出了毛病,修复没有什么用,应该是少了msvc的什么库,好的接着卸载重装,鉴于百度网盘的速度,要想下载vs2013,wdk8.1(这个快,从微软网站下),vmware14.0,win7 x64.iso等玩意儿,时间要好长。

由于SSDT unhook貌似要用到PE的一些知识,所以小弟我想趁这个机会认识一下PE结构,起码也要有一个印象。

由于PE文件结构知识很多,小弟的文中可能会有一些说法欠妥甚至知识型错误的地方,希望各位大大直接批评指正。
先介绍一下啥是PE文件:PE的全称是Portable Executable意思是可以移植的执行体,有挺多文件都是PE格式,比如常见的EXE,DLL,SYS等格式的文件。PE文件都遵循下图的文件结构(32位PE文件,本文只讨论32位的PE文件):
本图片来自于:https://www.0xaa55.com/forum.php?mod=viewthread&tid=529
PE32.jpg

这些结构体在我们的winnt.h都有定义,所以我们进行学习并且实践的时候可以直接声明定义来用(直接#include <windows.h>即可)。
PE文件可以使用winhex进行查看:
1.png
我们可以看到图中圈红线的两部分:4D 5A对应的字符是MZ,这里是PE文件的DOS头开始地方,接下来我们可以看到蓝圈的50 45对应的字符是PE,说明我们这里是一个PE文件。然后PE文件每个部分每个结构各个成员的意义可以看最上面的那张PE结构图。

我主要是学习PE从以下几个方面进行的:1,打印PE文件结构的一些成员的值。2,我自己想改写一下DOS Stub部分的内容,就是把它的“This program cannot be run in DOS mode”(有没有感觉有些语法不对?)改成自己定义的字符串
3,打印我的EXE所用到输出表的函数

第一个比较好写,由于这个PE文件我们用winhex进行查看的时候他是在磁盘上的,并且winnt.h还给我们定义了他们的结构体,所以我们可以用读文件的方式来进行对相应结构体的读取,怎么对相应结构体进行读取呢?用文件偏移来移动文件指针进行读取。
第二个也是很好写,和第一个一样,只不过多了一个写文件的步骤。
比如PE文件初始时候在winhex上显示的是:
2.png

运行程序后我们对图中画红圈的字符串进行改写成自定义的(用这个东西去表白不错):
3.png

并且更改完这个以后我们的“hello world”程序还是可以正常运行的:
4.png

最让人恼火的是第三个,打印输出表的函数。
我们可以从第一张图片中看到要想找到输入表这个结构体,必经之路是IMAGE_DATA_DIRECTORY,我们通过结构体IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[1]一串儿找下去即可。但是IMAGE_DATA_DIRECTORY这个结构体里面只有两个成员,有用的就是那个VirtualAddress这个RVA(相对偏移地址),这个对于我前两个问题用文件读写结构体来说是个问题。一开始我想的是使用RVA+ImageBase来进行内存读写结构体,结果程序崩溃,调试发现指针访问了无法读写的地址,后来发现进程空间,内存空间这两个东东有些区别,并且有的博客说可以用文件映射,将PE文件映射进内存后用文件映射的基址+RVA进行读写,这个方法可以的。(其实一开始我不信那个邪,一直用ImageBase+RVA来找输入表,我知道输入表里面的有些成员不好验证对不对,我就来找调试信息表,因为PEiD可以查看到调试信息表中的一些成员,结果一直不对,程序崩溃,调试显示内存访问不该访问的地址,浪费了挺多时间和白开水)

所以我们就用文件映射的基址和IMAGE_DATA_DIRECTORY中的VirtualAddress在内存中找输入表,找到输入表后我们开始进行对输入表中的函数来查找。
从第一张PE结构图中我们可以看出结构体IMAGE_IMPORT_DIRECTORY中有两个成员OriginalFirstThunk和FirstThunk后面划着线指向别的东西(IMAGE_THUNK_DATA),这两个成员是非常重要的,在PE文件没有被绑定的情况下,OriginalFirstThunk和FirstThunk是一样的。通过我查找一些资料知道了,在一开始的IAMGE_IMPORT_DIRECTORY是一个承载着DLL的数组,每一个元素是一个DLL并且以全0结尾,而我们从PE结构图中看到这个里面还包含了IMAGE_THUNK_DATA这个结构体,这个结构体指向了一个IMAGE_IMPORT_BY_NAME的结构体,而IMAGE_IMPORT_BY_NAME这个结构体里面有我们要的函数名字。所以我们在通过文件映射+RVA找到导入表iid以后通过OriginalFirstThunk或者FirstThunk来找到IAT(建议用OriginalFirstThunk,为什么可以看文末的参考资料),通过IMAGE_THUNK_DATA里的一个RVA(成员是AddressOfData)找到IMAGE_IMPORT_BY_NAME来打印最后的函数名称。
我在这里列出来IMAGE_THUNK_DATA和IMAGE_IMPORT_BY_NAME两个结构体的定义:
  1. typedef struct _IMAGE_THUNK_DATA32 {
  2.     union {
  3.         DWORD ForwarderString;      // PBYTE
  4.         DWORD Function;             // PDWORD
  5.         DWORD Ordinal;
  6.         DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
  7.     } u1;
  8. } IMAGE_THUNK_DATA32;
复制代码

  1. typedef struct _IMAGE_IMPORT_BY_NAME {
  2.     WORD    Hint;
  3.     CHAR   Name[1];
  4. } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
复制代码


最后是全部的程序实现:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <windows.h>
  5. #include <Dbghelp.h>

  6. // 打印指定PE文件的一些信息
  7. int PrintPEInfo()
  8. {
  9.         HANDLE                                  hFile;
  10.         IMAGE_DOS_HEADER          ImageDosHeader;
  11.         IMAGE_NT_HEADERS          ImageNtHeader;
  12.         IMAGE_SECTION_HEADER  *pImageSectionHeader;
  13.         BOOL                                  bStatus;
  14.         DWORD                                  dwRetSize;
  15.         LARGE_INTEGER                  FileOffset;

  16.         // 打开目标文件
  17.         hFile = CreateFile(L"C:\\Users\\Administrator\\Desktop\\target.exe",
  18.                 FILE_ALL_ACCESS,
  19.                 FILE_SHARE_READ,
  20.                 NULL,
  21.                 OPEN_ALWAYS,
  22.                 FILE_ATTRIBUTE_NORMAL,
  23.                 NULL);
  24.         if (hFile == INVALID_HANDLE_VALUE)
  25.         {
  26.                 printf("CreateFile error:%d\n", GetLastError());
  27.                 return 1;
  28.         }

  29.         // 将PE文件的DOS头部的内容读到结构体ImageDosHeader中
  30.         bStatus = ReadFile(hFile,
  31.                 &ImageDosHeader,
  32.                 sizeof(IMAGE_DOS_HEADER),
  33.                 &dwRetSize,
  34.                 NULL);
  35.         if (bStatus == FALSE)
  36.         {
  37.                 printf("Read image_dos_header failed:%d\n", GetLastError());
  38.                 goto cleanup;
  39.         }

  40.         // 打印MZ和PE头偏移量
  41.         printf("ImageDosHeader.e_magic:%s\nImageDosHeader.e_lfanew:%X\n", &ImageDosHeader.e_magic, ImageDosHeader.e_lfanew);


  42.         // 设置文件的读写指针
  43.         FileOffset.QuadPart = ImageDosHeader.e_lfanew;
  44.         bStatus = SetFilePointerEx(hFile, FileOffset, NULL, FILE_BEGIN);
  45.         if (bStatus == FALSE)
  46.         {
  47.                 printf("SetFilePointerEx error:%d\n", GetLastError());
  48.                 goto cleanup;
  49.         }

  50.         // 读取PE头
  51.         bStatus = ReadFile(hFile,
  52.                 &ImageNtHeader,
  53.                 sizeof(IMAGE_NT_HEADERS),
  54.                 &dwRetSize,
  55.                 NULL);
  56.         if (bStatus == FALSE)
  57.         {
  58.                 printf("ReadFile PE error:%d\n", GetLastError());
  59.                 goto cleanup;
  60.         }
  61.         // 打印“PE”
  62.         printf("ImageNtHeader.Signature:%s\n", &ImageNtHeader.Signature);
  63.         // 打印镜像基址
  64.         printf("ImageBase:%x\n", ImageNtHeader.OptionalHeader.ImageBase);
  65.         // 打印区块的数量
  66.         printf("ImageNtHeader.FileHeader.NumberOfSections:%d\n", ImageNtHeader.FileHeader.NumberOfSections);

  67.         FileOffset.QuadPart = FileOffset.QuadPart + sizeof(IMAGE_NT_HEADERS);
  68.         bStatus = SetFilePointerEx(hFile, FileOffset, NULL, FILE_BEGIN);
  69.         if (bStatus == FALSE)
  70.         {
  71.                 printf("SetFilePointerEx error:%d\n", GetLastError());
  72.                 goto cleanup;
  73.         }

  74.         // 用一个数组来承载描述每个区块的信息,便于到时候打印
  75.         pImageSectionHeader = (IMAGE_SECTION_HEADER*)malloc(sizeof(IMAGE_SECTION_HEADER)*ImageNtHeader.FileHeader.NumberOfSections);
  76.         if (pImageSectionHeader == NULL)
  77.         {
  78.                 printf("pImageSectionHeader malloc error:%d\n", GetLastError());
  79.                 goto cleanup;
  80.         }

  81.         bStatus = ReadFile(hFile,
  82.                 pImageSectionHeader,
  83.                 sizeof(IMAGE_SECTION_HEADER)*ImageNtHeader.FileHeader.NumberOfSections,
  84.                 &dwRetSize,
  85.                 NULL);
  86.         if (bStatus == FALSE)
  87.         {
  88.                 printf("Read image_section_header error:%d\n", GetLastError());
  89.                 free(pImageSectionHeader);
  90.                 goto cleanup;
  91.         }

  92.         // 把区块的名字打印出来
  93.         for (int i = 0; i < ImageNtHeader.FileHeader.NumberOfSections; i++)
  94.         {
  95.                 printf("pImageSectionHeader[%d]:%s\n", i + 1, pImageSectionHeader[i].Name);
  96.         }
  97.         free(pImageSectionHeader);

  98. cleanup:
  99.         CloseHandle(hFile);
  100.         return 0;
  101. }



  102. // 改写一小部分的DOS stub
  103. void WritePE()
  104. {
  105.         HANDLE hFile;
  106.         DWORD dwRetSize;
  107.         char buffer[1024];
  108.         char me[] = "Watermelon is watermelon, forever is forever!";
  109.         LARGE_INTEGER FileOffset;
  110.         BOOL bStatus;

  111.         hFile = CreateFile(L"C:\\Users\\Administrator\\Desktop\\target.exe",
  112.                 FILE_ALL_ACCESS,
  113.                 FILE_SHARE_WRITE,
  114.                 NULL,
  115.                 OPEN_ALWAYS,
  116.                 FILE_ATTRIBUTE_NORMAL,
  117.                 NULL);

  118.         if (hFile == INVALID_HANDLE_VALUE)
  119.         {
  120.                 printf("[WritePE] CreateFile error:%d\n", GetLastError());
  121.                 goto cleanup;
  122.         }

  123.         bStatus = ReadFile(hFile,
  124.                 buffer,
  125.                 0x100,
  126.                 &dwRetSize,
  127.                 NULL);
  128.         if (bStatus = FALSE)
  129.         {
  130.                 printf("[WritePE] ReadFile error:%d\n", GetLastError());
  131.                 goto cleanup;
  132.         }

  133.         for (int i = 0; i < 0x100; i++)
  134.         {
  135.                 if (buffer[i] == 'T' && buffer[i + 1] == 'h' && buffer[i + 2] == 'i' && buffer[i + 3] == 's')
  136.                 {
  137.                         FileOffset.QuadPart = i;
  138.                         break;
  139.                 }

  140.         }

  141.         // 重置文件指针
  142.         SetFilePointerEx(hFile,
  143.                 FileOffset,
  144.                 NULL,
  145.                 FILE_BEGIN);

  146.         bStatus = WriteFile(hFile,
  147.                 me,
  148.                 strlen(me),
  149.                 &dwRetSize,
  150.                 NULL);
  151.         if (bStatus == FALSE)
  152.         {
  153.                 printf("[WritePE] WriteFile error:%d\n", GetLastError());
  154.                 goto cleanup;
  155.         }
  156.         printf("WriteFile has done...\n");


  157. cleanup:
  158.         CloseHandle(hFile);
  159.        
  160. }


  161. // 读取导入表,打印函数
  162. void PrintPEFunction()
  163. {
  164.         HANDLE hFile = CreateFile(L"C:\\Users\\Administrator\\Desktop\\target.exe",
  165.                 GENERIC_READ,
  166.                 FILE_SHARE_READ,
  167.                 NULL,
  168.                 OPEN_EXISTING,
  169.                 FILE_ATTRIBUTE_NORMAL,
  170.                 NULL);

  171.         if (hFile == INVALID_HANDLE_VALUE)
  172.         {
  173.                 printf("[test] CreateFile errror:%d\n", GetLastError());
  174.                 return;
  175.         }

  176.         // 创建文件映射,使用内存来找导入表
  177.         HANDLE hMap = CreateFileMapping(hFile, NULL,
  178.                 PAGE_READONLY,
  179.                 0,
  180.                 0,
  181.                 NULL);

  182.         if (hMap == INVALID_HANDLE_VALUE)
  183.         {
  184.                 printf("[test] CreateFileMapping error:%d\n", GetLastError());
  185.                 goto clean;
  186.         }

  187.         UCHAR *lpBaseAddress = (UCHAR*)MapViewOfFile(hMap,
  188.                 FILE_MAP_READ, 0, 0, 0);

  189.         if (lpBaseAddress == NULL)
  190.         {
  191.                 printf("[test] MapViewOfFile error:%d\n", GetLastError());
  192.                 goto cleanup;
  193.         }

  194.         PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress;
  195.         PIMAGE_NT_HEADERS pImageNtHeader = (PIMAGE_NT_HEADERS)(lpBaseAddress + pImageDosHeader->e_lfanew);

  196.         // 获取导入表的RVA
  197.         DWORD VirtualAddress = pImageNtHeader->OptionalHeader.DataDirectory[1].VirtualAddress;

  198.         if (VirtualAddress == 0)
  199.         {
  200.                 printf("VirtualAddress为0,请更换别的PE文件或者联系我:[email]starlight_chou@163.com[/email]");
  201.                 goto cleanup;
  202.         }

  203.         // 获取导入表地址
  204.         PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(pImageNtHeader,
  205.                 lpBaseAddress,
  206.                 VirtualAddress,
  207.                 NULL);

  208.         IMAGE_IMPORT_DESCRIPTOR iid;
  209.         IMAGE_THUNK_DATA thunk;
  210.         // 由于以0(NULL)结尾,所以我们在这里置零
  211.         memset(&iid, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR));
  212.         memset(&thunk, 0, sizeof(IMAGE_THUNK_DATA));

  213.         // 开始进行遍历
  214.         for (int i = 0; memcmp(pImportTable + i, &iid, sizeof(IMAGE_IMPORT_DESCRIPTOR)); i++)
  215.         {
  216.                 // 获取dll名字,见32位PE结构表
  217.                 printf("\n第%d个DLL,名称:%s\n", i+1, (CHAR*)ImageRvaToVa(pImageNtHeader,
  218.                         lpBaseAddress,
  219.                         pImportTable[i].Name,
  220.                         NULL));
  221.                 // 开始遍历target.exe从对应的DLL中用到了那些函数(IAT)
  222.                 PIMAGE_THUNK_DATA32 pThunk = (PIMAGE_THUNK_DATA32)ImageRvaToVa(pImageNtHeader,
  223.                         lpBaseAddress,
  224.                         pImportTable[i].OriginalFirstThunk,  // 无绑定情况下和pImportTable[i].FirstThunk一样的。
  225.                         NULL);
  226.                 for (int j = 0; memcmp(pThunk + j, &thunk, sizeof(IMAGE_THUNK_DATA)); j++)
  227.                 {
  228.                         // 通过RVA最高位判断函数的导入方式,
  229.                         // 如果最高位为1,按序号导入,否则按照名称导入
  230.                         if (pThunk[j].u1.AddressOfData & IMAGE_ORDINAL_FLAG32)
  231.                         {
  232.                                 printf("[%d]\t%ld", j+1, pThunk[j].u1.AddressOfData & 0xffff);
  233.                         }
  234.                         else
  235.                         {
  236.                                 // 按照名称导入
  237.                                 // 重新判断地址
  238.                                 PIMAGE_IMPORT_BY_NAME pFuncName = (PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(pImageNtHeader,
  239.                                         lpBaseAddress,
  240.                                         pThunk[j].u1.AddressOfData,
  241.                                         NULL);
  242.                                 printf("[%d]\t%ld\t%s\n", j+1, pFuncName->Hint, pFuncName->Name);
  243.                         }
  244.                 }
  245.         }
  246. cleanup:
  247.         CloseHandle(hMap);
  248. clean:
  249.         CloseHandle(hFile);
  250.         return;
  251. }

  252. int main(void)
  253. {
  254.         printf("\n\n#########################################\n\n");
  255.         PrintPEInfo();
  256.         printf("\n\n#########################################\n\n");
  257.         WritePE();
  258.         printf("\n\n#########################################\n\n");
  259.         PrintPEFunction();
  260.         printf("\n\n#########################################\n\n");

  261.         getchar();
  262.         return 0;
  263. }
复制代码


运行结果(更改DOS stub的那部分运行结果见上文):


  1. #########################################

  2. ImageDosHeader.e_magic:MZ?
  3. ImageDosHeader.e_lfanew:E8
  4. ImageNtHeader.Signature:PE
  5. ImageBase:65766572
  6. ImageNtHeader.FileHeader.NumberOfSections:5
  7. pImageSectionHeader[1]:.text
  8. pImageSectionHeader[2]:.rdata
  9. pImageSectionHeader[3]:.data
  10. pImageSectionHeader[4]:.rsrc
  11. pImageSectionHeader[5]:.reloc


  12. #########################################

  13. WriteFile has done...


  14. #########################################


  15. 第1个DLL,名称:MSVCR120.dll
  16. [1]     576     _configthreadlocale
  17. [2]     500     __setusermatherr
  18. [3]     781     _initterm_e
  19. [4]     780     _initterm
  20. [5]     439     __initenv
  21. [6]     674     _fmode
  22. [7]     575     _commode
  23. [8]     592     _crt_debugger_hook
  24. [9]     428     __crtUnhandledException
  25. [10]    427     __crtTerminateProcess
  26. [11]    559     _cexit
  27. [12]    309     ?terminate@@YAXXZ
  28. [13]    425     __crtSetUnhandledExceptionFilter
  29. [14]    916     _lock
  30. [15]    1284    _unlock
  31. [16]    558     _calloc_crt
  32. [17]    430     __dllonexit
  33. [18]    1082    _onexit
  34. [19]    788     _invoke_watson
  35. [20]    579     _controlfp_s
  36. [21]    634     _except_handler4_common
  37. [22]    643     _exit
  38. [23]    1614    exit
  39. [24]    498     __set_app_type
  40. [25]    438     __getmainargs
  41. [26]    535     _amsg_exit
  42. [27]    363     _XcptFilter
  43. [28]    1682    getchar
  44. [29]    1789    printf

  45. 第2个DLL,名称:KERNEL32.dll
  46. [1]     726     GetSystemTimeAsFileTime
  47. [2]     526     GetCurrentThreadId
  48. [3]     522     GetCurrentProcessId
  49. [4]     1069    QueryPerformanceCounter
  50. [5]     877     IsProcessorFeaturePresent
  51. [6]     871     IsDebuggerPresent
  52. [7]     289     EncodePointer
  53. [8]     254     DecodePointer


  54. #########################################

复制代码


实验的目标文件target.exe是一个helloworld程序:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(void)
  4. {
  5.         printf("hello world\n");
  6.         getchar();
  7.         return 0;
  8. }
复制代码



参考资料:
https://www.0xaa55.com/forum.php ... 13&highlight=PE
https://www.0xaa55.com/forum.php ... 29&highlight=PE
https://bbs.pediy.com/thread-21932.htm
https://baike.baidu.com/item/pe% ... /6488140?fr=aladdin
https://baike.baidu.com/item/%E8 ... /1628932?fr=aladdin
以及众多前辈的博客和文章,由于当时没有做好记录,属实不好意思,感谢前辈们的分享。
最后附上看雪的(参考资料中的第三个连接)中的附件,可以用这个对照着PE结构表进行分析学习。

pe1.9+cn.rar

124.78 KB, 下载次数: 2

看雪论坛的附件

Passion Coding!
回复

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
 楼主| 发表于 2019-2-20 01:20:02 | 显示全部楼层
PE知识太多了,具体学还是仔细看书:https://www.0xaa55.com/forum.php ... 98&highlight=PE
感谢站长分享!orz
Passion Coding!
回复 赞! 靠!

使用道具 举报

65

主题

115

回帖

1万

积分

用户组: 超级版主

OS与VM研究学者

UID
1043
精华
35
威望
789 点
宅币
8290 个
贡献
1094 次
宅之契约
0 份
在线时间
2066 小时
注册时间
2015-8-15
发表于 2019-2-20 13:05:24 | 显示全部楼层
没必要做文件映射,你直接根据PE节表把所有节按相对地址对文件偏移进行手动映射就行了,你肯定要学会把解析PE功能移植到驱动编程的。
关于节表,参照IMAGE_SECTION_HEADER结构体。节表位于NT头后面,数量在文件头里。
回复 赞! 靠!

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
 楼主| 发表于 2019-2-20 18:28:52 | 显示全部楼层
tangptr@126.com 发表于 2019-2-20 13:05
没必要做文件映射,你直接根据PE节表把所有节按相对地址对文件偏移进行手动映射就行了,你肯定要学会把解析 ...

小弟我清楚一些节表的概念并且在文中遍历了指定EXE的节表中含有的节的数目和名称。我当时看PE文件结构图的时候看到数据目录表(DataDirectory[1])是描述输入表的,我就用的数据目录表来找输入表,期间用过一些方法,比如RVA和文件偏移(FileOffset)相互转化,然后用读写文件的方式来读写输入表,但是要考虑到磁盘和内存的字节对齐,最后还是感觉用文件映射和ImageRvaToVa来找输入表对我来说比较简单的。从节表来找输入表之前一直没有想到过,我得查查资料学习一下。谢谢tangptr大佬指导orz
Passion Coding!
回复 赞! 靠!

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
 楼主| 发表于 2019-2-20 18:52:43 | 显示全部楼层
tangptr@126.com 发表于 2019-2-20 13:05
没必要做文件映射,你直接根据PE节表把所有节按相对地址对文件偏移进行手动映射就行了,你肯定要学会把解析 ...

有道理,我刚查了一下输入表信息一般在.idata节里面,可以试着找一下.idata节来获取输入表
Passion Coding!
回复 赞! 靠!

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
 楼主| 发表于 2019-2-21 00:50:31 | 显示全部楼层
附上用文件读写来找输入表的方法:
步骤1:循环扫描区块表,找到输入表在哪个区块中。用区块的RVA(pImageSectionHeader[i].VirtualAddress)作为start,用start与区块的大小(pImageSectionHeader[i].SizeOfRawData)的加和作为end,判断输入表的RVA是否在这个之间(start<=IID.VirtualAddress<=end),如果在这个区间之间,则说明输入表在当前这个区块中。
步骤2:用输入表的RVA(IID.VirtualAddress)减去start,得到输入表RVA相对于起始地址的偏移量RVA2
步骤3:用当前区块的磁盘文件偏移地址(pImageSectionHeader[i].PointerToRawData)加上RVA2就得到了输入表在磁盘文件上的偏移地址,接下来读写IID即可

小弟只上一段寻找输入表的程序了,一些结构体的成员获取见正文哈:
  1. iidrva = ImageNtHeader.OptionalHeader.DataDirectory[1].VirtualAddress; // IID的RVA
  2.         // 寻找.idata节
  3.         DWORD rva2;
  4.         for (int i = 0; i < ImageNtHeader.FileHeader.NumberOfSections; i++)
  5.         {
  6.                 DWORD begin = pImageSectionHeader[i].VirtualAddress;
  7.                 DWORD end = begin + pImageSectionHeader[i].SizeOfRawData;
  8.                 if (iidrva >= begin && iidrva <= end)                // 判断输入表在哪个区块中
  9.                 {
  10.                         rva2 = iidrva - begin;
  11.                         FileOffset.QuadPart = rva2 + pImageSectionHeader[i].PointerToRawData; // 输入表在磁盘文件中的偏移

  12.                         SetFilePointerEx(hFile, FileOffset, NULL, FILE_BEGIN);
  13.                         // 读取IID
  14.                         ReadFile(hFile, &iid,
  15.                                 sizeof(IMAGE_IMPORT_DESCRIPTOR),
  16.                                 &dwRetSize,
  17.                                 NULL);
  18.                         printf("OriginalFirstThunk:%X\n", iid.OriginalFirstThunk);  // 打印OriginalFirstThunk
  19.                         printf("FirstThunk:%X\n", iid.FirstThunk);                                        // 打印FirstThunk
  20.                 }
  21.         }
复制代码


运行结果:(与PEiD的信息进行对比)
1.png
Passion Coding!
回复 赞! 靠!

使用道具 举报

1110

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24221 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
发表于 2019-2-22 00:56:24 | 显示全部楼层
你改了Stub后还没有测试。我想看你在DOSBOX等环境下测试运行它的时候显示的内容的截图。
回复 赞! 靠!

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
 楼主| 发表于 2019-2-22 17:20:07 | 显示全部楼层
本帖最后由 watermelon 于 2019-2-22 17:21 编辑
0xAA55 发表于 2019-2-22 00:56
你改了Stub后还没有测试。我想看你在DOSBOX等环境下测试运行它的时候显示的内容的截图。 ...


报告!我只是在文中改了stub后用winhex看了看并且在win10上运行了一下可以运行(显示“hello world”),但是dosbox上不能运行32位程序,他显示“Illegal command”,而16位exe我用winhex看感觉不是一个PE格式,我网上查了是一个NE格式。Orz
Passion Coding!
回复 赞! 靠!

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-18 11:58 , Processed in 0.048862 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表