• 2007年01月29日

    黑客技巧谈之EXE程序的自删除实现

    黑客技巧谈之EXE程序的自删除实现

    程序的自删除已经不是什么新鲜的话题了,它广泛运用于木马、病毒中。试想想,当你的程序还在运行中(通常是完成了驻留、感染模块),它就自动地把自己从磁盘中删掉,这样一来,就做到了神不知鬼不觉,呵呵,是不是很cool呢?

    自删除(Self Deleting)最早的方法是由 Gary Nebbett 大虾写的,太经典了,不能不提。程序如下:

    #include "windows.h"

    int main(int argc, char *argv[])
    {
    char buf[MAX_PATH];
    HMODULE module;

    module = GetModuleHandle(0);
    GetModuleFileName(module, buf, MAX_PATH);
    CloseHandle((HANDLE)4);

    __asm
    {
    lea eax, buf
    push 0
    push 0
    push eax
    push ExitProcess
    push module
    push DeleteFile
    push UnmapViewOfFile
    ret
    }

    return 0;
    }

    试试编译它,运行。怎么样?从你的眼皮底下消失了吧?是不是很神奇?

    Gary Nebbett 钻了系统的一个漏洞,他的程序是关闭了 exe 文件的 IMAGE(硬编码为4),然后用 UnmapViewOfFile 解除了 exe 文件在内存中的映象,接着通过堆栈传递当前程序的 Handle 给 DeleteFile() ,实现了程序的自删除。

    Gary Nebbett 果然不愧为 WIN 系统下顶尖的底层高手。那么是否还有其他的方法实现程序的自删除呢?答案是肯定的。

    在 Win9x/ME 下,还可以利用 WININIT.INI 的一些特性。在 WININIT.INI 文件里面有一个节 [Rename] ,只要在里面写入要 “Nul=要删除的文件”,那么下次系统重新启动的时候,该文件就会被自动删除了。以下是一个例子:

    [Rename]
    NUL=c:\SelfDelete.exe

    利用这个特性,我们就可以在程序中对这个 ini 文件进行操作。值得注意的是,当需要自删除的文件多于一个的时候,就不能使用 WritePrivateProfileString 来实现,因为这个 API 会阻止多于一个“NUL=”这样的入口在同一个节里面出现,所以最好还是自己手动实现。

    第三种方法是利用批处理文件。先让我们做一个试验:

    创建一个 a.bat ,给它写入以下内容:

    del %0.bat

    现在运行它吧,屏幕一闪而过,最后留下一串字符:“The batch file cannot be found”。这时候它已经从你的硬盘中消失了。

    这说明,批处理文件是可以删除自己的,于是我们可以把这个小技巧运用在自己的程序当中:

    :Repeat
    del "C:\MYDIR\SelfDelete.exe"
    if exist "SelfDelete.exe" goto Repeat
    rmdir "C:\MYDIR"
    del "\DelUS.bat"

    它会重复不断地搜索是否有 SelfDelete.exe 这个文件,直到删除了它为止;当删除完毕后,这个批处理文件就会把自己删除。
    (注:本方法可以支持所有的 Windows 版本,即 Win9x/Me/NT/2000/XP)

    用批处理文件的方法有一个缺陷,就是会突然弹出一个 DOS 窗,冷不防的吓人一跳,不过据我所知这是目前唯一可以在 WinXP 下起作用的方法。当然,最理想的方法是用 Gary Nebbett 的那种,不过它的缺陷是没法在 WinXP 下起作用。

    系统安全之菜鸟也能搞定C++内存泄漏

    背景

    C++内存分配与释放均由用户代码自行控制,灵活的机制有如潘多拉之盒,即让程序员有了更广的发挥

    空间,也产生了代代相传的内存泄漏问题。对于新手来说,最常犯的错误就是new出一个对象而忘记释放,对于一般小应用程序来说,一点内存空间不算什么。但是当内存泄漏问题出现在需要24小时运行的平台类程序上的时候,将会使系统可用内存飞速减少,最后耗尽系统资源,导致系统崩溃。

    所以学会如何防止并检查内存泄漏,是一个合格的c++程序员必须具备的能力。但是由于内存泄漏是程序运行并满足一定条件时才会发生,直接从代码中查出泄漏原因的难度较大,而且一旦内存泄漏发生在多线程程序中,从大量的代码中要靠人工找出泄漏原因,无论对新人还是老手都是一场噩梦

    本文介绍一种在VS2003中检查内存泄漏的方法,供各位新人老手参考,在VC6中实现需要做一些变动,详情可自行参照相关资料。

    检查策略分析

    首先,假定我们需要检测一个24小时运行的平台程序的内存泄漏情况,我们无法确定具体的内存泄漏速度,但是我们可以确定该程序在一定时间内(如10分钟)泄漏的内存量是接近的,设为L(eak)。

    考虑在10分钟的运行时间内程序新申请到的内存A(lloc),这部分内存其实包含了程序运行正常申请,并会在后续运行中进行释放的普通内存块N(ormal)和泄漏的内存L,即:A = N + L

    在后续的运行中,由于N部分不断的申请和释放,所以这部分的总量基本上是不变的,而L部分由于只申请而不释放,占用的内存总量将会越来越大。

    将这个结果放到运行时间轴上,现在我们观察程序运行中的20分钟,我们假定内存泄漏速度为dL/10分钟,时间轴如下:

    ----------------|--------------------|-------------------|----------------------------
    Tn-2 Tn-1 Tn

    三点间隔均为10分钟,则我们有如下结论:

    Tn点总的内存分配量 An = N + dL * n,N为正常分配内存,dL*n为内存泄漏量的总和,而Tn-1点的内存总量则为 An-1 = N + dL*(n-1)。注意,我们这里不考虑释放的内存量,仅考虑增加的内存量。因此很明显单位时间内的内存泄漏量 dL = An - An-1。

    生成内存Dump文件的代码实现

    要完成如上的策略,我们首先需要能跟踪内存块的分配与释放情况,并且在运行时将分配情况保存到文件中,以便进行比较分析,所幸m$已经为我们提供了一整套手段,可以方便地进行内存追踪。具体实现步骤如下:

    包含内存追踪所需库

    在StdAfx.h中添加如下代码,注意必须定义宏_CRTDBG_MAP_ALLOC,否则后续dump文件将缺少内存块的代码位置。

    #ifdef _DEBUG
    //for memory leak check
    #define _CRTDBG_MAP_ALLOC //使生成的内存dump包含内存块分配的具体代码为止
    #include
    #include
    #endif

    启动内存追踪

    上述步骤完成后,则可以在应用程序启动处添加如下代码,启动内存追踪,启动后程序将自动检测内存的分配与释放情况,并允许将结果输出。

    //enable leak check
    _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG);

    将结果输出指向dump文件

    由于默认情况下,内存泄漏的dump内容是输出到vs的debug输出窗口,但是对于服务类程序肯定没法开着vs的debug模式来追踪内存泄漏,所以必须将dump内容的输出转到dump文件中。在程序中添加如下部分:

    HANDLE hLogFile;//声明日志文件句柄
    hLogFile = CreateFile("./log/memleak.log", GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ,
    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建日志文件
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);//将warn级别的内容都输出到文件(注意dump的
    报告级别即为warning)
    _CrtSetReportFile(_CRT_WARN, hLogFile);//将日志文件设置为告警的输出文件

    保存内存Dump

    完成了以上的设置,我们就可以在程序中添加如下代码,输出内存dump到指定的dump文件中:

    _CrtMemState s1, s2, s3;//定义3个临时内存状态
    ......
    _CrtDumpMemoryLeaks();//Dump从程序开始运行到该时刻点,已分配而未释放的内存,即前述An
    //以下部分非必要,仅为方便后续分析增加信息
    _CrtMemCheckpoint( &s2 );
    if ( _CrtMemDifference( &s3, &s1, &s2) )
    {
    _CrtMemDumpStatistics( &s3 );//dump相邻时间点间的内存块变化
    //for next compare
    _CrtMemCheckpoint( &s1 );
    }
    time_t now = time(0);
    struct tm *nowTime = localtime(&now);
    _RPT4(_CRT_WARN,"%02d %02d:%02d:%02d snapshot dump.\n",
    nowTime->tm_mday, nowTime->tm_hour,nowTime->tm_min,nowTime->tm_sec);//输出该次dump时间

    以上代码最好放在一个函数中由定时器定期触发,或者手动snapshot生成相等时间段的内存dump。

    dump文件内容示例如下:

    Detected memory leaks!
    Dumping objects ->
    {20575884} normal block at 0x05C4C490, 87 bytes long.
    Data: 02 00 1D 90 84 9F A6 89 00 00 00 00 00 00 00 00
    ...
    d:\xxxxx\xxxworker.cpp(903) : {20575705} normal block at 0x05D3EF90, 256 bytes long.
    Data: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ...
    Object dump complete.
    0 bytes in 0 Free Blocks.
    215968 bytes in 876 Normal Blocks.
    0 bytes in 0 CRT Blocks.
    0 bytes in 0 Ignore Blocks.
    0 bytes in 0 Client Blocks.
    Largest number used: 220044 bytes.
    Total allocations: 7838322 bytes.
    10 16:29:14 snapshot dump.

    上面红色部分即为用户代码中分配而未释放的内存块位置。

    解析Dump文件

    前面我们已经通过dump文件获取到各时刻点的内存dump,根据前面的分析策略,我们只需要将第n次dump的内存块分配情况An,与第n-1次dump内存块分配情况An-1作比较,即可定位到发生内存泄漏的位置。由于dump文件一般容量巨大,*人工进行对比几乎不可能,所以仅介绍比较的思路,各位需要自行制作小工具进行处理。

    1、提取两个相邻时间点的dump文件D1和D2,设D1是D2之前的dump

    2、各自提取dump文件中用户代码分配的内存块(即有明确代码位置,而且为normal block的内存块),分别根据内存块ID(如“d:\xxxxx\xxxworker.cpp(903) : {20575705}”红色部分)保存在列表L1和L2

    3、遍历列表L2,记录内存块ID没有在L1中出现过的内存块,这些内存块即为可能泄漏的内存

    4、根据3的结果,按照内存的分配代码位置,统计各处代码泄漏的内存块个数,降序排列,分配次数越多的代码,内存泄漏可能性越大。


    收藏到:Del.icio.us





    评论

  • 杭州仁禾雕塑艺术有限公司位于杭州市临平高速公路口,是以创作、设计、制作城市不锈钢雕塑、锻铜、铸铜雕塑、石雕、玻璃钢雕塑等为主的现代高科技艺术性企业。本公司以雕塑为本,以艺术为根,挟中华文化之传统,偕欧陆艺术之风韵,风格独特,尽显淋漓尽致。产品美仑美奂、栩栩如生、令人叹为观止。http://www.renheds.cn

发表评论

您将收到博主的回复邮件
记住我