欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CyxvcProtect加壳程序研究与改进

程序员文章站 2022-07-15 14:38:15
...

花了2天时间读了一份完整的PE文件加壳源码,源码比较基础,没有使用代码偷取技术也没有使用反调试的应用。作为学习加壳程序编写的典型项目非常好用,原作者编码习惯非常棒,条例十分清晰,非常适合初学者学习。所以我在一边学习他的编写思路的情况下一边学习加壳技术,后续学习到的新技术都会在这个代码的基础上加以改进。
这篇博文写之前已经完成了一些较为简单的改进。
Github项目地址:CyxvcProtect
原版链接:看雪学院———-用C++实现的壳(基础版)
1.简单整理了下原版的加壳流程
CyxvcProtect加壳程序研究与改进
2.在源码的基础上做了点图标UI细节的改进,增加了一个反调试选项
CyxvcProtect加壳程序研究与改进CyxvcProtect加壳程序研究与改进
3.添加反调试机制(可选)
3环下获取进程列表,进行敏感进程比对(反调试,反沙箱,反虚拟机)
获取自身全路径,进行敏感词检测
调试状态检测

反调试部分源码直接使用了我的ZeroNet最新版的反调试代码,部分摘要如下,完整代码请移步GitHub

void AntiDebug() {
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);
    HANDLE hprocesssnapshot = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE != hprocesssnapshot) {
        BOOL bprocess = g_pfnProcess32First(hprocesssnapshot, &pe32);
        while (bprocess) {
            if (find_debuger((char*)pe32.szExeFile)) {
                g_pfnExitProcess(0);
            }
            bprocess = g_pfnProcess32Next(hprocesssnapshot, &pe32);
        }
    }

    char filename[MAX_PATH];
    if (g_pfnGetModuleFileNameA(NULL,(LPTSTR)filename,MAX_PATH)) {
        if (find_virus_path(filename)) {
            g_pfnExitProcess(0);
        }
    }

    if (g_pfnIsDebuggerPresent()) {
        g_pfnExitProcess(0);
    }
}

bool find_debuger(const char *processname) {
    for (int i = 0; i<4; i++) {
        char *s = debug_name[i];
        while (*processname != '\0' && *processname == *s) {
            processname++;
            s++;
        }
        if (*processname == '\0' && *s == '\0') return true;
    }
    return false;
}

bool find_virus_path(const char *processname) {
    const char *ori = processname;
    for (int i = 0; i<7; i++) {
        processname = ori;
        while (*processname != '\0') {
            char *s = path_name[i];
            while (*processname != *s && *processname != (*s) - 32 && *processname != '\0') {
                processname++;
            }
            while (*processname != '\0' && (*processname == *s || *processname == (*s) - 32)) {
                processname++;
                s++;
            }
            if (*s == '\0') {
                return true;
            }
        }
    }
    return false;
}

Others:
1.为啥Windows 10下用GetProcAddress获取不到GetModuleFileName这个函数呢,修改为GetModuleFileNameA才成功
2.加壳后的程序的输入表是空的,DLL中的函数是通过手动实现GetProcAddress然后动态获取的,PE中的输入表IAT都是DLL启动后修复的,所以这里我尝试手动修改加壳后的PE文件的输入表指向附加到PE的dll的输入表,这样就不需要手动实现GetProcAddress函数也避免了调用API总是需要动态获取的麻烦了,手动尝试成功,没问题。还没考虑好怎么把这些手动操作写入源程序自动完成,后面再写
3.加壳后的程序在xp下运行不成功,Windows 10下没问题。经过调试定位问题在GetKernel32Addr函数处
这个函数获取kernel32.dll的方式只适用于windows7及以上,xp下获取方式稍有不同
windows 7+获取kernel32.dll的方法

            mov eax, dword ptr fs : [0x30]   // eax = PEB的地址
            mov eax, [eax + 0x0C]            // eax = 指向PEB_LDR_DATA结构的指针
            mov eax, [eax + 0x1C]            // eax = 模块初始化链表的头指针InInitializationOrderModuleList
            mov eax, [eax]                   // eax = 列表中的第二个条目
            mov eax, [eax]                   // eax = 列表中的第三个条目
            mov eax, [eax + 0x08]            // eax = 获取到的Kernel32.dll基址
            mov dwKernel32Addr, eax

修改为如下代码即可兼容xp

            mov eax, dword ptr fs : [0x30]   // eax = PEB的地址
            mov eax, [eax + 0x0C]            // eax = 指向PEB_LDR_DATA结构的指针
            mov eax, [eax + 0x1C]            // eax = 模块初始化链表的头指针InInitializationOrderModuleList
            mov eax, [eax]                   // eax = 列表中的第二个条目
            mov eax, [eax + 0x08]            // eax = 获取到的Kernel32.dll基址
            mov dwKernel32Addr, eax