用C语言写被导入EXE的代码片段

 

用过CFF Explorer之后,发现它可以给EXE添加Seciton,甚至还可以直接导入一个文件作为EXE中一个Section,功能好赞。配合OllyDBG来对现有软件的EXE做一些小小的hack感觉效果很好。

最早的时候我是直接用OllyDBG打开EXE,然后找到一片都是0的地方来汇编代码进去。但是这样受到的限制很多,最直接的就比如有的时候找不到一堆0的地方,找到也不确定能不能写:有可能这些部分其他地方要用,又或者汇编了代码进去要保存的时候OllyDBG说存不了。现在可以直接自己添加一个Section,不会出现找不到空间的情况了。

既然可以导入文件,那比起直接在OllyDBG里面敲汇编,用Nasm来写汇编代码要方便得很多。在测试导入Nasm写的汇编以后,我就在捉摸着更加方便的事情:用C语言来写。

我选用的是OpenWatcom C编译器,没什么太多理由,正好抓到它而已。它反正也可以生成Raw二进制格式的文件。

研究了一下它的链接器,最终的结果是用这样的参数:

OPTION OFFSET=xxxxxx FORMAT RAW BIN

或者

OUTPUT RAW OFFSET=xxxxxx

一起用就会出一些奇怪的结果。说明文档非常长,不知道是不是哪里漏掉了什么没看到的部分……

一起用的时候有的时候就出现最前面16个字节全部是0,然后后面的代码全部错位,大汗……

比如gimp 2.8的补丁,实际上就是调用一下msvcrt.dll里面的_putenv,给一个LANG=en就完事了。代码如下:


void __stdcall gimppatch(int param);

void callgp()
{
    gimppatch(1);
}

extern "C" void* (__stdcall **fGetModuleHandleA)(const char*);
extern "C" void* (__stdcall **fGetProcAddress)(void*, const char*);

void __stdcall gimppatch(int param)
{
    int* p = &param;
    p--;
    *p = 0x4014b0;

    void* msvcrt = fGetModuleHandleA("msvcrt.dll");
    int (__cdecl *putenv)(const char*) = (int (__cdecl*)(const char*)) fGetProcAddress(msvcrt, "_putenv");
    putenv("LANG=en");
}

函数指针的解释:因为函数地址在导入表中,于是导入表对应位置里保存的事实上就是一个函数指针,然后导入表对应位置的地址( fGetProcAddress 的值)就是指向函数指针的指针。

然后要链接嘛,链接的时候那些符号由另一个文件提供:

section .text
    global _fGetModuleHandleA
    global _fGetProcAddress

section .code
_fGetModuleHandleA:
    dd 0x960b54

_fGetProcAddress:
    dd 0x960b58

于是编译链接:

wpp386 -s gimpPatch.cpp

其中 -s 是防止它检查堆栈溢出啥的,因为会导致一个CHK链接不上

然后另一边就直接

nasm -f win32 gimpImp.asm

链接即可:

wlink f gimpPatch.obj f gimpImp.obj form raw bin  op off=0x98a000 name gimpPatch.bin

其中98a000是看了gimp-2.8.exe的section,把最后一个section的起始地址加上大小得到要增加的新section的起始地址:0x589000 + 0x200 = 0x589200,然后新section要对齐到4k边界,就变成0x58a000了。然后加上加载的基址0x400000得到0x98a000。(话说我发现,这参数好像还有顺序问题的样子……)

用CFF Explorer导入以后,找到之前C语言callgp函数,就留下push 1和call,前面的全部nop掉。之所以要弄一个callgp函数,是为了gimppatch执行完毕之后能够正确跳回exe入口,于是把gimppatch的返回地址给改成gimp的原入口处。之后改一下EXE的入口,到callgp函数所在位置,修改就结束了。

 

 

追记:

1、wlink的参数似乎是有顺序的,注意那个warning,可以知道offset参数到底有没有正常

2、文章中提到的方法,调用参数的时候都是用的函数指针,很蛋疼。如果想直接#incude <windows.h>的话,那个asm文件要稍微改一改。比如这个

section .text
    global __imp__WriteConsoleW@20
    global __imp__HeapFree@12
    global __imp__MultiByteToWideChar@24
    global __imp__HeapAlloc@12
    global __imp__GetProcessHeap@0
    global __imp__VirtualProtect@16
    global __imp__GetProcAddress@8
    global __imp__GetModuleHandleW@4
    global __imp__RtlMoveMemory@12
 
section .data
    __imp__WriteConsoleW@20 equ 0x60a143
    __imp__HeapFree@12 equ 0x60a14b
    __imp__MultiByteToWideChar@24 equ 0x60a163
    __imp__HeapAlloc@12 equ 0x60a147
    __imp__GetProcessHeap@0 equ 0x60a14f
    __imp__VirtualProtect@16 equ 0x60a157
    __imp__GetProcAddress@8 equ 0x60a153
    __imp__GetModuleHandleW@4 equ 0x60b0d4
    __imp__RtlMoveMemory@12 equ 0x60a15b

 

 

 

 

 

 

发表评论