CyxvcProtect加壳程序研究与改进

花了2天时间读了一份完整的PE文件加壳源码,源码比较基础,没有使用代码偷取技术也没有使用反调试的应用。作为学习加壳程序编写的典型项目非常好用,原作者编码习惯非常棒,条例十分清晰,非常适合初学者学习。所以我在一边学习他的编写思路的情况下一边学习加壳技术,后续学习到的新技术都会在这个代码的基础上加以改进。
这篇博文写之前已经完成了一些较为简单的改进。
Github项目地址:CyxvcProtect
原版链接:看雪学院———-用C++实现的壳(基础版)
1.简单整理了下原版的加壳流程
加壳流程
2.在源码的基础上做了点图标UI细节的改进,增加了一个反调试选项
原UI新UI
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

猜你喜欢

转载自blog.csdn.net/joliph/article/details/80376344