Reverse Code Engineering

逆向工程代码 обратная код машиностроения

Reverse Code Engineering

逆向工程代码 обратная код машиностроения

Reverse Code Engineering

Share what I know............... learn what I don’t

آخرین نظرات

تریس فراخوانی های API

سه شنبه, ۳۰ شهریور ۱۳۹۵، ۱۲:۲۸ ب.ظ

API Call Tracing یکی از روشهایی است که با آن میتوان یک دید کلی و خلاصه از یک فایل اجرایی بدست آورد.

شاید در برخی موارد بررسی فراخوانی های API را مد نظر ما باشد مخصوصا برای فهمیدن رفتار یک Malware .
در اینجا ما در مورد برخی از این تکنیک ها توضیح می دهیم:

1. آنپک کردن برنامه های پک شده
2. نمایش رفتار برنامه
3. پیدا کردن توابعی که برایمان مفید هستند


من از pydbg برای لاگ کردن توابع فراخوانی شده استفاده می کنم, و در آخر هم از IDAPython برای انجام اتوماتیک برخی ازین آنالیزهای دستی.

API Calls Logging with PEfile & PyDbg:


با تکیه بر موارد بالا,ما برای اسکریپتمان به اطلاعات زیر نیازمندیم:
1. آدرس برگشت - از کجا API فراخوانی شده؟
2. نام API - کدام API فراخوانی شده؟

پس ما باید یک breakpoint روی هر فراخوانی تابع قرار بدیم,چونکه به نام API یا آدرس آن احتیاج داریم.اگر ما نام تابع را داشته باشیم از طریق آن میتوانیم آدرس تابع را بدست بیاوریم و نقطه توقف روی آن قرار دهیم, در اینجا, آدرس ما می تواند مستقیما breakpoint روی آن قرار گیرد.اما سوال اینجاست که چطور میتوانیم نام توابع APIرو بدست بیاوریم؟
این مشکل رو ما با PEfile حل کردیم.پس ما اول توابع ورودی رو بدست میاریم و پس از اون با قرار دادن BP روی اینها آدرسهارو نیز بدست میاریم.
اما این روش محدودیت های زیر رو هم دارد:
1. در مورد DLL هایی که از طریق تابع LoadLibrary() لود شوند شکست می خورد.
2. اگر برنامه پک شده باشه پس جدول ورودی در زمان آنپک شدن ایجاد خواهد شد, پس شکست می خورد.

قبل از حل این مشکل اجازه بدید در مورد نحوه ی کار Unpacker Stub یا لودر ها برای ساختن جدول ورودی در هنگام اجرا توضیح مختصر میدم:
به طور معمول آنها از LoadLibrary برای لود کردن DLL و GetProcAddress برای بدست آوردن آدرس API استفاده می کنند.
این 2 تا تابع توسط kernel32.dll اکسپورت شدن, و در داخل هر پروسسی به صورت پیش فرض وجود دارند.
پس اگر ما یک  BP روی GetProcAddress قرار بدیم,نام API از پشته قابل دستبابی است. در ادامه یک BP هم روی آدرس بدست آمده از تابع GetProcAddress قرار می دهیم.
اما روش های دیگری هم برای بازسازی جدول ورودی در هنگام اجرا وجود دارد که توسط بدافزارها مورد استفاده قرار میگیرد.
تو اسمبلی یه چیزی شبیه به کد زیر :


      	push dword ptr fs:[30h] ; PEB
        pop eax
        mov eax,[eax+0ch] ; LDR
        mov ecx,[eax+0ch] ; InLoadOrderModuleList
        mov edx,[ecx]
        push edx
        mov eax,[ecx+30h]

در این روش, لودر ابتدا آدرس مبنای kernel32.dll رو بدست می آورد.
سپس به export table این DLL وارد شده و به دنبال آدرس تابع LoadLibrary میگردد.
پس از این لودر با این تابع همه کتابخانه ی مورد نیاز را لود کرده و با استفاده از روش زیر آدرس API ها را بدست می آورد:
1. GetProcAddress شبیه متد قبلی است
2. حرکت میان export table همه ی DLL های لود شده.


در اینجا ما گام های زیر را برای API Call Tracing انجام میدیم:
1. رفتن به داخل جدول ورودی فایل و قرار دادن BP روی هر API
2. همچنین قرار دادن BP روی تابع GetProcAddress
3. اگر به BP برخورد کردیم و تابع GetProcAddress نبود, پس می توانیم از پشته آدرس برگشت و نام APIرو بدست بیاریم.
4. اگر به تابع GetProcAddress برخوردیم, آدرس برگشت رو از پشته برمیداریم و روی آن BP قرار میدیم.
5. اگر به آدرس برگشت برخوردیم, پس مقدار رجیستر EAX رو بدست میاریم و یک BP روی آن ست میکنیم.

با این روش ما یه اسکریپتی می نویسیم که API رو با آدرس برگشت هارو لاگ می کنه:


import sys,struct
import pefile
from pydbg import *
from pydbg.defines import *

def log(str):
	global fpp
	print str
	fpp.write(str)
	fpp.write("\n")
	return 
	
def addr_handler(dbg):		       
	global func_name
	ret_addr = dbg.context.Eax
	if ret_addr:
		dict[ret_addr] = func_name
		dbg.bp_set(ret_addr,handler=generic)
	return DBG_CONTINUE

def generic(dbg):
	global func_name
	eip = dbg.context.Eip
	esp = dbg.context.Esp
	paddr = dbg.read_process_memory(esp,4)
	addr = struct.unpack("L",paddr)[0]
	addr = int(addr)
	if addr < 70000000:
		log("RETURN ADDRESS: 0x%.8x\tCALL: %s" % (addr,dict[eip])) 
	if dict[eip] == "KERNEL32!GetProcAddress" or dict[eip] == "GetProcAddress": 
		try:
			esp = dbg.context.Esp
			addr = esp + 0x8
			size = 50
			pstring = dbg.read_process_memory(addr,4)
			pstring = struct.unpack("L",pstring)[0]
			pstring = int(pstring)
			if pstring > 500:
				data = dbg.read_process_memory(pstring,size)
				func_name = dbg.get_ascii_string(data)
			else:
				func_name = "Ordinal entry"
			paddr = dbg.read_process_memory(esp,4)
			addr = struct.unpack("L",paddr)[0]
			addr = int(addr)
			dbg.bp_set(addr,handler=addr_handler)
		except:
			pass
	return DBG_CONTINUE


def entryhandler(dbg):
	getaddr = dbg.func_resolve("kernel32.dll","GetProcAddress")  
	dict[getaddr] = "kernel32!GetProcAddress"
	dbg.bp_set(getaddr,handler=generic)
	for entry in pe.DIRECTORY_ENTRY_IMPORT:
		DllName = entry.dll
		for imp in entry.imports:				  
			api = imp.name
			address = dbg.func_resolve(DllName,api)
			if address:
				try:
					Dllname = DllName.split(".")[0]
					dll_func = Dllname + "!" + api
					dict[address] = dll_func
					dbg.bp_set(address,handler=generic)
				except:
					pass
	
	return DBG_CONTINUE		

def main():
	global pe, DllName, func_name,fpp
	global dict
	dict = {}
	file = sys.argv[1]
	fpp = open("calls_log.txt",'a')
	pe = pefile.PE(file)
	dbg = pydbg()
	dbg.load(file)
	entrypoint = pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.AddressOfEntryPoint
	dbg.bp_set(entrypoint,handler=entryhandler)
	dbg.run()
	fpp.close()

if __name__ == '__main__':
	main()
      

و خروجی کد بالا:


    RETURN ADDRESS: 0x004030e8	CALL: kernel32!GetModuleHandleA
    RETURN ADDRESS: 0x004030f3	CALL: kernel32!GetCommandLineA
    RETURN ADDRESS: 0x00404587	CALL: kernel32!GetModuleHandleA
    RETURN ADDRESS: 0x00404594	CALL: kernel32!GetProcAddress
    RETURN ADDRESS: 0x004045aa	CALL: kernel32!GetProcAddress
    RETURN ADDRESS: 0x004045c0	CALL: kernel32!GetProcAddress

حالا چندتا سناریو رو توی دنیای واقعی بررسی می کنیم.


1) Unpacking UPX using API Call Tracing

خروجی زیر از یک فایل پک شده با UPX هستش:

    RETURN ADDRESS: 0x00784b9e	CALL: GetProcAddress
    RETURN ADDRESS: 0x00784b9e	CALL: GetProcAddress
    RETURN ADDRESS: 0x00784b9e	CALL: GetProcAddress
    RETURN ADDRESS: 0x00784b9e	CALL: GetProcAddress
    RETURN ADDRESS: 0x00784b9e	CALL: GetProcAddress
    RETURN ADDRESS: 0x00784bc8	CALL: KERNEL32!VirtualProtect
    RETURN ADDRESS: 0x00784bdd	CALL: KERNEL32!VirtualProtect		--> 1
    RETURN ADDRESS: 0x0045ac09	CALL: GetSystemTimeAsFileTime	--> 2
    RETURN ADDRESS: 0x0045ac15	CALL: GetCurrentProcessId
    RETURN ADDRESS: 0x0045ac1d	CALL: GetCurrentThreadId
    RETURN ADDRESS: 0x0045ac25	CALL: GetTickCount
    RETURN ADDRESS: 0x0045ac31	CALL: QueryPerformanceCounter
    RETURN ADDRESS: 0x0044e99f	CALL: GetStartupInfoA
    RETURN ADDRESS: 0x0044fd9c	CALL: HeapCreate

در اینجا API در مکان 1 آدرس برگشت 0x00784bdd و API در مکان دوم آدرس 0x0045ac09 رو دارد.
تفاوت میان این دوتا آدرس نشان دهنده ی این است که آدرس 0x0045ac09 در تابعی که محتوی OEP هستش قرار داره.
در زیر بررسی می کنیم:




2) Binary Behaviour Profiling

بدست آوردن نمایی از رفتار برنامه.

به خروجی زیر نگاه کنید, چه چیزی برداشت می کنید؟

    RETURN ADDRESS: 0x004012ce	CALL: msvcrt!fopen       			 --> 1
    RETURN ADDRESS: 0x00401311	CALL: msvcrt!fseek
    RETURN ADDRESS: 0x0040131c	CALL: msvcrt!ftell
    RETURN ADDRESS: 0x0040133a	CALL: msvcrt!fseek
    RETURN ADDRESS: 0x00401346	CALL: msvcrt!malloc      			--> 2
    RETURN ADDRESS: 0x00401387	CALL: msvcrt!fread       			--> 3
    RETURN ADDRESS: 0x00401392	CALL: msvcrt!fclose
    RETURN ADDRESS: 0x004013b4	CALL: KERNEL32!OpenProcess  		--> 4
    RETURN ADDRESS: 0x004013ee	CALL: KERNEL32!VirtualAllocEx		--> 5
    RETURN ADDRESS: 0x00401425	CALL: KERNEL32!WriteProcessMemory   --> 6
    RETURN ADDRESS: 0x0040146b	CALL: KERNEL32!CreateRemoteThread   --> 7
    RETURN ADDRESS: 0x004014a4	CALL: msvcrt!exit

این کاملا مشخصه که این فایل باینری در حال خواندن یک فایل و همچنین تزریق کد به داخل پروسس دیگری هستش.


3) Finding Interesting Functions

پیدا کردن توابع دلخواه


    RETURN ADDRESS: 0x00443c29	CALL: inet_ntoa			--> point 1
    RETURN ADDRESS: 0x0044a6ee	CALL: KERNEL32!HeapAlloc
    RETURN ADDRESS: 0x00446866	CALL: KERNEL32!GetLocalTime
    RETURN ADDRESS: 0x0044a6ee	CALL: KERNEL32!HeapAlloc
    RETURN ADDRESS: 0x00443f79	CALL: socket			--> point 2
    RETURN ADDRESS: 0x00443fb5	CALL: setsockop
    RETURN ADDRESS: 0x00443fd0	CALL: setsockopt
    RETURN ADDRESS: 0x00444045	CALL: ntohl
    RETURN ADDRESS: 0x0044404f	CALL: ntohs
    RETURN ADDRESS: 0x00444063	CALL: bind			--> point 3
    RETURN ADDRESS: 0x0044412c	CALL: ntohl
    RETURN ADDRESS: 0x0044413c	CALL: ntohs
    RETURN ADDRESS: 0x0043adf6	CALL: WSAAsyncSelect
    RETURN ADDRESS: 0x0044416b	CALL: connect			-->  point 4
    RETURN ADDRESS: 0x00444176	CALL: WSAGetLastError
   
    RETURN ADDRESS: 0x00441979	CALL: USER32!DispatchMessageA
    RETURN ADDRESS: 0x00444ce0	CALL: KERNEL32!GetTickCount
    RETURN ADDRESS: 0x00444cfa	CALL: KERNEL32!QueryPerformanceCounter
    RETURN ADDRESS: 0x00444499	CALL: recv			--> point 5	
    RETURN ADDRESS: 0x0044a8c6	CALL: KERNEL32!HeapFre
    RETURN ADDRESS: 0x0043adf6	CALL: WSAAsyncSelect
    RETURN ADDRESS: 0x004441f7	CALL: closesocket
    RETURN ADDRESS: 0x0044a8c6	CALL: KERNEL32!HeapFree 

جاهایی رو که مشخص کردم توابعی هستند که برای فعالیت های شبکه ای در نظر گرفته شدن.





Extending API Tracing with IDAPython


حالا از لاگی که توسط اسکریپت بالا ساخته میشه می تونیم استفاده کنیم و با اسکریپت زیر که برای IDAPython هستش این توابع رو با رنگ مشخص کنیم.


from idaapi import *
from idc import *
import sys


class logparse():
	def __init__(self,file_path):
		self.file_path = file_path
		self.fp	= open(self.file_path,'r')
		self.data = self.fp.readlines()
		
	def parser(self):
		dict = {}
		for line in self.data:
			line_slice = line.split()
			address = line_slice[2]
			name = line_slice[4]
			dict[address] = name
		
		for ea in dict.keys():
			print dict[ea]
			ea_c = PrevHead(ea)
			SetColor(ea_c,CIC_ITEM,0x8CE6F0)
		return
	
def main():
	file_path = AskFile(0,"*.*","Enter file name: ")
	logobj = logparse(file_path)
	logobj.parser()
	return
	
if __name__ == '__main__':
	main() 

منبع
http://www.securityxploded.com
  • موافقین ۲ مخالفین ۰
  • ۹۵/۰۶/۳۰
  • ۱۲۳۰ نمایش

IDAPython

PEfile

PyDbg

اسکریپت نویسی

نظرات (۰)

هیچ نظری هنوز ثبت نشده است
ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی