
मेमोरी में मैन-इन-द-मिडल बनाने का उपकरण
MemITM (मेम इन द मिडल) टूल को विंडोज प्रोसेस मेमोरी में "मैसेज" को आसानी से इंटरसेप्ट करने के लिए विकसित किया गया है। हमने एन्क्रिप्शन से पहले नेटवर्क मैसेज या आईपीसी मैसेज को कैप्चर करने और उन्हें निरीक्षण या फजिंग के लिए बदलने के लिए कई कस्टम मेमोरी इंटरसेप्शन टूल विकसित किए। प्रत्येक टूल वास्तव में कस्टम, जेनेरिक नहीं, C/ASM में लागू किया गया था और उपयोग/रखरखाव/अनुकूलन में आसान नहीं था।
MemITM टूल को इन समस्याओं को संबोधित करने के लिए विकसित किया गया है, और इसमें शामिल हैं:
आप इसे (स्रोत + संकलित) यहाँ से डाउनलोड कर सकते हैं: https://github.com/Amossys/MemITM
मान लीजिए कि आप किसी विशिष्ट विंडोज प्रक्रिया में फ़ाइल लिखने को इंटरसेप्ट करना चाहते हैं (इसे करने के सरल तरीके हैं, लेकिन यह उदाहरण के लिए है)। फ़ाइल लिखने का काम WriteFile फ़ंक्शन द्वारा किया जाता है, जो kernelbase.dll द्वारा एक्सपोर्ट किया जाता है, और यह BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, ...) योजना का अनुसरण करता है। WriteFile एक __fastcall फ़ंक्शन है: lpBuffer RDX द्वारा इंगित किया जाता है और NumberOfBytesToWrite R8 द्वारा। आइए इन बफर को इंटरसेप्ट करें।
IDA Pro में अपनी "kernelbase.dll" फ़ाइल खोलें, generate.idapython.py स्क्रिप्ट लोड करें, और बस getHook(LocByName("WriteFile"), "rdx","r8") चलाएँ उसके बाद updateConfig("config.bin") चलाएँ।
नोटपैड चलाएँ और फिर python memitm.py notepad.exe config.bin चलाएँ।
बस इतना ही! WriteFile फ़ंक्शन पर एक हुक लगा दिया गया है, और memitm.py का "लॉगर" और "फज़र" फ़ंक्शन बफर की एक प्रतिलिपि प्राप्त करेंगे।
getHook फ़ंक्शन बफर और उसके आकार के लिए रजिस्टर निर्दिष्ट करने की अनुमति देता है, लेकिन कुछ मामलों में ऐसा नहीं हो सकता है। मान लीजिए कि आप NtCreateFile सिस्कॉल को इंटरसेप्ट करना चाहते हैं और फ़ाइल का नाम प्राप्त करना चाहते हैं। NtCreateFile योजना का अनुसरण करता है NTSTATUS NtCreateFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, ...), और फ़ाइल का नाम ObjectAttributes->ObjectName->Buffer द्वारा प्राप्त किया जा सकता है (आकार ObjectAttributes->ObjectName->Length * sizeof(WCHAR) है)।
getHook अपने t1customOpcodes पैरामीटर में रजिस्टर के बजाय एक शेलकोड निर्दिष्ट करने की भी अनुमति देता है। इस शेलकोड को:
RCX रजिस्टर में रखना चाहिए;RDX रजिस्टर में रखना चाहिए;NtCreateFile के मामले में, हम कार्य करने के लिए बस इस ASM कोड को चला सकते हैं:
mov rax, [r8+0x10] ; rax अब ObjectAttributes->ObjectName है
mov rcx, [rax + 0x10] ; rcx अब ObjectAttributes->ObjectName->Buffer है
mov rdx, [rax] ; rdx अब ObjectAttributes->ObjectName->Length है
and rdx, 0xFFFF ; Length एक USHORT है
shl rdx, 1 ; और इसे *2 किया जाना चाहिए
आइए इसे असेंबल करें (उदाहरण के लिए ऑनलाइन डिसअसेंबली वेबसाइट का उपयोग करके): 498B4010488B4810488B104881E2FFFF000048D1E2। हमारी getHook कॉल अब getHook(LocByName("NtCreateFile", t1customOpcodes="498B…E2") होनी चाहिए।
हाँ! कॉन्फिग फ़ाइल में एकाधिक इंटरसेप्शन पॉइंट परिभाषाएँ एम्बेड की जा सकती हैं, और getHook फ़ंक्शन फ़ाइल में जोड़ता है। आप एकाधिक मॉड्यूल में इंटरसेप्शन पॉइंट सेट कर सकते हैं। उदाहरण के लिए, आप ALPC और IOCTL संदेशों को एक साथ इंटरसेप्ट कर सकते हैं।
आप उन्हें पायथन कॉलबैक में उनके "मैसेज आईडी" से अलग कर सकेंगे।
चिंता न करें, एक सरल HTTP सर्वर (logserver.py) और httpNetSend फ़ंक्शन भी है जो आपको अपने टेस्ट केस (और संशोधन) को रीयलटाइम में दूरस्थ होस्ट पर भेजने की अनुमति देता है।
खैर, आप कुछ बिट्स को फ्लिप करने के लिए bufferBitFlip फ़ंक्शन का उपयोग शुरू कर सकते हैं। कच्चे संदेशों को लॉग करने से आप अपना स्वयं का डिसेक्टर लिख सकेंगे, और मैन्युअल रूप से एक बेहतर फज़र लागू करना शुरू कर सकेंगे। याद रखें: आप बफर का आकार नहीं बदल सकते!
उदाहरण के लिए, हमारे WriteFile उदाहरण में, निम्नलिखित fuzzer फ़ंक्शन "hello" को "world" से बदल देगा:
def fuzzer(data, msgID=None, pid = 0):
return data.replace("hello","world")
उदाहरण के लिए, ALPC संदेशों के लिए, आपके पास निम्नलिखित memitm.py सेटअप हो सकता है:
def logger(data, msgID=None, pid=0):
totalLen = 0
dataLen = 0
typ = 0
dataInfoOffset = 0
cid = 0
tid = 0
messageID = 0
clientViewSize = 0
# PORT_MESSAGE हेडर (0x18 बाइट्स) को छोड़ें
if len(data) > 0x18:
totalLen = struct.unpack("<H",data[:2])[0]
dataLen = struct.unpack("<H",data[2:4])[0]
typ = struct.unpack("<H",data[4:6])[0]
dataInfoOffset = struct.unpack("<H",data[6:8])[0]
zeroInit = struct.unpack("<L",data[4:8])[0]
cid = struct.unpack("<L",data[8:0xC])[0]
tid = struct.unpack("<L",data[0xC:0x10])[0]
messageID = struct.unpack("<L",data[0x10:0x14])[0]
clientViewSize = struct.unpack("<L",data[0x14:0x18])[0]
logMessage = "ALPC message : %x:%x - %x:%x - %d:%d - %x - %x" % (totalLen, dataLen, typ, dataInfoOffset, cid, tid, messageID, clientViewSize)
print logMessage
return
IDA प्रो स्क्रिप्ट में एक सरल "हुकिंग" इंजन एम्बेडेड है, जो कई निर्देशों को एक विशिष्ट क्षेत्र में ले जाने की अनुमति देता है (हमारे DIMCT टूल से प्राप्त)। इसे कई कॉर्नर केसों को संभालना होगा, जैसे मेमोरी रिलेटिव निर्देश, क्रॉस-रेफरेंस, आदि, और यदि आप वास्तव में छोटे बेसिक ब्लॉक्स (x86 बाइनरी के लिए <5 बाइट्स, x64 के लिए <12 बाइट्स) को इंटरसेप्ट करने का प्रयास कर रहे हैं जिनमें कई क्रॉस-रेफरेंस हैं, तो संभवतः बहुत सारे "यहाँ हुक स्थापित नहीं कर सकता" प्रदर्शित करेगा। अधिक जानकारी के लिए, बस स्रोत कोड पढ़ें।
यह कॉन्फिग फ़ाइल में निम्नलिखित जानकारी उत्पन्न करता है:
RCX/RDX में रखता है और DLL लॉग रूटीन को कॉल करता है;DLL इंजेक्टर एक सरल DLL इंजेक्टर है (CreateRemoteThread/LoadLibraryA सामान)।
DLL स्वयं एक साझा मेमोरी क्षेत्र आरंभ करता है और कॉन्फिग डेटा की प्रतीक्षा करता है (साझा मेमोरी क्षेत्र में 3 जेनेरिक फील्ड हैं: साझा मेमोरी मैसेज आईडी, मैसेज आकार और मैसेज बफर)। प्राप्त होने पर, यह मेमोरी में हुक को पार्स और स्थापित करता है, और इसके बाद किसी भी कॉन्फिग अपडेट की अनुमति नहीं होगी (यदि आप नए हुक सेट अप करना चाहते हैं तो आपको प्रक्रिया को मारना होगा)। कोई भी रखा गया हुक genericHookFunction DLL फ़ंक्शन पर उतरेगा। यह फ़ंक्शन केवल मेमोरी क्षेत्र (महत्वपूर्ण अनुभागों से संरक्षित) को बफर डेटा, उसके आकार और मैसेज आईडी (साझा मेमोरी मैसेज आईडी फील्ड के उच्च बिट्स में सेट) से भरता है। पायथन प्रक्रिया को सूचित करने के लिए, नए संदेशों को संकेत देने और प्रक्रिया द्वारा उन्हें पैच करने की प्रतीक्षा करने के लिए 2 ईवेंट का उपयोग किया जाता है (टाइमआउट 2 सेकंड है)। यदि बफर अपडेट किया गया है, तो मूल बफर भी संशोधित किया जाता है।