@@ 0,0 1,5645 @@
+---
+title: "Adventures in Boreland"
+date: 2024-02-15
+---
+
+> Comrades, leave me here a little, while as yet 'tis early morn:\
+> Leave me here, and when you want me, sound upon the bugle horn.
+
+— Alfred Tennyson, <cite>Locksley Hall</cite>
+
+---
+
+<style>
+ .centerslide {
+ max-width: fit-content;
+ margin-left: auto;
+ margin-right: auto;
+ }
+</style>
+
+
+::: {dir="rtl"}
+
+يفضل تكون قاري المقال السابق، [The Hunt for a UEFI Graphics Bug](/blog/uefi-graphics-bug/iq.html).
+
+بيوم من الأيام ببداية السنة الدراسية هاي، چنت بمختبر ++C بقاعة تختلف عن القاعة مال المحاضرة الفاتت. ذيچ چانت كل الحواسيب اللي بيها نظامها Windows 7 (32 bit) وبيها گيگتين رام، بينما الحاسبة اللي گدامي چان بيها Windows 10 (64 bit) و8 گيگات رام وi5-8400. شي لفت انتباهي انو حواسيب ذيچ القاعة چان بيها لوگو القسم كخلفية ونظامها 'نظيف' لأن مشغلين بيها Deep Freeze، بينما الجديدات الطلاب لاعبين بيها طوبة.
+
+ابو المختبر كتب برنامج يطبع Hello World عالسبورة وگعد يعلمنا شلون نشغل برنامج Borland C++ 5.02 (بدون e) حتى نطبق بي. بس شغلته رأسا طلعتلي نافذة أكتب بيها، محتاجيت أسوي project أو أي شي.
+
+بالأخير گال (منقول بتصرف):
+
+> لمن تسوون Run، راح تشوفون الكتابة تطلع وتختفي بسرعة\
+> هذا البرنامج يغلق النافذة لمن ينتهي التنفيذ، مو مثل التوربو اللي منگدر نستعمله هنا لأن يحتاج حاسبة 32 بت\
+
+رحت عليه بعد ما خلصت المحاضرة:
+
+> \- استاذ اذا اعدل على البورلاند واخلي النافذة تبقى، احصل درجات اضافية؟\
+> \- اي بس كون صدك\
+> \- تمام... المحاضرة الجاية يكون جاهز
+
+(كلشي مچنت اعرف ببرمجة الوندوز، اصلا صارلي سنين ماستخدمه)
+
+---
+
+ب"التوربو" قصده Borland Turbo C++ 4.5، وهو نسخة اقدم وقليلة الدسم من البورلاند مخصصة للإستخدام المنزلي. منا وجاي راح استخدم "توربو" لهذا و"بورلاند" لذاك.
+
+بالفعل اذا نجرب التوربو على نظام 32 بت حيتنصب عادي:
+
+![](turbo-install.png)
+
+بينما اذا نجرب على نظام 64 بت:
+
+![](cant-run.png)
+
+اذا نكتب برنامج بسيط بي حنشوف انو التوربو عنده console خاص بي وهذا يبقى مفتوح بعد ما ينتهي التنفيذ، بينما البورلاند يستعمل الconsole مال الوندوز.\
+ممكن لاحظتوا انو `iostream` وراها `h.` و`cout` ماكو قبلها `::std` و`()main` مابيها return type، هذا البرنامج چان موجود قبل ما يكون اكو شي اسمه C++ standard.
+
+![](turbo-con.png)
+
+زين ليش القديم يبقي النافذة والجديد لا؟ البورلاند نزل سنة 1997 فگلت يمكن الوندوز مال ذاك الوكت چان يبقيهن فمحتاجوا يسوون شي اضافي، بس جربته على Windows 95 وNT 4.0 وثنينهن يغلقوهن. لعد ليش التوربو وحد؟ لأن حاسبيه نسخة مال هواة؟
+
+التوربو نزل سنة 1995، والنظام اللي عاشره هو Windows 3. بوكتها الوندوز مچان نظام مستقل بحد ذاته وانما چان برنامج مال DOS، أو، نظام مكمل للDOS، أو hypervisor فوگ الDOS. [الموضوع معقد](https://www.xtof.info/inside-windows3.html). المهم چان الوندوز بس يدعم البرامج الرسومية واذا ردت برامج نصية فماعندك غير الDOS (ويه قيوده). لحدما مايكروسوفت سوت library اسمها QuickWin تنطي console رسومي للبرامح النصية. بورلاند جوابها چان اسمه... EasyWin وهذا اللي دنشوفه هنا. المنافسة بين مايكروسوفت وبورلاند چانت شرسة لدرجة مايكروسوفت چانت [تاخذ موظفي بورلاند بالليموزينات](https://news.ycombinator.com/item?id=29514340).
+
+![[Borland C++ 4.0 User's Guide](http://bitsavers.informatik.uni-stuttgart.de/pdf/borland/borland_C++/Borland_C++_Version_4.0_Users_Guide_Oct93.pdf), p. 420](ezwin.png)
+
+\
+
+![توربو بداخل Windows 3.1](win3turbo.png)
+
+بينما Windows 95 چان عنده console (لكن مو console "حقيقي" مثل مال Windows NT وانما [عبارة عن DOS virtual machine يتم تمرير الIO عن طريقها](https://news.ycombinator.com/item?id=36660391)) لهذا مچان اكو حاجة لEasyWin، بس لمن گلبت بالكتيب مال البورلاند لگيته بعده موجود:
+
+![[Borland C++ 5 User's Guide](http://bitsavers.informatik.uni-stuttgart.de/pdf/borland/borland_C++/Borland_C++_Version_5_Users_Guide_1997.pdf), p. 28](ezwin5.png)
+
+يعني اذا تريد تستخدم EasyWin، لازم تسوي project جديد وتختاره:
+
+![](newproj.png)
+
+وراها حتطلع نافذة فارغة ولازم تختار ملف الcpp:
+
+![](projcreated.png)
+
+اني كطالب ميهمني احفظ هالخطوات، اريد برنامج يشتغل بلمح البصر وينطيني نافذة اكتب بيها كود رأسا، فهيچ "حل" مو مقبول.
+
+اذا نباوع على الmenu bar مال البورلاند حنشوف اكو دگمة اضافية مچانت موجودة بالتوربو: Script.
+
+![](scriptbar.png)
+
+شنو هاي الmodules؟
+
+![](scriptmodules.png)
+
+خلي نشوف محتوى وحدة منهن:
+
+![](editspp.png)
+
+الscripting language شكلها شي جدي مو كلاوات. اكو كتيب كامل عليها، راح اخليه هو يعرف عنها:
+
+![[Borland C++ 5 ObjectScripting Programmer's Guide](http://bitsavers.informatik.uni-stuttgart.de/pdf/borland/borland_C++/Borland_C++_Version_5_ObjectScripting_Programmers_Guide_1997.pdf), p. 20](cscriptintro.png)
+
+اذا نسوي سكربت بأسم `personal.spp` ونخليه ويه البقية حيشتغل تلقائيا لمن يشتغل البرنامج:
+
+![Ibid, p. 22](personalspp.png)
+
+نگدر نستدعي function من DLL خارجي بداخل السكربت:
+
+![Ibid, p. 65](cscriptdll.png)
+
+اللغة تدعم نوع بدائي من الevents، الفكرة انو يصير call لmethod معينة (اسمها event) لمن يصير حدث معين، والevent handler يسوي override للmethod فيتنفذ بدالها لمن يصير الحدث:
+
+![Ibid, p. 73](cscriptevents.png)
+
+الكتيب يذكر هوايه events، اكثر وحدة لفتت نظري هي `DebugeeAboutToRun` ضمن ال`Debugger` class.
+
+![Ibid, p. 156](debugeeaboutto.png)
+
+ال`Debugger` class موجودة الinstance مالته بملف `debug.spp`:
+
+![](debugexp.png)
+
+ماطول هي `export` نگدر نسوي `import` الها من غير سكربت:
+
+![Ibid, p. 61](expimp.png)
+
+صرنا نعرف كلشي نحتاجه حتى نشغل function من DLL معين قبل تنفيذ اي كود من البرنامج اللي كتبه المستخدم. الfunction هاي، اللي حنسميها `()inject`، راح تخلي نافذة البرنامج تبقى بعد ما ينتهي التنفيذ:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal personalspp" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers>
+import debugger;
+
+import "injector.dll" {
+ void inject();
+}
+
+on debugger:>DebugeeAboutToRun() {
+ inject();
+}
+</code></pre>
+</section>
+</div>
+</div>
+</div>
+<figcaption>personal.spp</figcaption></figure>
+
+
+::: {dir="rtl"}
+
+ممكن تگول: شلون البورلاند ديستدعي function من ملف DLL عشوائي؟
+
+الوندوز يدعم نوعين من الdynamic linking:
+
+- Load-time dynamic linking: خلاله يكون اكو import tables تنحط بالbinary مال البرنامج اثناء الlinking، يكون بيهن عناوين لأسم كل function خارجية يستعملها البرنامج من DLL معين. اثناء التنفيذ الloader راح يستبدل عناوين الأسامي ويحط بمكانها عناوين لمكان الfunctions بالaddress space مال البرنامج.
+
+- Run-time dynamic linking (وهو اللي ديصير هنا): خلاله البرنامج يسوي `()LoadLibrary` لملف DLL معين وراها ياخذ الaddress مال function موجودة بي عن طريق اعطاء اسمها ل`()GetProcAddress`.
+
+"اسم" الfunction يسموه الsymbol مالتها. ممكن يطابق اسمها بالكود بس مو شرط (مثل ما راح نشوف بعد شوية).
+
+نجي نكتب كود اولي للDLL، حيكون 32 بت (لأن البورلاند 32 بت) وحستخدم Visual Studio 2015. ال`declspec(dllexport)_` معناها هاي الfunction حتكون exported ونگدر نستعملها من خارج الDLL، الموضوع يشبه الpublic والprivate بالOOP:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp1" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+_declspec(dllexport) void inject();
+
+void inject() {
+ MessageBoxA(nullptr, "test", "test", 0);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+اذا نجي نباع على الsymbol مال الfunction حنشوفه شي غريب:
+![](mangled.png)
+
+ السبب هو الC++ symbol mangling/decoration: بلغة ++C ممكن يكون اكو اكثر من function بنفس الاسم (بسبب الfunction overloading مثلا)، لهذا الsymbol لازم تنحط وياها معلومات اضافية نگدر نفك ترميزها عن طريق اي demangler:
+
+![](demangle.png)
+
+حتى نلغي هالموضوع، مجرد نضيف `"extern "C`:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp2" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="5"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include<windows.h>
+
+extern "C" _declspec(dllexport) void inject();
+
+void inject() {
+ MessageBoxA(nullptr, "test", "test", 0);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+
+![](externc.png)
+
+::: {dir="rtl"}
+
+اذا نحط الDLL بفولدر البورلاند (يم الEXE مالته) ونحاول ننفذ اي كود، راح نشوف الرسالة تطلع قبل ما يتنفذ اول سطر من البرنامج:
+
+![](msgfirst.png)
+
+المشكلة الرسالة دتطلع اكثر من مرة، بعدين حنشوفلها حل. هسه اريد اخلي الDLL ينطيني debug messages اختيارية، صح هالشي مو ضروري للشغل (لأن اگدر احطهن وراها امسحهن) بس اريد ابقيهن واخليهن اختياريات حتى اعرف شنو المشكلة اذا صار خلل بالتنفيذ داخل المختبر:
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp3" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="4-11|12-18"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO };
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+extern "C" _declspec(dllexport) void inject(char dbglvl);
+
+void inject(char dbglvl) {
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+\
+
+<figure>
+<div class="centerslide">
+<div class="reveal personalspp2" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="4,8"><script type="text/template">
+import debugger;
+
+import "injector.dll" {
+ void inject(char dbglvl);
+}
+
+on debugger:>DebugeeAboutToRun() {
+ inject(2);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>personal.spp</figcaption></figure>
+
+::: {dir="rtl"}
+
+
+اذا نجرب من جديد حتطلع الرسالة اكثر من مرة (مثل ما توقعنا) بس هالمرة كل ما ندوس OK البورلاند حيگول انو صار خلل:
+
+![](scriptexcp.png)
+
+سبب هالشي هو اختلاف الcalling convention (طريقة تمرير الparameters للfunctions)، البورلاند عباله الfunction هي stdcall، بهذا الconvention الparameters راح تندفع للstack بالعكس، يعني اذا عندك function كالاتي:
+
+
+
+```
+int add(int arg1, int arg2, int arg3)
+```
+
+هيچ حتكون الcall مالتها:
+
+```
+push arg3
+push arg2
+push arg1
+call add
+```
+
+
+
+الناتج (الreturn value) راح يرجع بالeax register والcallee هو اللي ينظف الstack (يعني بعد الreturn راح ترجع الstack لنفس حالتها قبل ما تسوي push للparameters). هذا الconvention هو المُستخدم بالWindows API. بينما الVisual Studio ديستعمل الcdecl convention واللي هو نفس الstdcall، الفرق الوحيد ان الcaller هو اللي ينظف الstack. لهذا السبب الكود اشتغل تمام والخطأ طلع بعد انتهاء التنفيذ.
+
+البورلاند يدعم تحديد الcalling convention:
+
+![Borland C++ 5 ObjectScripting Programmer's Guide, p. 66](cdeclsup.png)
+
+\
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal personalspp3" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="4"><script type="text/template">
+import debugger;
+
+import "injector.dll" {
+ void __cdecl inject(char dbglvl);
+}
+
+on debugger:>DebugeeAboutToRun() {
+ inject(2);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>personal.spp</figcaption></figure>
+
+::: {dir="rtl"}
+
+اذا نجرب من جديد البورلاند حيشتكي ان الfunction مو موجودة:
+![](symnotfound.png)
+
+شكله تحديد الcalling convention خلاه يبحث عن غير symbol. نگدر نستعمل اداة WinAPIOverride حتى نشوف كل استخدامات البورلاند ل`()GetProcAddress` وبالتالي نعرف شنو الsymbol اللي ديبحث عنها:
+
+![](getprocaddress.png)
+
+گام يبحث عن `inject_` بدل `inject` لمن حدننا الcdecl convention. سبب هالشي هو ان البورلاند بنفسه يحط `_` لمن يبني cdecl function وهو ديفترض ان الدنيا كلها ماشيه مثله.
+
+![[Borland C++ 5 Programmer's Guide](http://bitsavers.informatik.uni-stuttgart.de/pdf/borland/borland_C++/Borland_C++_Version_5_Programmers_Guide_1997.pdf), p. 65](borcallconv.png)
+
+قبل شوية گلنا البورلاند ديفترض الcalling convention هو stdcall، ليش؟ هالشي ممذكور بالكتيب بس لمن ماتحدد الconvention حيجرب كل طرق التسمية المذكورة بالجدول (عدا مال fastcall) وحيطابق الconvention حسب الاسم الصحيح:
+
+![](convtry.png)
+
+نگدر نعتبر هالتسميات نوع من الC symbol mangling، حتى Visual Studio هم [يسوي هيچ شي](https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names#FormatC) لمن يبني برامج 32 بت:
+
+![](vsconv.png)
+
+مكتوب هنا ان الcdecl symbols ينحط قبلها `_` مثل ما يسوي البورلاند، لعد ليش الsymbol مالتنا مابيها؟ معقولة المعلومات غلط؟ خلي نسويها `stdcall__` ونشوف شيصير:
+
+![](stdcallmangling.png)
+
+صار الmangling مثل ما كاتبين. بعد البحث اكثر، اكتشفت كاتبين التالي بال[documentation](https://learn.microsoft.com/en-us/cpp/cpp/cdecl) مال `cdecl__`:
+
+![](cdecldec.png)
+
+حتى نحل الموضوع، حننطي تسمية ثانية للsymbol، يعني alias، او مثل ما اني اسميه: symbolic link.
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp4" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="16"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+extern "C" _declspec(dllexport) void inject(char dbglvl);
+
+void inject(char dbglvl) {
+ #pragma comment(linker, "/export:_inject=inject")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+اذا نسوي build حيطلع خلل:
+
+![](symerr.png)
+
+ال[documentation مال EXPORT/](https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function) بي شغلة لفتت انتباهي:
+
+![](docexp.png)
+
+مو گلنا cdecl مابيها mangling؟ خلي نتأكد:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp5" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="17"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+extern "C" _declspec(dllexport) void inject(char dbglvl);
+
+void inject(char dbglvl) {
+ //#pragma comment(linker, "/export:_inject=inject")
+ #pragma message("function name: " __FUNCTION__ " mangled name: " __FUNCDNAME__ )
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+
+![](pragmamsg.png)
+
+::: {dir="rtl"}
+
+مو كلش دافتهم شديصير هنا، اعتقد الlinker حاسب حسابه انو ديصير mangling لسبب ما، المهم ضفت underscore للطرفين واشتغل:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp6" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="16"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+extern "C" _declspec(dllexport) void inject(char dbglvl);
+
+void inject(char dbglvl) {
+ #pragma comment(linker, "/export:__inject=_inject")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+
+![](twoinjects.png)
+
+::: {dir="rtl"}
+ال`declspec_` بعد ماله داعي:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp7" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="13"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+extern "C" void inject(char dbglvl);
+
+void inject(char dbglvl) {
+ #pragma comment(linker, "/export:__inject=_inject")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+![](nodeclspec.png)
+
+::: {dir="rtl"}
+
+اصلا نگدر نشيل ال `"extern "C` ونسوي alias للC++-mangled symbol مباشرة:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp8" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="14"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+void inject(char dbglvl) {
+ #pragma comment(linker, "/export:__inject=" __FUNCDNAME__)
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+لمن حددنا stdcall فوگ، الsymbol صارت `inject@4_`، ال4 هي حجم الarguments بالبايتات. بس احنا عدنا `char`، مو المفروض الحجم يكون بايت واحد؟
+
+سبب هالشي هو ال[stack alignment](https://stackoverflow.com/a/672482)، الVisual Studio ديسوي DWORD alignment (4 بايتات) بينما البورلاند يسوي byte alignment وهمين عباله العالم ماشيه مثله:
+
+![](boralign1.png)
+
+![Borland C++ 5 Programmer's Guide, p. 72-73](boralign2.png)
+
+هذا معناه انو البورلاند دينطي بايت واحد للfunction وهي دتتوقع اربع بايتات، هالشي مديسبب مشكلة چبيرة ويه الcdecl convention لأن الcaller (البورلاند) هو المسؤول عن تنظيف الstack، فراح ينطي بايت واحد ويشيل بايت واحد. بينما لو مختارين stdcall چان البورلاند ينطي بايت والfunction تشيل 4 من الstack، يعني 3 بايتات زيادة. خلي نجرب:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp9" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="13,15-16"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+extern "C" void __stdcall inject(char dbglvl);
+
+void __stdcall inject(char dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ //MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+\
+
+<figure>
+<div class="centerslide">
+<div class="reveal personalspp4" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="4"><script type="text/template">
+import debugger;
+
+import "injector.dll" {
+ void __stdcall inject(char dbglvl);
+}
+
+on debugger:>DebugeeAboutToRun() {
+ inject(2);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>personal.spp</figcaption></figure>
+
+![](excepstdcall.png)
+
+::: {dir="rtl"}
+زين شراح يصير اذا نخلي الfunction تشيل 10003 بايت زيادة؟
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp10" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="13,15, 17-18"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+struct haha { char b[10000]; };
+
+extern "C" void __stdcall inject(char dbglvl, haha bytez);
+
+void __stdcall inject(char dbglvl, haha bytez) {
+ #pragma comment(linker, "/export:inject=_inject@10004")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ //MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+\
+
+!["Ight Imma head out"](lolcrash.gif)
+
+::: {dir="rtl"}
+
+نگدر نخلي الfunction تاخذ `int` (4 بايتات) بدل `char` ونحل المشكلة:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal personalspp5" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="4"><script type="text/template">
+import debugger;
+
+import "injector.dll" {
+ void __stdcall inject(int dbglvl);
+}
+
+on debugger:>DebugeeAboutToRun() {
+ inject(2);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>personal.spp</figcaption></figure>
+
+\
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp11" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="13,15"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+---
+
+> In another moment down went Alice after it, never once considering how in the world she was to get out again.
+
+— Lewis Carroll, <cite>Alice's Adventures in Wonderland</cite>
+
+::: {dir="rtl"}
+كل برامج البورلاند تنتهي بcall الى هاي الfunction:
+
+![](exitprocessredir.png)
+
+اللي راح تودينا لهاي الjmp:
+
+![](jumpfunc.png)
+
+هاي يسموها indirect jump، لأن هي مدتروح للaddress المذكور (0x004130f8) وانما راح تروح للaddress اللي موجود بالمكان المذكور:
+
+![](jmpplace_c.png)
+
+فيعني المفروض المكان النهائي اللي راح تروحله هو 0x00 0x32 0x01 0x00، واللي لو نحوله الى address لازم ناخذ البايتات بالمگلوب لأن المعمارية little-endian، فراح يكون عندنا 0x00013200. هذا العنوان المفروض هو العنوان مال `()ExitProcess` بالimport table، الوجهة النهائية لكل برامج الوندوز.
+
+اول شي اجه ببالي هو ان استبدل الjmp بinfinite loop عن طريق الDLL، صح مو حل مثالي بس المفروض يشتغل. قبل ما نكتب اي كود خلي نأكد نظريتنا عن طريق استبداله يدويا بالباينري، كل اللي نحتاجه هو ان نسوي relative jump الى negative offset يمثل عدد البايتات مال الjump instruction نفسها. يعني اذا الinstruction اربع بايتات فراح نرجع اربع بايتات وبالتالي تصير infinite loop. اختيار patch instruction مال Ghidra مدينطيني اللي ببالي فراح نضطر نكون الinstruction يدويا.
+
+نجي نباع كتيب Intel:
+
+![[Intel 64 and IA-32 Architectures Software Developer’s Manual: Instruction Set Reference](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html), p. 663](iajmp.png)
+
+اول instruction مذكورة حجمها بايتين، الopcode مالتها 0xeb والoperand مالتها هو signed 8-bit offset، يعني الoperand لازم يكون 2- حتى نسوي infinite loop، حتى نحول 2- الى two's complement مجرد نضيفها الى 8^2، حيكون الناتج 0xfe. الinstruction النهائية حتكون 0xeb 0xfe. الinstruction الأصلية چانت 6 بايتات، مو ضروري نسوي اي شي بخصوص الاربعة الباقيات بس في سبيل الكمالية ححط instructions متسوي شي (nop: 0x90) بمكانهن:
+
+![](finalloop.png)
+
+![](loopcpu.png)
+
+ردت أبدي بالكود، بس جتي ببالي فكرة أحسن.
+
+---
+اذا نباع على الimports مال برنامجنا حنشوف اكو User32.dll، حيكون هالأسم مألوف اذا لاعب بالايقونات بالوندوز قبل: هاي الGUI library.
+
+![](importuser32.png)
+
+خلي نشوف الimport table مالته:
+
+![](user32table.png)
+
+الفكرة: نضيف كود يتنفذ بدل `()ExitProcess` يعرض MessageBox، هاي الMessageBox حتبقي النافذة مفتوحة لحد ما المستخدم يغلقها وما راح تاكل الCPU. حتى نستبدل عنوان `()ExitProcess` بعنوان الكود مالنا لازم اول شي نعرف شنو الpid مال الEXE اللي ديتنفذ حتى نگدر نقره ونعدل على ذاكرته. البورلاند مموفر هالمعلومة للscripting language فمنگدر نمررها للfunction، لازم نقراها من ذاكرة البورلاند. الDLL مالتنا ديشتغل بنفس الprocess والaddress space مال البورلاند، فذاكرة البورلاند والpid والchild processes مالته هنه مالاتنا.
+
+شلون نعرف الpid وين صاير؟ الفكرة بسيطة: نشغل برنامج بالبورلاند، ناخذ الpid مالته، نفحص ذاكرة البورلاند ونلگي كل القيم اللي تطابق الpid والaddresses مالها، هاي القيم هوايه منها راح تطابق الpid بالصدفة فنعيد العملية اكثر من مرة حتى نضيق نطاق البحث ونلگي الaddress الصحيح. "اوگف لحظة، هذا مو نفس الشي اللي يسووه الغشاشين حتى يزيدون نقاطهم بالألعاب؟" صحيح، ولهذا السبب البرنامج اللي حنستخدمه لهلغرض اسمه Cheat Engine. نبدي بأول فحص:
+
+![](firstscan.png)
+
+نعيد تنفيذ البرنامج ونفحص الpid الجديد:
+
+![](secondscan.png)
+
+اذا ننتظر شوية حنشوف اكو كم قيمة حتتغير من وحدها:
+
+![](thirdpid.png)
+
+واذا نعيد تنفيذ البرنامج حيرجعن صحيحات:
+
+![](4pid.png)
+
+وبعد فترة هم حيرجعن غلط. اذا نراقب الكود اللي ديستخدمهن حنعرف ذني memory structures مال وندوز، مو مال بورلاند، فنستبعدهن:
+
+![](internalwatch.png)
+
+بالاخير، ما بقى إلا القليل (وهذا المطلوب):
+
+![](realones.png)
+
+
+المشكلة ذني مو عناوين ثابتة، حيتغيرن اذا نغلق البورلاند ونفتحه. نحتاج نبدي من عنوان ثابت بالباينري وراها نبدي نتبع pointer وره pointer لحد ما نوصل للpid. احنا عندنا العنوان النهائي ونريد نبقى نرجع ليوره لحد ما نوصل لعنوان ثابت:
+
+![[المصدر](https://www.youtube.com/watch?v=3dyIrcx8Z8g)](ptrchain.jpg)
+
+(للسهولة، لمن أكتب struct أقصد اما struct أو struct instance)
+
+البرنامج اكيد مديحتفظ بالpid بvariable طاير وانما حاطه بstruct او class. المعالج ماعنده مفهوم اسمه struct، اذا عندك بالكود struct كل عناصرها integers، ابسط شي ممكن يسويه الكومبايلر هو ان يحط الintegers وحدة وره اللخ بالذاكرة ويحط عنوان بداية المجموعة بregister (مثل edx) ويحتفظ بالoffsets مال كل عنصر منها، وكل ما يحاول يستخدم عنصر بinstruction معينة حينطيها الoffset والbase address. يعني اذا ديقره اول عنصر حينطي edx+0x0، ثاني عنصر edx+0x4 وهكذا. ماطول احنا عدنا العنوان النهائي مال الpid (مجرد نختار اي واحد من اللي بقوا بالأخير)، نگدر نشوف شنو الinstruction اللي دتستعمله حتى نعرف شنو الoffset وشنو الbase address:
+
+![](1ptr.png)
+
+يعني عدنا struct عنوانها 0x07fd3a14، خلي نگول اسمها `Process`، وبيها عنصر خلي نگول اسمه `pid` الoffset مالته 0x38. بديهيا، الbase address عنوان متغير لكن الoffset ثابت:
+
+:::
+
+<span style="color: red;">Process(0x07fd3a14)</span> + pid(0x38) -> 3276
+
+::: {dir="rtl"}
+
+شلون نرجع ليوره خطوة بعد ونلگي pointer لل`Process` struct بنفسها؟ نبحث عن الbase address مالتها بالذاكرة:
+
+![](2ptrscan.png)
+
+حنختار اي واحد من ذوله ونحسبه الpointer مال الstruct، اللي هو بنفسه عنصر بstruct ثانية خلي نگول اسمها `Debugger`. نكرر نفس الخطوات حتى نلگي الoffset مال الpointer والbase address مال الstruct اللي موجود بيها:
+
+![](2ptrwatch.png)
+
+يعني:
+
+:::
+
+<span style="color: red;">Debugger(0x07fd2d28)</span> + Process_ptr(0x40) -> <span style="color: red">Process(0x07fd3a14)</span> + pid(0x38) -> 3276
+
+::: {dir="rtl"}
+
+نبحث عن الbase address مال `Debugger` بالذاكرة:
+
+![](3ptr.png)
+
+طلعلنا عنوانين خضر، يعني ذني offsets ثابتة بالملفات المذكورة مو عناوين ذاكرة متغيرة. حختار مال `BCWBDK32.DLL`، الحروف الچبيرة قنعتني بي:
+
+:::
+
+<span style="color: green">Debugger_ptr(BCWBDK32.DLL + 0x35008)</span> -> <span style="color: red;">Debugger(0x07fd2d28)</span> + Process_ptr(0x40) -> <span style="color: red">Process(0x07fd3a14)</span> + pid(0x38) -> 3276
+
+::: {dir="rtl"}
+
+يعني اذا نريد نوصل للpid، حنروح للbase address مال `BCWDBK32.DLL` ونضيفله 0x35008، حنلگي pointer هناك، اذا نتبعه حنروح لبداية `Debugger` (اللي هو مكان متغير بالذاكرة)، لمن نمشي 0x40 بايت وراه حنلگي pointer ثاني، هذا حيودينا لبداية `Process`، مناك نصعد 0x38 بايت وحيطلع الpid گبالنا.
+
+نضيف الpointer chain:
+
+![](addchain.png)
+
+حتى نتأكد من صحة اللي سويناه، نغلق البورلاند ونفتحه من جديد وننفذ برنامج بي، المفروض الpointer chain تودينا للpid مال البرنامج:
+
+![](chainverif.png)
+
+كم ملاحظة قبل ما نباشر بكتابة الكود:
+
+- راح افترض اسماء الملفات كلها ASCII (البورلاند ميدعم غير شي).
+- ما راح احاول اخذ كل حالة وكل خطأ ممكن يصير بعين الاعتبار.
+- الكود مو ++idiomatic C: راح تشوف هوايه C-style casts مثلا.
+- راح افترض انت توثق بالبورلاند والبرامج اللي يولدها (وهذا امر بديهي، انت گاعد تشغلهن بحاسبتك).
+
+اول شي حناخذ الpid والhandle مال الprocess مالتنا (اللي هي، للتذكير، نفسها مال بورلاند):
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp12" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="25-26,28-32|13-16"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+وحدة بناء الprocesses بالWindows هي الmodules، الmodule هي اما EXE او DLL، حتى نلگي الbase address مال `BCWDBK32.DLL` لازم نفر الmodules لحد ما نلگيه. الكود مال `()GetBaseAddress` اخذته من النت:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp13" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="49-53|19-31|4"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borland.handle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return;
+ }
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+صار عدنا كلشي نحتاجه حتى نتبع الpointer chain:
+
+:::
+<figure>
+<div class="centerslide">
+<div class="reveal cpp14" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="56-61|5,9"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borland.handle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ pid = *(DWORD*)(pid + i);
+ }
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(pid)).c_str());
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+
+نجرب الكود:
+
+![](pidget.png)
+
+المشكلة اذا الواحد ديستخدم غير نسخة من البورلاند ممكن الoffsets تختلف وتصير كارثة، الوندوز يوفر function تخلينا نتأكد من صحة الpointer قبل ما نقراه:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp15" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="60-63"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borland.handle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD *ptr = (DWORD*)(pid + i);
+ if (IsBadReadPtr(ptr, sizeof(DWORD))) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return;
+ }
+ pid = *ptr;
+ }
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(pid)).c_str());
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+هاي الfunction بيها [مشاكل](https://devblogs.microsoft.com/oldnewthing/20060927-07/?p=29563)، كحل بديل نگدر نستعمل [`()VirtualQuery`](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualquery) حتى نشوف اذا الpointer موجود بmemory page قابلة للقراءة لو لا:
+
+![](virtualquery.png)
+
+وصف اول argument صايغيه بطريقة تدوخ شوية، الزبدة انو تشتغل على اي pointer ضمن page معينة، مو الا الbase address مالها. المعلومات حتجينا بstruct من نوع `MEMORY_BASIC_INFORMATION`. خلي نشوف شنو الpages اللي موجوده بيها الpointers مال الpointer chain:
+
+![](1stptrchn.png)
+
+"وين اكو pages بهالأحجام؟" كلها 4 كيلوبايت بس Process Hacker ديعرض الpages المتلاصقة وخصائصها متماثلة سوية. الImage معناها هاي الpage بيها محتوى ملف الDLL (اللي هو `BCWDBK32.DLL`). الpointer اللي نريده صاير بمنطقة RW (Read-Write) لأن بديهيا هو مو ثابت وانما متغير:
+
+![](mem_image.png)
+
+الCommit معناها الpage جاهزة وقابلة للاستعمال مو مجرد محجوزة:
+
+![](commit.png)
+
+![](2ndptrchn.png)
+
+![](3rdptrchn.png)
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp16" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="15-19|21-22|24-25|27-28|30|78-82"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidPtr(void* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi))) {
+ return FALSE;
+ }
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borland.handle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return;
+ }
+ pid = *ptr;
+ }
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(pid)).c_str());
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+زين اذا اجانا pointer تايه وصار الpointer النهائي على حافة readable region؟ يعني مثلا فوگ احنا بدينا عند 0x7050000 والregion چان حجمها 256 كيلوبايت واللي وراها لزگ حجمها 128 كيلوبايت، حتى نوصل للحافة نحول للبايتات ونجمع ونطرح واحد (لأن دنحسب من الصفر):
+:::
+
+$$256 * 1024 + 128 * 1024 - 1 = \mathrm{0x5ffff}$$
+
+:::{dir="rtl"}
+ونحطها بدل 0x35008 حتى تنضاف للbase address مال `BCWDBK32.DLL`، الfunction مالتنا راح تنطي `TRUE` لأن الpointer بنفسه موجود بreadable page، بس لمن نقره القيمة اللي بي محنقره بايت وحدة وانما اربعة (لأن الDWORD اربع بايتات)، والبايتات ال3 البقية مو readable فيصير crash:
+:::
+
+![](validptr.png)
+
+![](scorp.png)
+
+![](term.png)
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp17" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="77-83|73"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidPtr(void* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borland.handle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return;
+ }
+
+ DWORD offsets[] = { 0x5ffff, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (IsValidPtr(ptr))
+ MessageDebug(DBG_INFO, "Valid pointer, trust me bro");
+ else
+ return;
+ if (!IsValidPtr((char*)ptr + 3))
+ MessageDebug(DBG_INFO, "Uhmm...");
+ pid = *ptr;
+ }
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(pid)).c_str());
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+دنسوي cast الى `*char` [حتى نجمع بس 3 بدل 4*3](https://www.c-faq.com/ptrs/explscale.html). بالنهاية حيكون عندنا:
+
+:::
+
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp18" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="77-80"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidPtr(void* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borland.handle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!(IsValidPtr(ptr) && IsValidPtr((char*)ptr + 3))) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return;
+ }
+ pid = *ptr;
+ }
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(pid)).c_str());
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+بدل ما ندوخ نفسنا بتفاصيل عمل الfunction كل ما نستعملها، نگدر نستبدلها بوحدة تسوي نفس الوظيفة بس فوگاها تچيك ان `ptr` و`ptr + 3` ضمن نفس الpage:
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp19" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="29-31|81"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borland.handle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return;
+ }
+ pid = *ptr;
+ }
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(pid)).c_str());
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+تعرف تگدر تحسب المعادلة الفوگ بدون ورقة وقلم؟
+:::
+
+
+\begin{align*}
+128 * 1024 &= 2^{7} * 2^{10} \\
+&= 2 * 2^{16} \\
+&= 2 * 2^{4^{4}} \\
+&= 2 * \mathrm{0x}10^{4} \\
+&= 2 * \mathrm{0x}10000 \\
+&= \mathrm{0x}20000 \\
+256 * 1024 &= 2 * (128 * 1024) \\
+&= 2 * \mathrm{0x}20000 \\
+&= \mathrm{0x}40000 \\
+256 * 1024 + 128 * 1024 - 1 &= \mathrm{0x}20000 + \mathrm{0x}40000 - 1 \\
+&= \mathrm{0x}60000 - 1 \\
+&= \mathrm{0x}5\mathrm{ffff}
+\end{align*}
+
+::: {dir="rtl"}
+خلي نحول الكود اللي كتبناه الى function ونفتح الprocess:
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp20" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="55|73-76|77-79|97-99"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+ if (GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(child.pid) + " handle: " + std::to_string((DWORD)(child.handle))).c_str());
+ }
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+![](pandh.png)
+
+::: {dir="rtl"}
+خلي نجرب شغلة:
+:::
+
+![](lolwut.gif)
+
+::: {dir="rtl"}
+
+احنا شغلنا برنامج بالبورلاند، طلعتلنا رسالة started injector.dll، وراها غلقنا البرنامج ودسنا ok. المفروض هسه باقي الكود يفشل، بس احنا مو بس دنحصل pid وانما handle هم. اوك خلي نگول الpid بقه بذاكرة البورلاند حتى بعد ما غلقنا البرنامج، شلون دنحصل handle؟ هالشي معناه `()OpenProcess` دتگدر تفتح process ميته.
+
+بالوندوز لمن تموت process حتبقى بالprocess table على هيئة زومبي طالما اكو handle الها بفد مكان، بس ما راح تبين بالTask Manager. خلي نكتب كود يچيك الexit code مال الprocess حتى نتأكد انو هيه بالفعل عايشه:
+:::
+
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp21" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="55-62|86-87"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+ if (GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(child.pid) + " handle: " + std::to_string((DWORD)(child.handle))).c_str());
+ }
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+
+حناخذ اسم البرنامج هم، حنحتاجه بعدين:
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp22" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="90-91|39"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+ if (GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(child.pid) + " handle: " + std::to_string((DWORD)(child.handle))).c_str());
+ }
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+
+::: {dir="rtl"}
+
+ممكن المستخدم دينفذ برنامج GUI. يجي ويه البورلاند هوايه examples موجودة بمجلد التنصيب، هذا واحد منهن:
+
+![](hdump.png)
+
+شلون نعرف اذا البرنامج عنده console؟ لاحظت بWindows 10 انو كل برامج الconsole عندها `conhost` كchild الها:
+
+![](conhost.png)
+
+بس لمن چيكت Windows 7 اكتشفت انو كل ال`conhosts` اللي بي صايرات children ل`csrss`، فمنگدر نستغل هالسلوك:
+
+![](csrss.png)
+
+بالنهاية استخدمت `()AttachConsole` لأن اذا فشل معناها الprocess مابيها console، لكن اذا نجح لازم نسوي `()FreeConsole` لأن الconsole I/O مالDLL/البورلاند حيروح لنافذة البرنامج، وبس تنغلق النافذة حيطير البورلاند وياها.
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp23" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="56-62|127-130"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+ MessageDebug(DBG_INFO, ("pid: " + std::to_string(child.pid)).c_str());
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+بالنسخة اللي وديتها للاستاذ چنت حاط function تاخذ اول child لبورلاند عنده console اذا اتباع الpointer chain فشل (باستثناء ادوات الconsole اللي تجي ويه البورلاند مثل الTurbo Debugger)، مع انو النسخة اللي داشتغل عليها اخذتها من موقع تدريسي بالقسم نفسه فإحتمالية كونها نفس النسخة اللي بحواسيب المختبر چانت عالية، بس گلت الاحتياط واجب. شغلة لاحظتها ان `()Process32Next` تنطيك zombie processes بWindows 10، بينما بWindows 7 لا.
+
+:::
+
+----
+
+> Thy grace may wing me to prevent his art,\
+> And thou like adamant draw mine iron heart.
+
+— John Donne, <cite>Holy Sonnet 1</cite>
+
+::: {dir="rtl"}
+
+ملفات الEXE تتبع صيغة الPortable Executable (PE) اللي تبدي بكم header وراها تحتوي على sections، اكو section للكود اسمه `text.` واكو للبيانات اسمه `data.`، العنوان اللي نريد نغيره موجود بsection اسمه `idata.` مخصص للimported functions (تذكر اللي حچيناه عن الload-time dynamic linking):
+
+![[المصدر](https://0xrick.github.io/win-internals/pe2/)](pe_overview.png)
+
+الصيغة بيها هوايه تفاصيل واكو عشرات المقالات عنها، يفضل تكون قاري وحدة منهن ([مثل هاي السلسلة](https://0xrick.github.io/win-internals/pe1/)) بس مو ضروري. الحلو بالPE ان الهياكل مالته داخل البرنامج بالقرص وبعد تحميل البرنامج للذاكرة متماثلات تقريبا، ماعدا اختلافات معدودة راح نشوف واحد منها بعد شوية. اللي راح اسويه هو ان احط برنامج مبني بالبورلاند بGhidra واخليها هي تشوفنا الطريق:
+
+![](gh1.png)
+
+مثل ما شفنا بالمخطط الفوگ، اول شي بالPE file هو الDOS header. برامج الMS-DOS والوندوز تتشارك بنفس الامتداد (EXE) فخلوا الPE اول شي يبدي بDOS header وبرنامج DOS بسيط حتى اذا واحد من ذاك الوكت حاول يشغل برنامج وندوز بداخل MS-DOS حتطلعله رسالة بدل لا يطلعله خطأ غريب يخليه دايخ. نگدر نگول ان كل برامج الوندوز اليوم بيها شظايا من الماضي.
+
+![](cannotintodos.png)
+
+بالDOS header اكو member اسمه `e_lfanew`، هذا الmember يحتوي على الRVA مال الNT headers (شوف المخطط). الRVA معناها Relative Virtual Address، اللي هو offset للbase address. بمعنى اذا `e_lfanew` قيمته 0x200 والbase address هو 0x00400000 فبداية الNT headers حتكون عند 0x00400200. خلي نكتب كود يقره الDOS header:
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp24" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="132-136,138-139"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <string>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+ #pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+الحلو ان كل الstructs اللي نحتاجها موجودة بالwindows headers، ميحتاج نعرف شي من يمنا او نقره الoffsets مال القيم مباشرة. هسه خلي نشوف شكو بالNT headers:
+
+![](gh2.png)
+
+الOptionalHeader بي هوايه امور متهمنا:
+
+![](gh3.png)
+
+بس بالأخير اكو شي اسمه DataDirectory، اللي هي عبارة عن array كل واحد من عناصرها عنده Size وVirtual Address (Relative):
+![](gh4.png)
+
+ال[documentation](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-data-directories-image-only) يگللنا شنو ذني العناصر:
+
+![](tables.png)
+
+وايضا موجود الها constants بالheaders:
+
+![](importdir.png)
+
+نكتب:
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp25" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="140-141,143"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+#pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+
+ IMAGE_NT_HEADERS nt_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)(baseaddress + (DWORD)dos_h.e_lfanew), &nt_h, sizeof(nt_h), nullptr);
+
+ DWORD idt_rva = nt_h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+اول شي موجود بال`idata.` هو الImport Directory Table (IDT) وهي عبارة عن array مال structs حجمها 20 بايت من نوع `IMAGE_IMPORT_DESCRIPTOR`، كل وحدة من عندهن تمثل DLL معين يستخدمه البرنامج ما عدا الأخيرة كلها مصفرة (هاي نستخدمها حتى نعرف وين نوگف لمن نسوي loop):
+
+![](gh5.png)
+
+شلون نعرف الstruct مصفرة بالكود؟ نچيك عنصر مهم منها، اذا مصفر فكلها مصفرة وبالتالي وصلنا للنهاية:
+:::
+
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp26" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="144-145|147-148,150-152"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+#pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+
+ IMAGE_NT_HEADERS nt_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)(baseaddress + (DWORD)dos_h.e_lfanew), &nt_h, sizeof(nt_h), nullptr);
+
+ DWORD idt_rva = nt_h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ PIMAGE_IMPORT_DESCRIPTOR idt_addr = (PIMAGE_IMPORT_DESCRIPTOR)(baseaddress + idt_rva);
+ IMAGE_IMPORT_DESCRIPTOR idt = { 0 };
+
+ for (;; idt_addr++) {
+ ReadProcessMemory(child.handle, idt_addr, &idt, sizeof(idt), nullptr);
+
+ if (!idt.FirstThunk)
+ break;
+ }
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+ال`P` ب`PIMAGE_IMPORT_DESCRIPTOR` معناها Pointer. لمن دنسوي increment ل`idt_addr` احنا دنضيف عشرين بايت مو واحد.
+
+بعد الimport descriptors تجي الImport Lookup Table (ILT) مال اول DLL يستخدمه البرنامج، كل واحد من عناصرها حجمه اربع بايتات ونوعه `IMAGE_THUNK_DATA` ويحتوي على RVA لstruct من نوع `IMAGE_IMPORT_BY_NAME`، العنصر الأخير ايضا صفر لنفس السبب:
+
+![](gh6.png)
+
+![](gh7.png)
+
+بعد الILT تجي الImport Address Table (IAT) مال اول DLL، اللي محتوياتها تكون نفس محتويات الILT لكن راح تتغير بعد تحميل البرنامج للذاكرة وايضا اخير عنصر منها صفر (ركزوا على القيمة اللي بالأزرگ، معليكم بالحچي اللي حاطته Ghidra):
+
+![](gh8.png)
+
+وراها تجي الILT والIAT مال ثاني DLL يستخدمه البرنامج. بعدها اسم اول DLL وراه اسم ثاني DLL:
+
+![](gh9.png)
+
+وراه تجي structs من نوع `IMAGE_IMPORT_BY_NAME` (اللي الRVA مالهن بالIAT والILT) لأول DLL، كل وحدة منهن اول شي تبدي ببايتين (الها استعمال بالواقع لكن البورلاند دائما يخليهن صفر) وراها يجي اسم الfunction اللي راح يستعملها البرنامج من الDLL:
+
+![](gh10.png)
+
+وراها تجي مال ثاني DLL وهكذا...
+
+اثناء تحميل البرنامج للذاكره، الloader راح يمشي على الIATs ويشيل الRVA مال كل `IMAGE_IMPORT_BY_NAME` (اللي تحتوي على اسم function معينة من الDLL) وراح يستبدلها بالعنوان الحقيقي لهاي الfunction ضمن الaddress space مال البرنامج. فبالتالي لمن البرنامج يسوي indirect jump لعنوان من IAT (مثل ما شفنا فوگ ويه `()ExitProcess`)، هو راح يسوي jump لعنوان الfunction الحقيقي بالذاكرة، مو للعنوان مال اسمها (بينما الILTs راح يبقن مثل ما هنه).
+
+بعناصر الIDT، الRVA مال اول عنصر من الIAT اسمه `FirstThunk` ومال الILT اسمه `OriginalFirstThunk`. احنا راح نمشي على الIAT والILT سويه حتى ناخذ أسم الfunction من الILT ونطابقه ويه عنوانها اللي ناخذه من الIAT:
+:::
+
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp27" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="153-156|158-160,162-163"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+#pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+
+ IMAGE_NT_HEADERS nt_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)(baseaddress + (DWORD)dos_h.e_lfanew), &nt_h, sizeof(nt_h), nullptr);
+
+ DWORD idt_rva = nt_h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ PIMAGE_IMPORT_DESCRIPTOR idt_addr = (PIMAGE_IMPORT_DESCRIPTOR)(baseaddress + idt_rva);
+ IMAGE_IMPORT_DESCRIPTOR idt = { 0 };
+
+ for (;; idt_addr++) {
+ ReadProcessMemory(child.handle, idt_addr, &idt, sizeof(idt), nullptr);
+
+ if (!idt.FirstThunk)
+ break;
+
+ PIMAGE_THUNK_DATA ilt_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.OriginalFirstThunk);
+ PIMAGE_THUNK_DATA iat_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.FirstThunk);
+ IMAGE_THUNK_DATA ilt = { 0 };
+ IMAGE_THUNK_DATA iat = { 0 };
+
+ for (;; iat_addr++, ilt_addr++) {
+ ReadProcessMemory(child.handle, ilt_addr, &ilt, sizeof(ilt), nullptr);
+ ReadProcessMemory(child.handle, iat_addr, &iat, sizeof(iat), nullptr);
+
+ if (!(ilt.u1.AddressOfData && iat.u1.AddressOfData))
+ break;
+
+ }
+ }
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+
+::: {dir="rtl"}
+احنا شنو اللي نريده من كل هذا؟
+
+نريد عنوان `()MessageBoxA`، حنسميه `MessageBoxA_addr`:
+
+![](msgboxiat.png)
+
+نريد عنوان `()ExitProcess` (حتى نستخدمه بالكود مالنا بعد ما اليوزر يغلق الMessageBox)، حنسميه `ExitProcess_addr`:
+
+![](exitprocessiat.png)
+
+نريد العنوان اللي موجود بي عنوان `()ExitProcess`، حتى نشيل عنوان `()ExitProcess` ونخلي بمكانه عنوان الكود مالنا، حنسميه `ExitProcess_addraddr`:
+
+![](exitprocessaddriat.png)
+
+المشكلة ان حجم الstrings ما متوفر، فإذا نريد نقراها كلها لازم نلملمها بايت بايت لحد ما نوصل للnull (او نقره فد 200 بايت، that also works). احنا منحتاج نسوي هالشي، `ExitProcess` و`MessageBoxA` ثنينهن 11 حرف (12 اذا تحسب الnull) فبس نحتاج نقره 11 بايت:
+
+
+:::
+
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp28" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="146-148|168-170|172-177|181-184"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+#pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+
+ IMAGE_NT_HEADERS nt_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)(baseaddress + (DWORD)dos_h.e_lfanew), &nt_h, sizeof(nt_h), nullptr);
+
+ DWORD idt_rva = nt_h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ PIMAGE_IMPORT_DESCRIPTOR idt_addr = (PIMAGE_IMPORT_DESCRIPTOR)(baseaddress + idt_rva);
+ IMAGE_IMPORT_DESCRIPTOR idt = { 0 };
+ DWORD ExitProcess_addraddr = 0;
+ DWORD ExitProcess_addr = 0;
+ DWORD MessageBoxA_addr = 0;
+
+ for (;; idt_addr++) {
+ ReadProcessMemory(child.handle, idt_addr, &idt, sizeof(idt), nullptr);
+
+ if (!idt.FirstThunk)
+ break;
+
+ PIMAGE_THUNK_DATA ilt_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.OriginalFirstThunk);
+ PIMAGE_THUNK_DATA iat_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.FirstThunk);
+ IMAGE_THUNK_DATA ilt = { 0 };
+ IMAGE_THUNK_DATA iat = { 0 };
+
+ for (;; iat_addr++, ilt_addr++) {
+ ReadProcessMemory(child.handle, ilt_addr, &ilt, sizeof(ilt), nullptr);
+ ReadProcessMemory(child.handle, iat_addr, &iat, sizeof(iat), nullptr);
+
+ if (!(ilt.u1.AddressOfData && iat.u1.AddressOfData))
+ break;
+
+ char name[12] = { 0 };
+ void* nameptr = (void*)(baseaddress + ilt.u1.AddressOfData + FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name));
+ ReadProcessMemory(child.handle, nameptr, &name, sizeof(name) - 1, nullptr);
+
+ if (strcmp(name, "ExitProcess") == 0) {
+ ExitProcess_addr = iat.u1.AddressOfData;
+ ExitProcess_addraddr = (DWORD)iat_addr;
+ } else if (strcmp(name, "MessageBoxA") == 0) {
+ MessageBoxA_addr = iat.u1.AddressOfData;
+ }
+ }
+ }
+
+ if (!(ExitProcess_addr && ExitProcess_addraddr && MessageBoxA_addr)) {
+ MessageDebug(DBG_ERROR, "couldn't get addresses");
+ return;
+ }
+
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+الوندوز يخلينا نسوي pages بغير processes، حنخصص page للstrings وpage للinstructions بداخل البرنامج الديتنفذ "يعني حتضيع 8 كيلوبايتات، خليهن بنفس الpage على الأقل ووفر 4 كيلو"، صح بس اشوف هيچي أرتب. حيكون عنوان الMessageBox هو أسم البرنامج الديتنفذ ومحتواها هو "Program terminated":
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp29" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="187-191|193-194|5"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <vector>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+#pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+
+ IMAGE_NT_HEADERS nt_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)(baseaddress + (DWORD)dos_h.e_lfanew), &nt_h, sizeof(nt_h), nullptr);
+
+ DWORD idt_rva = nt_h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ PIMAGE_IMPORT_DESCRIPTOR idt_addr = (PIMAGE_IMPORT_DESCRIPTOR)(baseaddress + idt_rva);
+ IMAGE_IMPORT_DESCRIPTOR idt = { 0 };
+ DWORD ExitProcess_addraddr = 0;
+ DWORD ExitProcess_addr = 0;
+ DWORD MessageBoxA_addr = 0;
+
+ for (;; idt_addr++) {
+ ReadProcessMemory(child.handle, idt_addr, &idt, sizeof(idt), nullptr);
+
+ if (!idt.FirstThunk)
+ break;
+
+ PIMAGE_THUNK_DATA ilt_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.OriginalFirstThunk);
+ PIMAGE_THUNK_DATA iat_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.FirstThunk);
+ IMAGE_THUNK_DATA ilt = { 0 };
+ IMAGE_THUNK_DATA iat = { 0 };
+
+ for (;; iat_addr++, ilt_addr++) {
+ ReadProcessMemory(child.handle, ilt_addr, &ilt, sizeof(ilt), nullptr);
+ ReadProcessMemory(child.handle, iat_addr, &iat, sizeof(iat), nullptr);
+
+ if (!(ilt.u1.AddressOfData && iat.u1.AddressOfData))
+ break;
+
+ char name[12] = { 0 };
+ void* nameptr = (void*)(baseaddress + ilt.u1.AddressOfData + FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name));
+ ReadProcessMemory(child.handle, nameptr, &name, sizeof(name) - 1, nullptr);
+
+ if (strcmp(name, "ExitProcess") == 0) {
+ ExitProcess_addr = iat.u1.AddressOfData;
+ ExitProcess_addraddr = (DWORD)iat_addr;
+ } else if (strcmp(name, "MessageBoxA") == 0) {
+ MessageBoxA_addr = iat.u1.AddressOfData;
+ }
+ }
+ }
+
+ if (!(ExitProcess_addr && ExitProcess_addraddr && MessageBoxA_addr)) {
+ MessageDebug(DBG_ERROR, "couldn't get addresses");
+ return;
+ }
+
+ std::vector<char> strings;
+ strings.insert(strings.end(), child.name, child.name + strlen(child.name) + 1);
+ DWORD msg_offset = strings.size();
+ char msg[] = "Program terminated";
+ strings.insert(strings.end(), msg, msg + sizeof(msg));
+
+ char* stringsmem = (char*)VirtualAllocEx(child.handle, nullptr, strings.size(), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ WriteProcessMemory(child.handle, stringsmem, strings.data(), strings.size(), nullptr);
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+خلي نشوف الfunction prototype مال `MessageBoxA`:
+
+![](msgboxproto.png)
+
+الcalling convention هو stdcall، فراح ندفع الparameters بالعكس للstack، حنبدي بأخير argument اللي يحدد شكل الMessageBox، حنختار `MB_OK` (اللي هي مجرد صفر) لأن بس نريد دگمة Ok وخلص:
+
+![](msgbox_utype.png)
+
+زين شلون ندفع صفر للstack؟ نگدر نستشير اي assembler:
+
+![](push0.png)
+
+
+
+الassembler انطانا 0x6a، اللي هو push imm8، يعني ياخذ operand حجمه 8 بت (بايت واحد) ويدفعه للstack (الoperand هنا هو 0x00):
+
+![Intel 64 and IA-32 Architectures Software Developer’s Manual: Instruction Set Reference, p. 1250](pushcodes.png)
+
+بعدها راح يجي الpointer مال عنوان الMessageBox:
+
+![](msgbox_lpcaption.png)
+
+
+
+كل pointer بنظام 32 بت حجمه 4 بايتات، فراح نستخدم وياه الopcode مال push imm32 من الجدول الفوگ (0x68):
+
+:::
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp30" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="196-204"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <vector>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+#pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+
+ IMAGE_NT_HEADERS nt_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)(baseaddress + (DWORD)dos_h.e_lfanew), &nt_h, sizeof(nt_h), nullptr);
+
+ DWORD idt_rva = nt_h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ PIMAGE_IMPORT_DESCRIPTOR idt_addr = (PIMAGE_IMPORT_DESCRIPTOR)(baseaddress + idt_rva);
+ IMAGE_IMPORT_DESCRIPTOR idt = { 0 };
+ DWORD ExitProcess_addraddr = 0;
+ DWORD ExitProcess_addr = 0;
+ DWORD MessageBoxA_addr = 0;
+
+ for (;; idt_addr++) {
+ ReadProcessMemory(child.handle, idt_addr, &idt, sizeof(idt), nullptr);
+
+ if (!idt.FirstThunk)
+ break;
+
+ PIMAGE_THUNK_DATA ilt_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.OriginalFirstThunk);
+ PIMAGE_THUNK_DATA iat_addr = (PIMAGE_THUNK_DATA)(baseaddress + idt.FirstThunk);
+ IMAGE_THUNK_DATA ilt = { 0 };
+ IMAGE_THUNK_DATA iat = { 0 };
+
+ for (;; iat_addr++, ilt_addr++) {
+ ReadProcessMemory(child.handle, ilt_addr, &ilt, sizeof(ilt), nullptr);
+ ReadProcessMemory(child.handle, iat_addr, &iat, sizeof(iat), nullptr);
+
+ if (!(ilt.u1.AddressOfData && iat.u1.AddressOfData))
+ break;
+
+ char name[12] = { 0 };
+ void* nameptr = (void*)(baseaddress + ilt.u1.AddressOfData + FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name));
+ ReadProcessMemory(child.handle, nameptr, &name, sizeof(name) - 1, nullptr);
+
+ if (strcmp(name, "ExitProcess") == 0) {
+ ExitProcess_addr = iat.u1.AddressOfData;
+ ExitProcess_addraddr = (DWORD)iat_addr;
+ } else if (strcmp(name, "MessageBoxA") == 0) {
+ MessageBoxA_addr = iat.u1.AddressOfData;
+ }
+ }
+ }
+
+ if (!(ExitProcess_addr && ExitProcess_addraddr && MessageBoxA_addr)) {
+ MessageDebug(DBG_ERROR, "couldn't get addresses");
+ return;
+ }
+
+ std::vector<char> strings;
+ strings.insert(strings.end(), child.name, child.name + strlen(child.name) + 1);
+ DWORD msg_offset = strings.size();
+ char msg[] = "Program terminated";
+ strings.insert(strings.end(), msg, msg + sizeof(msg));
+
+ char* stringsmem = (char*)VirtualAllocEx(child.handle, nullptr, strings.size(), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ WriteProcessMemory(child.handle, stringsmem, strings.data(), strings.size(), nullptr);
+
+ std::vector<unsigned char> instructions;
+ instructions.insert(instructions.end(), {
+ // push MB_OK
+ 0x6A, 0x00,
+ // push "program.exe"
+ 0x68,
+ });
+ instructions.insert(instructions.end(), (char*)&stringsmem, (char*)&stringsmem + sizeof(stringsmem));
+}
+</script></code></pre>
+</section>
+
+</div>
+</div>
+</div>
+<figcaption>injector.cpp</figcaption></figure>
+
+::: {dir="rtl"}
+بعدها حتجي رسالة الMessageBox:
+:::
+
+![](msgbox_lptext.png)
+
+
+<figure>
+<div class="centerslide">
+<div class="reveal cpp31" style="width: 60vw; height: 20vh;">
+<div class="slides">
+<section>
+<pre><code data-trim data-line-numbers="205-210"><script type="text/template">
+// Exclude rarely-used stuff from Windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Psapi.h>
+#include <vector>
+
+enum DebugLevel { DBG_ERROR = 1, DBG_INFO};
+DebugLevel CurrentDebugLevel;
+void MessageDebug(DebugLevel minimumlevel, const char *s) {
+ if (CurrentDebugLevel >= minimumlevel) {
+ MessageBoxA(nullptr, s, "injector.dll", 0);
+ }
+}
+
+BOOL IsValidDWORDPtr(DWORD* ptr) {
+ MEMORY_BASIC_INFORMATION mbi = { 0 };
+ if (!VirtualQuery(ptr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ if (mbi.Protect != PAGE_READWRITE)
+ return FALSE;
+
+ if (mbi.State != MEM_COMMIT)
+ return FALSE;
+
+ if (!(mbi.Type & (MEM_IMAGE | MEM_PRIVATE)))
+ return FALSE;
+
+ char* maxpageptr = (char*)mbi.BaseAddress + mbi.RegionSize - 1;
+ if ((char*)ptr + 3 > maxpageptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct Process {
+ HANDLE handle;
+ DWORD pid;
+ char name[MAX_PATH];
+};
+
+DWORD GetBaseAddress(HANDLE phandle, char *modname) {
+ HMODULE modules[1024];
+ DWORD totalbytes;
+ EnumProcessModules(phandle, modules, sizeof(modules), &totalbytes);
+ char modulefilename[MAX_PATH];
+ for (DWORD i = 0; i < totalbytes / sizeof(HMODULE); i++) {
+ GetModuleBaseNameA(phandle, modules[i], modulefilename, sizeof(modulefilename));
+ if (strcmp(modname, modulefilename) == 0) {
+ return (DWORD)modules[i];
+ }
+ }
+ return 0;
+}
+
+BOOL CheckConsole(DWORD pid) {
+ if (AttachConsole(pid)) {
+ FreeConsole();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CheckZombie(HANDLE phandle) {
+ DWORD exitcode = 0;
+ GetExitCodeProcess(phandle, &exitcode);
+ if (exitcode != STILL_ACTIVE) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL GetChildByMem(HANDLE borlandhandle, Process* child) {
+ DWORD bcwdbk_baseaddress = GetBaseAddress(borlandhandle, "BCWDBK32.DLL");
+ if (!bcwdbk_baseaddress) {
+ MessageDebug(DBG_ERROR, "couldn't find BCWDBK32.DLL");
+ return FALSE;
+ }
+
+ DWORD offsets[] = { 0x35008, 0x40, 0x38 };
+ DWORD pid = bcwdbk_baseaddress;
+ for (auto i : offsets) {
+ DWORD* ptr = (DWORD*)(pid + i);
+ if (!IsValidDWORDPtr(ptr)) {
+ MessageDebug(DBG_ERROR, "couldn't follow pointer chain");
+ return FALSE;
+ }
+ pid = *ptr;
+ }
+
+ HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
+ if (!phandle)
+ return FALSE;
+
+ if (CheckZombie(phandle))
+ return FALSE;
+
+ if (!GetModuleBaseNameA(phandle, nullptr, child->name, MAX_PATH))
+ return FALSE;
+
+ child->handle = phandle;
+ child->pid = pid;
+ return TRUE;
+}
+
+extern "C" void __stdcall inject(int dbglvl);
+
+void __stdcall inject(int dbglvl) {
+#pragma comment(linker, "/export:inject=_inject@4")
+ CurrentDebugLevel = (DebugLevel)dbglvl;
+ MessageDebug(DBG_INFO, "started injector.dll");
+
+ Process borland = { 0 };
+ Process child = { 0 };
+
+ borland.pid = GetCurrentProcessId();
+ borland.handle = OpenProcess(PROCESS_ALL_ACCESS, 0, borland.pid);
+ if (!borland.handle) {
+ return;
+ }
+
+ if (!GetChildByMem(borland.handle, &child)) {
+ MessageDebug(DBG_ERROR, "couldn't find the child");
+ return;
+ }
+
+ if (!CheckConsole(child.pid)) {
+ MessageDebug(DBG_INFO, "not a console program");
+ return;
+ }
+
+ DWORD baseaddress = GetBaseAddress(child.handle, child.name);
+ if (!baseaddress) {
+ MessageDebug(DBG_ERROR, "no base address detected");
+ return;
+ }
+
+ IMAGE_DOS_HEADER dos_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)baseaddress, &dos_h, sizeof(dos_h), nullptr);
+
+ IMAGE_NT_HEADERS nt_h = { 0 };
+ ReadProcessMemory(child.handle, (void*)(baseaddress + (DWORD)dos_h.e_lfanew), &nt_h, sizeof(nt_h), nullptr);
+
+ DWORD idt_rva = nt_h.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ PIMAGE_IMPORT_DESCRIPTOR idt_addr = (PIMAGE_IMPORT_DESCRIPTOR)(baseaddress + idt_rva);
+ IMAGE_IMPORT_DESCRIPTOR idt = { 0 };
+ DWORD ExitProcess_addraddr = 0;
+ DWORD ExitProcess_addr = 0;
+ DWORD MessageBoxA_addr = 0;
+
+ for (;; idt_addr++) {
+ ReadProcessMemory(child.handle, idt_addr, &idt, sizeof(idt), nullptr);
+