VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

有编程疑问吗?还请到提问专区发帖提问!
搜索
查看: 1185|回复: 4

[技术文章] 一个Hello World引发的故事

[复制链接]
001
51_avatar_middle
online_vip 小逸 发表于 2018-2-19 17:24:32 | 显示全部楼层 |阅读模式
本帖最后由 小逸 于 2018-2-19 17:29 编辑

一.概述
  • 转眼间就在科锐步入了第三阶段,近日课堂上钱老师从一个简单的Hello World 引发出一系列BUG,并带着我们修复BUG.这些问题设计到了开发,脱壳,调试技巧等一系列知识点,在解决这些问题的过程中我不仅学到了脱壳,调试的技巧,更是被逆向的艺术给折服.现在我就来将这个题目和我个人解决问题的过程分享给VC驿站的各位朋友.此文虽力求完美,却因小弟才疏学浅难免有不足之处,希望各位朋友点评,让小弟进步.

1.使用VC6编译出的obj文件与汇编联合编译.因为VC6使用的kernel32.LIB和masm32使用的kernel32.LIB中对GetEnvironmentStrings这个函数的声明不一致遇到第一个错误.
2.使用工具inc2l将masm32中的kernel32.inc重新打包为kernel32.lib,因为环境变量的原因,遇到第二个问题:不成功且没有任何提示.于是打算分析inc2l.
3.分析之前发现inc2l加壳,于是使用ESP定律脱壳.
4.脱壳Dump后程序导入表被毁掉了.所以使用impREC重建导入表.(一定几率会触发一个BUG.).impREC会添加新节(mackt)存放节表,有的样本新增节表的位置刚好超过了0x200.ImpREC并没有修改PE中的SizeOfHeaders的大小0x200.(本人环境没触发,所以无法体现)

二.环境
  • Microsoft Visual 6.0
  • RadASM
  • Windows 7
三.C工程3.1 新建VS工程,编写函数
  • 使用VC新建控制台工程TestHello,编写函数ShowHello()输出Hello World.
    1. #include <stdio.h>

    2. //为方便汇编调用,使用 extern "C" 名称粉碎
    3. extern "C" void fnShowHello();

    4. void fnShowHello()
    5. {
    6.     printf("Hello World.\r\n");
    7. }
    复制代码

3.2 查看.obj文件
  • 在工程目录下Debug文件夹中查看Test.obj文件,并将滚动条拖动到尾部,查看ShowHello()函数是否生成.

一个Hello World引发的故事

  • 可以看到我们的函数已经存在.其实从名称中可以看出调用约定, cdecl 一般都喜欢在函数名前加下划线,而 stdcall 则喜欢在函数名后加@与参数字节总和,例如我在函数中加个Messagebox,编译后再查看Test.obj.

一个Hello World引发的故事

四.汇编工程4.1 新建汇编控制台工程
  • 使用RadASM新建汇编控制台工程TestBug后,将C语言TestHello工程中的Test.obj复制到TestBug目录下,并拖到工程中.

一个Hello World引发的故事

4.2 Link错误
  • 尝Link接时报了几个错误,不要灰心,钱老师常教导我们的,有错误是好事,在解决错误的过程中成长.

一个Hello World引发的故事

4.2.1 分析错误一: _main
  • 之所以提示缺少 _main 符号这个错误,是因为在我们的.obj里使用了printf,而 printf 的实现在LIBCD.LIB里.所以我们C语言的工程在生成Test.obj的时候将LIBCD.LIB一起链接.而在LIBCD.LIB里有一个crt0.obj,其中包含了VC6的入口代码.有两种方法可以验证我们的想法.
4.2.1.1 IDA查看
  • 可以将LIBCD.LIB用IDA打开查看其中的crt0.obj.(注意两点,(1)LIBCD.LIB在VC6安装目录->VC98->LIB中. (2)测试的时候发现IDA打开VC安装目录下的LIBCD.LIB时,因为权限问题打开失败.所以将其复制到桌面打开.)

一个Hello World引发的故事

  • 双击crt0.obj进去就会看到入口代码.
    一个Hello World引发的故事
  • 向下翻,会执行一些初始化的工作,如下图:
    一个Hello World引发的故事


4.2.1.2 桟回溯
  • 打开VC工程,通过桟回溯窗口进入 WinMainCRTStartup 函数中.
    一个Hello World引发的故事

  • 可看到 WinMainCRTStartup 函数中会进行一些初始化的工作,如根据定义的宽字节或者多字节的标志选择A版还是W版的获取环境变量函数.
    一个Hello World引发的故事

  • 最后根据条件编译调用到mian函数.
    一个Hello World引发的故事


4.2.2 解决错误一: _main
  • 由上可知由于链接了LIBCD.LIB,而LIBCD.LIB中又有入口函数被链接.而拷贝到RadASM工程中的Test.obj并没有定义入口函数,缺少一个main的实现.而我们链接的流程又需要调用main符号.基于此,只要能检测到main符号,就能解决掉这个错误.有两个解决方案,任选其一都可以解决.
4.2.2.1 修改入口点
  • 方法一,将工程中的程序入口点改为为main:
    一个Hello World引发的故事
4.2.2.2 定义main函数
  • 方法二,定义main的空函数.
    一个Hello World引发的故事
4.2.3 分析错误二: GetEnvironmentStrings
  • 链接时的第二个错误提示没找到GetEnvironmentStrings函数实现,GetEnvironmentStrings的实现是在kernel32.LIB这个库中.VC6使用的kernel32.LIB这个库位于安装目录/VC98/Lib,RadASM链接时使用的kernel32.LIB这个库位于masm32/Lib下.我们分别去看看VC6与RadASM使用GetEnvironmentStrings这个函数的声明.
4.2.3.1 masm32-GetEnvironmentStrings函数声明
  • RadASM使用的GetEnvironmentStrings函数声明位于masm32的include目录下的kernel32.inc中.
    一个Hello World引发的故事

  • 如果定义了 UNICODE,则用GetEnvironmentStrings 替换 GetEnvironmentStringsW.(GetEnvironmentStrings是宏,GetEnvironmentStringsW是真正的函数名.)

  • 如果没定义了 UNICODE,则用GetEnvironmentStrings 替换 GetEnvironmentStringsA.(GetEnvironmentStrings是宏,GetEnvironmentStringsA是真正的函数名.)


4.2.3.2 VC6-GetEnvironmentStrings函数声明

一个Hello World引发的故事

  • 如果定义了 UNICODE,则用GetEnvironmentStrings 替换 GetEnvironmentStringsW.(GetEnvironmentStrings是宏,GetEnvironmentStringsW是真正的函数名.)
  • 如果没定义 UNICODE,则用GetEnvironmentStringsA 替换 GetEnvironmentStrings.(GetEnvironmentStringsA是宏,GetEnvironmentStrings是真正的函数名.)


4.2.3.3 分析结论
  • 通过对比,我们发现了masm32与微软VC6对 GetEnvironmentStrings 这个函数声明不一致导致上文链接错误.那么这个锅该谁背呢?大部分人都会认为微软是巨头,潜意识中都认为以微软为标准,这个错误是masm32的过. 我们再看看微软声明的其他API,看看微软定义API时的风格,如MessageBox.
    一个Hello World引发的故事

  • 如果定义了 UNICODE,则用MessageBoxW 替换 MessageBox.(MessageBox是宏,MessageBoxW是真正的函数名.)

  • 如果没定义 UNICODE,则用MessageBoxA 替换 MessageBox.(MessageBox是宏,MessageBoxA是真正的函数名.)

  • 微软声明的API时大部分情况下都以带W或者带A后缀为函数名.偏偏声明GetEnvironmentStrings却相反.所以,谁的锅,真不好说.


解决错误二: GetEnvironmentStrings
  • 既然发现问题是出于masm32与VC6对 GetEnvironmentStrings 函数声明不一致导致的错误,那么我们将masm32中的函数声明修改与VC6中的函数声明一致,是不是就能解决问题了呢?(修改前记得备份,惨痛的教训...)
    一个Hello World引发的故事

  • 修改后保存,再尝试编译链接.
    一个Hello World引发的故事

  • 为什么依旧报错呢?因为masm32中已经根据没修改前的kernel32.inc打包成kernel32.LIB了.我们必须重新将修改后的kernel32.inc打包,再替换.

  • 在masm32/tools/inc2l中提供了打包工具inc2l.exe.(inc:头文件扩展名 |2: 谐音 to | l:lib ),从inc转成lib文件格式.

  • 将inc2l.exe与修改后的kernel32.inc文件拷贝到D:\Tool,用命令行运行.


一个Hello World引发的故事

  • 并没有如愿产生我们所期待的kernel32.inc.好吧,那下面动手分析下为什么没有产生LIB文件吧.
五.ESP定律脱壳5.1 查壳
  • 用Exeinfo查壳结果如下,可看出程序加了PEcompact壳.ESP定律手动脱壳几乎通杀全部于压缩壳和部分加密壳.(前提是无对抗)
    一个Hello World引发的故事
5.2 寻找OEP
  • 一般的加壳程序的入口处有保存环境的代码,因为要跳到OEP(入口点称为EP,原始入口点称为OEP),势必要在它恢复代码后还原环境,否则会产生不兼容.一般都会将寄存器环境保存到桟中.所以很多壳在程序的伪入口点会看到push之类的代码,就是为了保存环境,那么等它push后,我们找到栈顶的位置,等壳恢复环境时,肯定会读.于是乎,我们找到了脱壳的通用思路,我们通过它保存环境的第一个push.一旦它push后,栈顶保存了初始寄存器的内容.找到栈顶的内存位置,下硬件访问断点.等它读这个数据的时候,那么就离还原寄存器环境很近了.还原完寄存器环境后,就开始跳OEP了.
  • 载入OD
    一个Hello World引发的故事

  • push eax,F8单步步过这条指令.(PUSH后,可看到ESP和EIP数值变化了.ESP定律中,其实变化的只有子模块.)
    一个Hello World引发的故事

  • 选中ESP,右键->数据窗口跟随
    一个Hello World引发的故事

  • 下硬件访问断点,因为它保存了环境,势必会在它恢复代码后还原环境.
    一个Hello World引发的故事

  • 按F9,注意下方触发的断点,直到断在主程序入口.
    一个Hello World引发的故事
    一个Hello World引发的故事

  • 上下翻动,找回复环境的JMP.(一般还原代码后都会有个jmp跳回OEP)
    一个Hello World引发的故事

  • 在JMP处下断点.并取消刚才下的硬件断点.
    一个Hello World引发的故事

  • F7单步过去.,回到OEP.
    一个Hello World引发的故事


5.3 Dump脱壳
  • 右键->OllyDumpEx->Dump.保存为inc2l_dump.exe
    一个Hello World引发的故事
六.impREC修复导入表
  • 上文中脱壳后的程序,运行后崩了.是因为脱壳后IAT还没填写就被dump了.(导入表有一部分被毁掉了.)
    一个Hello World引发的故事

  • 还原的思路如下:因为dump后程序导入表被毁掉了.所以得重建导入表.因此得根据脱壳前程序的导入表还原脱壳后程序的导入表.OD载入inc2l.exe.走到已经被填充IAT的位置.记下IAT的起点:
    一个Hello World引发的故事

  • 找到IAT终点,根据IAT起点算出IAT大小.写个自动化程序,取出其中每一个地址,由写的程序自动识别是哪个函数哪个库,这些信息一旦获取,就可以重新建立一个新的导出表.庆幸的是,这个工具已经有人写出来了,就是impREC.
    一个Hello World引发的故事


6.1 impREC附加活动进程
  • 用OD载入inc2l.exe(Dump前的程序),在impREC中附加进程.(之前一直找不到inc2l.exe的活动进程,感谢张老师的提示,权限问题,右键管理员运行impREC即可)
    一个Hello World引发的故事
6.2 手动定位IAT表信息
  • impREC修复导入表需要IAT的起始位置和大小.上文中我们已经找到IAT起点 :00408050 , IAT 终点 : 0040823B .那么大小就是1EC.
    一个Hello World引发的故事

  • 当然impREC也有自动搜索的功能,但貌似不是很精准,所以,建议手动.
    一个Hello World引发的故事

  • 已经采集到了IAT信息.
    一个Hello World引发的故事


6.3 修复导入表.
  • 点击Fix Dump,选择脱壳后的程序,根据采集的IAT信息重建导入表.
    一个Hello World引发的故事

  • 重建导入表后,并没有覆盖原文件,而是新存为inc2l_dump_.exe.尝试运行,发现已经可以运行.那么我们开始载入OD分析吧.
    一个Hello World引发的故事


七.调试,定位原因.7.1 带参调试
  • 这是控制台程序,需要带命令行参数.一开始直接载入OD才发现是浪费时间.加上参数后再调试.
    一个Hello World引发的故事
7.2 断点
  • 该程序会生成LIB文件,而一般生成Lib文件需要中间文件的支持.因此必然要进行文件操作.(当然不排除变态的做法).因此我们在CreateFileA,CreateFileB(因为不知道调用的是A版还是W版,所以干脆都下断点),ReadFile,WriteFile.然后F9运行.

  • 打开Kernel32.inc.文件句柄为 0x0000007C
    一个Hello World引发的故事



一个Hello World引发的故事

  • 读取Kernel32.inc.
    一个Hello World引发的故事
  • 创建kerenel32.def.文件句柄为 0x00000080
    一个Hello World引发的故事



一个Hello World引发的故事

  • 创建kerenel32.asm.文件句柄为 0x00000084
    一个Hello World引发的故事

一个Hello World引发的故事

  • 多次往Kernel32.asm写数据代码
    一个Hello World引发的故事
  • 往kerenl32.def写代码
    一个Hello World引发的故事

  • 目录下创建了kernel32.asm和kernel32.def文件,并且写入很多数据
    一个Hello World引发的故事


7.3 初步判断
  • 根据以上分析:读inc, 写asm,def文件.按照一般思路,有了asm,def文件,想要生成lib.一般都会使用微软提供的ml/cl程序(至少我是这样的).如果在程序调用这些程序,那么应该会有CreateProcess来创建进程.因此,我们在CreateProcessA,CreateProcessW来创建进程.
    一个Hello World引发的故事

一个Hello World引发的故事


一个Hello World引发的故事

  • 查看MSDN,CreateProcess失败返回值为0.使用 GetLastError 查看错误代码.
    一个Hello World引发的故事
  • 为了查看详细的错误代码,我们写个DEMO程序,模拟inc2l的CreateProcess.输出错误原因.

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <windows.h>

    4. void ShowErrorMsg();
    5. int main(int argc,char *argv[])
    6. {
    7.     char szCommandLine[] = "\\masm32\\bin\\ml /c /coff kernel32.asm";
    8.     STARTUPINFO si = { sizeof(si) };
    9.     PROCESS_INFORMATION pi;
    10.     si.dwFlags = STARTF_USESHOWWINDOW;   //指定wShowWindow成员有效
    11.     si.wShowWindow = TRUE;               //此成员设为TRUE的话则显示新建进程的主窗口
    12.     BOOL bRet = CreateProcess(
    13.         NULL,                            //不在此指定可执行文件的文件名
    14.         szCommandLine,                   //命令行参数
    15.         NULL,                            //默认进程安全性
    16.         NULL,                            //默认进程安全性
    17.         FALSE,                           //指定当前进程内句柄不可以被子进程继承
    18.         NORMAL_PRIORITY_CLASS,           
    19.         NULL,                            //使用本进程的环境变量
    20.         NULL,                            //使用本进程的驱动器和目录
    21.         &si,
    22.         &pi
    23.         );

    24.     if (bRet)
    25.     {
    26.         CloseHandle(pi.hThread);
    27.         CloseHandle(pi.hProcess);
    28.     }

    29.     else
    30.     {
    31.         ShowErrorMsg();
    32.     }

    33.     system("pause");
    34.     return 0;
    35. }

    36. void ShowErrorMsg()
    37. {
    38.     LPVOID lpMsgBuf;
    39.     FormatMessage(
    40.         FORMAT_MESSAGE_ALLOCATE_BUFFER |
    41.         FORMAT_MESSAGE_FROM_SYSTEM |
    42.         FORMAT_MESSAGE_IGNORE_INSERTS,
    43.         NULL,
    44.         GetLastError(),
    45.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    46.         (LPTSTR)&lpMsgBuf,
    47.         0,
    48.         NULL
    49.         );

    50.     MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK | MB_ICONINFORMATION);

    51.     LocalFree(lpMsgBuf);
    52. }
    复制代码

  • 拷贝到inc2l_dump.exe同目录下.发现系统找不到指定文件.是不是CreateProcess中路径参数 \masm32\bin\ml 的错误呢,将DEMO程序中的路径换成相对路径再试试.
    一个Hello World引发的故事

  • 那我们再将DEMO程序和kernel32.asm,拷贝到\masm32\tools\inc2l目录下运行.运行正常还生成了obj文件.
    一个Hello World引发的故事


一个Hello World引发的故事

  • 经过测试,相对路径错误,绝对路径正确.那么说明是环境变量的问题.猜想inc2l中是不是需要哪个环境变量指向\masm32\bin里呢.抱着疑问,我们继续回到OD调试.通过桟回溯找到取环境变量的位置.
    一个Hello World引发的故事
  • 有两个跳转,拼接的命令行参数不同.
    一个Hello World引发的故事



一个Hello World引发的故事

  • 大致跟进去查看后,猜测大致流程如下图:
    一个Hello World引发的故事
7.4 修复
  • 既然是因为取环境变量"mdir",而我们又没有这个环境变量.那是不是加上了环境变量就正常了呢.加个环境变量测试下.
    一个Hello World引发的故事

一个Hello World引发的故事
*说明我们的猜测是正确的.

总结
1. 打开读取参数文件.
2. 创建参数.def文件和参数.asm文件.
3. 根据参数写 参数.def文件和 参数.asm文件
4. 取环境变量"mdir",如果取到则和 "\bin\ml" 与 "bin\link"组合编译链接.如果没有取到环境,则用默认的"masm32\bin\ml 与 "masm32\bin\link"组合编译链接.

  • 因为默认的没有盘符,所以inc2l.exe必须与masm32处于同一盘下才能正常使用.之前我们将inc2l.exe复制到D盘,而masm32安装在C盘,因此没有正确打包成LIB文件.为了验证我们的猜想,我们将 "mdir" 环境变量删除,并在inc2l_dump.exe所在的D盘根目录新建文件夹masm32,在d:\masm32中新建文件夹bin,并且将ml.exe | link.exe拷贝其中.再尝试编译.

一个Hello World引发的故事

  • 我们 已经 从上诉的实验中得知,之所以没有打包成功,是因为编译链接时候找不到ml.exe 与 link.exe. 而我们在安装VC6和masm32后,已经把,ml.exe 与link.exe加入环境变量中,那么我们如果我们将inc2l_dump.exe中 没有取到环境变量"mdir", 则直接使用ml.exe link.exe 编译链接呢?

一个Hello World引发的故事

  • OD中看到默认的"masm32\bin\ml 与 "masm32\bin\link" 全局字符串,那么我们打开WinHex定位到此处.
    一个Hello World引发的故事
  • 注意,此处用的是Pascal,前4字节所以放长度,后面放字符串,0结尾. 所以修改时注意.
    一个Hello World引发的故事


一个Hello World引发的故事

总结修复方案
1.设置环境变量"mdir"
2.保证inc2l.exe与masm32处在同一盘下.
3.修改inc2l.exe二进制,直接使用ml.exe link.exe编译链接.

八.Hello World8.1 替换kernel32.lib
  • 将打包好的kernel32.lib替换C:\masm32\lib\kernel32.lib.
    一个Hello World引发的故事
8.2 入口函数mainCRTStartup.
  • 尝试编译链接已经成功.
    一个Hello World引发的故事

  • 但运行却依旧出问题.
    一个Hello World引发的故事

  • 这是因为我们给RadAsm提供的.obj是VC环境下编译的,因此对应的入口函数也应该改为mainCRTStartup.
    一个Hello World引发的故事

  • 运行编译出的EXE,久违的Hello World.终于出来了.
    一个Hello World引发的故事


九. 尾声
  • 此文断断续续写了一个星期,结笔之时已是大年初一,希望新的一年,VC驿站越办越好,桃李满天下.



发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你已经在论坛发帖求助,并且从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友?可以给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

73_avatar_middle
online_member 变形金刚 发表于 2018-2-19 21:56:06 | 显示全部楼层
楼主新年快乐!支持楼主!

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你已经在论坛发帖求助,并且从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友?可以给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

98_avatar_middle
online_member tomok 发表于 2018-2-20 10:49:41 | 显示全部楼层
好好学习 楼主分享内容!

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你已经在论坛发帖求助,并且从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友?可以给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

60_avatar_middle
online_member 白雪峰去 发表于 2018-4-11 22:34:57 | 显示全部楼层
学习了~~~~

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你已经在论坛发帖求助,并且从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友?可以给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入驿站 qq_login

本版积分规则

关闭

站长提醒上一条 /1 下一条

QQ
QQ在线咨询
联系电话
13591366679
手机扫一扫 关注本站精彩内容
wxqrcode

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号tongdun|网站地图wx_jqr

GMT+8, 2018-5-24 09:54

Powered by Discuz! X3.4

© 2009-2018 cctry.com

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