首页
社区
课程
招聘
[原创]一个HelloWorld引出的故事
发表于: 2018-4-14 12:18 9020

[原创]一个HelloWorld引出的故事

2018-4-14 12:18
9020

近日在科锐学完了汇编课程,练手之余发现了由VC6编译出obj文件与汇编联合编译时候引发的BUG,在解决的过程中再次发现另外两个BUG.此文由笔记整理而成,虽力求完美,却因小弟才疏学浅难免有不足之处,希望各位朋友点评,让小弟进步.这篇笔记由初见BUG,ESP定律脱壳,重建导入表,调试分析inc2l,修复inc2l这几部分构成.笔记结构如下:

使用RadAsm新建汇编控制台工程TestBug后,将C语言TestHello工程编译的Test.obj文件复制到TestBug目录下,并拖到工程中.如图1.
图片描述

Link的时候提示两个错误,如图2:
图片描述

将LIBCD.LIB(LIBCD.LIB在VC6安装目录 > VC98 > LIB中)复制到桌面.(因权限问题本人在安装目录下打开失败,所以将其复制到桌面打开.)如图3.
图片描述

双击crt0.obj进去,会看到入口代码,如图4.
图片描述

向下翻,会执行一些初始化的工作,如图5.
图片描述

由上可知是因为Link了LIBCD.LIB,而LIBCD.LIB中又由入口函数被LINK.但拷贝到RadAsm工程中的Test.obj并没有定义入口函数,缺少一个main的实现.而Link的流程又需要调用main符号.基于此,只要能检测到main符号,就能解决掉这个错误.有两个解决方案:

解决方案一:定义main的空函数:(如图7)
图片描述

解决方案二:将工程中的程序入口点改为main:(如图8)
图片描述

图片描述

既然发现问题是出于masm32与VC6对 GetEnvironmentStrings 函数声明不一致导致的错误,那么我们将masm32中的函数声明修改与VC6中的函数声明一致,是不是就能解决问题了呢?(修改前记得备份,惨痛的教训…)(如图12)
图片描述

修改后保存,再次Link,却依旧报错.因为因为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,用命令行运行.(如图13)
图片描述

并没有如愿生成我们所期待的kernel32.lib.好吧,那下面动手分析下为什么没有产生lib文件吧.

分析之前习惯性的用用Exeinfo查壳,发现程序加了PEcompact壳.ESP定律手动脱壳几乎通杀全部于压缩壳和部分加密壳.(前提是无对抗)(如图14)
图片描述

一般的加壳程序的入口处有保存环境的代码,因为要跳到OEP(入口点称为EP,原始入口点称为OEP),势必要在它恢复代码后还原环境,否则会产生不兼容.一般都会将寄存器环境保存到桟中.所以很多壳在程序的伪入口点会看到push之类的代码,就是为了保存环境,那么等它push后,我们找到栈顶的位置,在壳恢复环境时,肯定会读.于是乎,我们找到了脱壳的通用思路,我们通过它保存环境的第一个push.一旦它push后,栈顶保存了初始寄存器的内容.找到栈顶的内存位置,下硬件访问断点.等它读这个数据的时候,那么就离还原寄存器环境很近了.还原完寄存器环境后,就开始跳OEP了.

载入OD(如图15)
图片描述

push eax,F8单步步过这条指令.(PUSH后,可看到ESP和EIP数值变化了.ESP定律中,其实变化的只有子模块.)(如图16)
图片描述

选中ESP,右键>数据窗口跟随(如图17)
图片描述

下硬件访问断点,因为它保存了环境,势必会在它恢复代码后还原环境.(如图18)
图片描述

按F9,注意下方触发的断点,直到断在主模块.(如图19)
图片描述

上下翻动,找回复环境的JMP.(一般还原代码后都会有个jmp跳回OEP)(如图20)
图片描述

在JMP处下断点.并取消刚才下的硬件断点.(如图21)
图片描述

F7单步过去.,回到OEP.(如图22)
图片描述

右键 ->OllyDumpEx -> Dump 保存为inc2l_dump.exe(如图23)
图片描述

双击脱壳后的程序,运行后却C05异常.这是因为是因为脱壳后IAT还没填写就被dump了.(导入表有一部分被毁掉了.)(如图24)
图片描述

还原的思路:因为dump后程序导入表被毁掉了.所以得重建导入表.因此得根据脱壳前程序的导入表还原脱壳后程序的导入表.OD载入inc2l.exe.走到已经被填充IAT的位置.记下IAT的起点与终点:(如图25,26)
图片描述
图片描述

IAT表起点:0x00408050 IAT表终点:0x0040823F 快大小:0x1F0

得到IAT表起点和终点还有IAT表大小后,写个自动化程序,取出其中每一个地址,由写的程序自动识别是哪个函数哪个库,这些信息一旦获取,就可以重新建立一个新的导出表.庆幸的是,这个工具已经有人写出来了,就是impREC.

点击Fix Dump,选择脱壳后的程序inc2l_dump.exe,根据采集的IAT信息重建导入表.成功后同目录下会多出inc2ldump.exe文件.

但却依然没有正常运行.报错 (0x0000007b),这个错误是PE格式的问题.用WinHex打开查看PE结构,发现impREC会添加新节(mackt)存放节表,有的样本新增节表的位置刚好超过了此样本PE中的SizeOfHeaders所限定的大小(200).恰恰ImpREC并没有更新PE中的SizeOfHeaders的大小.估计大多数样本SizeOfHeaders都超过了200.(如图30)
图片描述

将SizeOfHeaders修改成1000以下200以上并且为200的倍数即可.(如图31)
图片描述

该程序会生成LIB文件,而一般生成Lib文件需要中间文件的支持.因此必然要进行文件操作.(当然不排除变态的做法).因此我们在CreateFileA,CreateFileB(因为不知道调用的是A版还是W版,所以干脆都下断点),ReadFile,WriteFile.然后F9运行.

打开Kernel32.inc.文件句柄为 0x0000007C.(如图32,33)
图片描述
图片描述

读取Kernel32.inc.(如图34)
图片描述

创建kerenel32.def.文件句柄为 0x00000080.(如图35,36)
图片描述
图片描述

创建kerenel32.asm.文件句柄为 0x00000084.(如图37,38)
图片描述
图片描述

多次往Kernel32.asm写数据代码.(如图39)
图片描述

往kerenl32.def写代码.(如图40)
图片描述

目录下创建了kernel32.asm和kernel32.def文件,并且写入很多数据.(图41)
图片描述

根据以上行为分析:读inc, 写asm,def文件.按照一般思路,有了asm,def文件,想要生成lib.一般都会使用微软提供的ml/cl程序(至少我是这样的).如果在程序调用这些程序,那么应该会有CreateProcess来创建进程.因此,我们在CreateProcessA,CreateProcessW来创建进程.(如图42,43,44)
图片描述
图片描述
图片描述

为了查看详细的错误代码,我们写个DEMO程序,模拟inc2l的CreateProcess.输出错误原因.

是不是CreateProcess中路径参数 \masm32\bin\ml 的错误呢,将DEMO程序中的路径换成相对路径再试试.再将DEMO程序和kernel32.asm,拷贝到\masm32\tools\inc2l目录下运行.运行正常还生成了obj文件.(如图46,47)
图片描述
图片描述

经过测试,相对路径错误,绝对路径正确.那么说明是环境变量的问题.猜想inc2l中是不是需要哪个环境变量指向\masm32\bin里呢.抱着疑问,我们继续回到OD调试.通过桟回溯找到取环境变量的位置.(如图48)
图片描述

有两个跳转,拼接的命令行参数不同.(如图49,50)
图片描述
图片描述

大致跟进去查看后,猜测大致流程如下图(天生艺术细胞不强,凑合着看吧):(如图51)
图片描述

既然是因为取环境变量"mdir",而我们又没有这个环境变量.那是不是加上了环境变量就正常了呢.加个环境变量测试下.(如图52,53)
图片描述
图片描述

执行流程

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

我们已经从上实验中得知,之所以没有打包成功,是因为编译链接时候找不到ml.exe 与 link.exe. 而我们在安装VC6和masm32后,已经把,ml.exe 与link.exe加入环境变量中(如图55)
图片描述

那么我们在inc2l_dump.exe没有取到环境变量"mdir", 则直接使用ml.exe 和 link.exe 编译链接呢?(即将"masm32\bin\ml 与 "masm32\bin\link" 修改为 "ml" 与 "link").OD中看到默认的"masm32\bin\ml 与 "masm32\bin\link" 全局字符串,那么我们打开WinHex定位到此处.(如图56)
图片描述

注意,此处用的是Pascal,前4字节所以放长度,后面放字符串,0结尾. 所以修改时注意.(如图57)
图片描述

此时再使用inc2l即可正常将inc文件打包成lib文件.(如图58)
图片描述

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

其实这个环境变量的问题也不好认定是否是BUG,说它是Bug,它在帮助里给出了提示,指出了要依赖环境变量mdir.但说它不是BUG,这提示藏得有点深,一般人机子上都不会默认有mdir这个环境变量吧.而且如果因为路径原因调用ml.exe和link.exe失败,至少得给个提示吧,因为XXX原因生成失败...这直接就是空白无任何提示.用户体验相当不好啊.
图片描述

将打包好的kernel32.lib替换C:\masm32\lib\kernel32.lib.(如图59)
图片描述

尝试编译链接已经成功,但运行依旧出问题.(如图60)
图片描述

这是因为我们给RadAsm提供的.obj是VC环境下编译的,因此对应的入口函数也应该改为mainCRTStartup.(如图61)
图片描述

运行编译出的EXE,久违的Hello World.终于出来了.(如图62)
图片描述

VC6使用的kernel32.lib库路径     : VC6安装目录/VC98/Lib/
RadASM使用的kernel32.lib库路径  : masm32/Lib
如果定义了 UNICODE
则用GetEnvironmentStrings 替换GetEnvironmentStringsW.
(GetEnvironmentStrings是宏,GetEnvironmentStringsW是真正的函数名.)

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

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

如果没定义 UNICODE
则用MessageBoxA 替换 MessageBox.
(MessageBox是宏,MessageBoxA是真正的函数名.)
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

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

    if (bRet)
    {
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }

    else
    {
        ShowErrorMsg();
    }

    system("pause");
    return 0;
}

void ShowErrorMsg()
{
    LPVOID lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR)&lpMsgBuf,
        0,
        NULL
        );

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

    LocalFree(lpMsgBuf);
}

一般的加壳程序的入口处有保存环境的代码,因为要跳到OEP(入口点称为EP,原始入口点称为OEP),势必要在它恢复代码后还原环境,否则会产生不兼容.一般都会将寄存器环境保存到桟中.所以很多壳在程序的伪入口点会看到push之类的代码,就是为了保存环境,那么等它push后,我们找到栈顶的位置,在壳恢复环境时,肯定会读.于是乎,我们找到了脱壳的通用思路,我们通过它保存环境的第一个push.一旦它push后,栈顶保存了初始寄存器的内容.找到栈顶的内存位置,下硬件访问断点.等它读这个数据的时候,那么就离还原寄存器环境很近了.还原完寄存器环境后,就开始跳OEP了.

  • 初见BUG
    • 使用VC6编译出的obj文件与汇编联合编译.因VC6使用的kernel32.lib和masm32使用的kernel32.lib对GetEnvironmentStrings这个函数的声明不一致引出第一个BUG.
    • 修复masm32中kernel32.INC后,在使用工具inc2l将kernel32.inc打包为为kernel32.lib时,引出第二个BUG.(inc2l打包不成功,且没有任何提示)
  • ESP定律脱壳
    • 在分析inc2l之前从查壳软件中得知inc2l加了压缩壳,使用ESP定律脱壳.
  • 重建导入表
    • 脱壳后,利用OD的Dump程序后,发现导入表已经被毁.使用工具impREC重建导入表引出了第三个BUG.(impREC会添加新节(mackt)存放节表,有的样本新增节表的位置刚好超过了PE中的SizeOfHeaders所限定的大小.恰恰ImpREC并没有更新PE中的SizeOfHeaders的大小.)(最新版impREC1.7依旧没有修复这个BUG)
  • 分析Inc2l
    • 经过分析,发现inc2l会取环境变量"mdir",如果取到则使用 "\bin\ml" 与 "bin\link"组合编译链接.如果没有取到环境,则使用缺省的"masm32\bin\ml 与 "masm32\bin\link"组合编译链接.但缺省的组合方式没有写明盘符,所以inc2l必须要和masm32在同一盘符下才能正常使用.
  • 修复Inc2l
    • 从上文得知了inc2l出错的由来,可知修复方案为:
      1. 新建环境变量"mdir".
      2. 保证inc2l与masm32处在同一盘符下.
      3. 修改inc2l的二进制,将缺省的组合方式修改为ml.exe 和 link.exe编译链接.
  • 使用VC6编译出的obj文件与汇编联合编译.因VC6使用的kernel32.lib和masm32使用的kernel32.lib对GetEnvironmentStrings这个函数的声明不一致引出第一个BUG.
  • 修复masm32中kernel32.INC后,在使用工具inc2l将kernel32.inc打包为为kernel32.lib时,引出第二个BUG.(inc2l打包不成功,且没有任何提示)
  • 在分析inc2l之前从查壳软件中得知inc2l加了压缩壳,使用ESP定律脱壳.
  • 脱壳后,利用OD的Dump程序后,发现导入表已经被毁.使用工具impREC重建导入表引出了第三个BUG.(impREC会添加新节(mackt)存放节表,有的样本新增节表的位置刚好超过了PE中的SizeOfHeaders所限定的大小.恰恰ImpREC并没有更新PE中的SizeOfHeaders的大小.)(最新版impREC1.7依旧没有修复这个BUG)
  • 经过分析,发现inc2l会取环境变量"mdir",如果取到则使用 "\bin\ml" 与 "bin\link"组合编译链接.如果没有取到环境,则使用缺省的"masm32\bin\ml 与 "masm32\bin\link"组合编译链接.但缺省的组合方式没有写明盘符,所以inc2l必须要和masm32在同一盘符下才能正常使用.
  • 从上文得知了inc2l出错的由来,可知修复方案为:
    1. 新建环境变量"mdir".
    2. 保证inc2l与masm32处在同一盘符下.
    3. 修改inc2l的二进制,将缺省的组合方式修改为ml.exe 和 link.exe编译链接.
  • 使用VC新建控制台工程TestHello,编写函数ShowHello().
  • 使用RadAsm新建汇编控制台工程TestBug后,将C语言TestHello工程编译的Test.obj文件复制到TestBug目录下,并拖到工程中.如图1.
    图片描述

  • Link的时候提示两个错误,如图2:
    图片描述

  • 之所以提示缺少 _Main符号这个错误,是因为在我们的Test.obj里使用了Printr,而Printf的实现在LIBCD.LIB里.所以我们C语言工程在生成Test.obj的时候将LIBCD.LIB一起Link.而在LIBCD.LIB里由一个ctr0.obj,其中包含了VC6的入口代码.我们可以用IDA或者桟回溯来验证猜想.
  • 将LIBCD.LIB(LIBCD.LIB在VC6安装目录 > VC98 > LIB中)复制到桌面.(因权限问题本人在安装目录下打开失败,所以将其复制到桌面打开.)如图3.
    图片描述

  • 双击crt0.obj进去,会看到入口代码,如图4.
    图片描述

  • 向下翻,会执行一些初始化的工作,如图5.
    图片描述

  • 打开VC工程,通过栈回溯窗口进入 WinMainCRTStartup 函数中.
  • 在 WinMainCRTStartup 函数中会进行一些初始化的工作,如根据字符集选择A版或者W版的获取环境变量函数.最后根据条件编译调用wmain或者是main.(如图6).
    图片描述
  • 由上可知是因为Link了LIBCD.LIB,而LIBCD.LIB中又由入口函数被LINK.但拷贝到RadAsm工程中的Test.obj并没有定义入口函数,缺少一个main的实现.而Link的流程又需要调用main符号.基于此,只要能检测到main符号,就能解决掉这个错误.有两个解决方案:

    • 解决方案一:定义main的空函数:(如图7)
      图片描述

    • 解决方案二:将工程中的程序入口点改为main:(如图8)
      图片描述

  • 解决方案一:定义main的空函数:(如图7)
    图片描述

  • 解决方案二:将工程中的程序入口点改为main:(如图8)
    图片描述

  • Link时的第二个错误提示没找到GetEnvironmentStrings函数实现,而GetEnvironmentStrings的实现在在kernel32.lib这个库中.
  • 下面分别去看看VC6与RadASM使用GetEnvironmentStrings这个函数的声明.
    VC6使用的kernel32.lib库路径     : VC6安装目录/VC98/Lib/
    RadASM使用的kernel32.lib库路径  : masm32/Lib
    
  • RadASM使用的GetEnvironmentStrings函数声明位于masm32的include目录下的kernel32.inc中.(如图9)
    图片描述
  • 通过对比,我们发现了masm32与微软VC6对 GetEnvironmentStrings 这个函数声明不一致导致上文链接错误.那么这个锅该谁背呢?大部分人都会认为微软是巨头,潜意识中都认为以微软为标准,这个错误是masm32的锅. 我们再看看微软声明的其他API,看看微软定义API时的风格,如MessageBox.(如图11)
    图片描述
  • 微软声明的API时大部分情况下都以带W或者带A后缀为函数名.偏偏声明 GetEnvironmentStrings 却相反.所以,谁的锅,真不好说.
  • 既然发现问题是出于masm32与VC6对 GetEnvironmentStrings 函数声明不一致导致的错误,那么我们将masm32中的函数声明修改与VC6中的函数声明一致,是不是就能解决问题了呢?(修改前记得备份,惨痛的教训…)(如图12)
    图片描述

  • 修改后保存,再次Link,却依旧报错.因为因为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,用命令行运行.(如图13)
    图片描述

  • 并没有如愿生成我们所期待的kernel32.lib.好吧,那下面动手分析下为什么没有产生lib文件吧.

  • 分析之前习惯性的用用Exeinfo查壳,发现程序加了PEcompact壳.ESP定律手动脱壳几乎通杀全部于压缩壳和部分加密壳.(前提是无对抗)(如图14)
    图片描述

  • 载入OD(如图15)
    图片描述

  • push eax,F8单步步过这条指令.(PUSH后,可看到ESP和EIP数值变化了.ESP定律中,其实变化的只有子模块.)(如图16)
    图片描述

  • 选中ESP,右键>数据窗口跟随(如图17)
    图片描述

  • 下硬件访问断点,因为它保存了环境,势必会在它恢复代码后还原环境.(如图18)
    图片描述

  • 按F9,注意下方触发的断点,直到断在主模块.(如图19)
    图片描述

  • 上下翻动,找回复环境的JMP.(一般还原代码后都会有个jmp跳回OEP)(如图20)
    图片描述

  • 在JMP处下断点.并取消刚才下的硬件断点.(如图21)
    图片描述

  • F7单步过去.,回到OEP.(如图22)
    图片描述

  • 右键 ->OllyDumpEx -> Dump 保存为inc2l_dump.exe(如图23)
    图片描述

  • 双击脱壳后的程序,运行后却C05异常.这是因为是因为脱壳后IAT还没填写就被dump了.(导入表有一部分被毁掉了.)(如图24)
    图片描述

  • 还原的思路:因为dump后程序导入表被毁掉了.所以得重建导入表.因此得根据脱壳前程序的导入表还原脱壳后程序的导入表.OD载入inc2l.exe.走到已经被填充IAT的位置.记下IAT的起点与终点:(如图25,26)
    图片描述
    图片描述

  • IAT表起点:0x00408050 IAT表终点:0x0040823F 快大小:0x1F0

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

  • 用OD载入inc2l.exe(Dump前的程序),在impREC中附加进程.(之前Win7中一直找不到inc2l.exe的活动进程,感谢张老师的提示,权限问题,右键管理员运行impREC即可)(如图27)
    图片描述
  • 当然impREC也有自动搜索的功能,但貌似不是很精准,所以,建议手动定位IAT表信息再填写.(如图28)
    图片描述
  • 上文获取到的信息:IAT表起点:0x00408050 IAT表终点:0x0040823F 快大小:0x1F0,填写后Get Imports获取IAT表信息.(如图29)
    图片描述
  • 点击Fix Dump,选择脱壳后的程序inc2l_dump.exe,根据采集的IAT信息重建导入表.成功后同目录下会多出inc2ldump.exe文件.

  • 但却依然没有正常运行.报错 (0x0000007b),这个错误是PE格式的问题.用WinHex打开查看PE结构,发现impREC会添加新节(mackt)存放节表,有的样本新增节表的位置刚好超过了此样本PE中的SizeOfHeaders所限定的大小(200).恰恰ImpREC并没有更新PE中的SizeOfHeaders的大小.估计大多数样本SizeOfHeaders都超过了200.(如图30)
    图片描述

  • 将SizeOfHeaders修改成1000以下200以上并且为200的倍数即可.(如图31)
    图片描述

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

  • 打开Kernel32.inc.文件句柄为 0x0000007C.(如图32,33)
    图片描述
    图片描述

  • 读取Kernel32.inc.(如图34)
    图片描述

  • 创建kerenel32.def.文件句柄为 0x00000080.(如图35,36)
    图片描述
    图片描述

  • 创建kerenel32.asm.文件句柄为 0x00000084.(如图37,38)
    图片描述
    图片描述

  • 多次往Kernel32.asm写数据代码.(如图39)
    图片描述

  • 往kerenl32.def写代码.(如图40)
    图片描述

  • 目录下创建了kernel32.asm和kernel32.def文件,并且写入很多数据.(图41)
    图片描述

  • 根据以上行为分析:读inc, 写asm,def文件.按照一般思路,有了asm,def文件,想要生成lib.一般都会使用微软提供的ml/cl程序(至少我是这样的).如果在程序调用这些程序,那么应该会有CreateProcess来创建进程.因此,我们在CreateProcessA,CreateProcessW来创建进程.(如图42,43,44)
    图片描述
    图片描述
    图片描述

  • 为了查看详细的错误代码,我们写个DEMO程序,模拟inc2l的CreateProcess.输出错误原因.

  • 将生成的程序拷贝到inc2l_dump.exe同目录下.发现错误原因系统找不到指定文件.(如图45)
    图片描述
  • 是不是CreateProcess中路径参数 \masm32\bin\ml 的错误呢,将DEMO程序中的路径换成相对路径再试试.再将DEMO程序和kernel32.asm,拷贝到\masm32\tools\inc2l目录下运行.运行正常还生成了obj文件.(如图46,47)
    图片描述
    图片描述

  • 经过测试,相对路径错误,绝对路径正确.那么说明是环境变量的问题.猜想inc2l中是不是需要哪个环境变量指向\masm32\bin里呢.抱着疑问,我们继续回到OD调试.通过桟回溯找到取环境变量的位置.(如图48)
    图片描述

  • 有两个跳转,拼接的命令行参数不同.(如图49,50)
    图片描述
    图片描述

  • 大致跟进去查看后,猜测大致流程如下图(天生艺术细胞不强,凑合着看吧):(如图51)
    图片描述

  • 既然是因为取环境变量"mdir",而我们又没有这个环境变量.那是不是加上了环境变量就正常了呢.加个环境变量测试下.(如图52,53)
    图片描述
    图片描述

  • 执行流程

    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拷贝其中.再尝试编译.(如图54)
    图片描述

  • 我们已经从上实验中得知,之所以没有打包成功,是因为编译链接时候找不到ml.exe 与 link.exe. 而我们在安装VC6和masm32后,已经把,ml.exe 与link.exe加入环境变量中(如图55)
    图片描述

  • 那么我们在inc2l_dump.exe没有取到环境变量"mdir", 则直接使用ml.exe 和 link.exe 编译链接呢?(即将"masm32\bin\ml 与 "masm32\bin\link" 修改为 "ml" 与 "link").OD中看到默认的"masm32\bin\ml 与 "masm32\bin\link" 全局字符串,那么我们打开WinHex定位到此处.(如图56)
    图片描述

  • 注意,此处用的是Pascal,前4字节所以放长度,后面放字符串,0结尾. 所以修改时注意.(如图57)
    图片描述

  • 此时再使用inc2l即可正常将inc文件打包成lib文件.(如图58)
    图片描述

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

  • 其实这个环境变量的问题也不好认定是否是BUG,说它是Bug,它在帮助里给出了提示,指出了要依赖环境变量mdir.但说它不是BUG,这提示藏得有点深,一般人机子上都不会默认有mdir这个环境变量吧.而且如果因为路径原因调用ml.exe和link.exe失败,至少得给个提示吧,因为XXX原因生成失败...这直接就是空白无任何提示.用户体验相当不好啊.
    图片描述

  • 将打包好的kernel32.lib替换C:\masm32\lib\kernel32.lib.(如图59)
    图片描述

  • 尝试编译链接已经成功,但运行依旧出问题.(如图60)
    图片描述

  • 这是因为我们给RadAsm提供的.obj是VC环境下编译的,因此对应的入口函数也应该改为mainCRTStartup.(如图61)
    图片描述

  • 运行编译出的EXE,久违的Hello World.终于出来了.(如图62)
    图片描述

最后于 2018-4-14 16:52 被韩逸编辑 ,原因: 使用外链,刚在重装服务器.所以外链挂了...已经换成本地图片.
上传的附件:
收藏
免费 1
支持
分享
最新回复 (15)
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
opj
2
分析得好清晰,技术帖,必须强顶!!!
2018-4-14 12:59
0
雪    币: 9210
活跃值: (1876)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
3
图挂了...
2018-4-14 16:13
0
雪    币: 44153
活跃值: (20195)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
建议图片放在论坛本地,帖图方法https://bbs.pediy.com/thread-225386.htm  让他看看这个
2018-4-14 16:49
0
雪    币: 6818
活跃值: (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2018-4-15 22:36
0
雪    币: 39
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
挺精彩的
2018-4-16 00:28
0
雪    币: 1795
活跃值: (63)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
7
精华帖,羡慕。。。啥时候我也能有一个精华
2018-4-16 02:11
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
OVA
8
新人  感觉在腾云驾雾
2018-4-22 16:05
0
雪    币: 9
活跃值: (175)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
分析的好扎实
2018-4-22 21:28
0
雪    币: 2375
活跃值: (433)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
vC6是不是太老了?
2018-5-28 09:43
0
雪    币: 12628
活跃值: (3127)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
编个程要解决这么多问题,还让不让人好好编程了!
2018-5-29 09:04
0
雪    币: 3723
活跃值: (599)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
14
分析的很透彻,  因为不是做专职开发的,  所以我也很喜欢用VC6,  但是VC6又有各种各样的问题.
比如link的时候卡住了,  又比如插件引起的崩溃,  基本上都需要挂上调试器,  然后把模块里面的代码改了以后dump出来再用.
周围很多同事都是科锐的,  支持一下钱老板,  顶一下科锐.
2018-5-29 10:14
0
雪    币: 1
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
这个精华没毛病,学习了!
2018-10-10 21:37
0
雪    币: 28
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
2018-10-18 16:52
0
游客
登录 | 注册 方可回帖
返回
//