
مجموعة أدوات للهندسة العكسية والاختبار العشوائي للتطبيقات القائمة على Protobuf
Protobuf هو تنسيق تسلسل (serialization) تم تطويره بواسطة Google ويُستخدم في عدد متزايد من تطبيقات Android والويب وسطح المكتب والمزيد. يتكون من لغة لتعريف هياكل البيانات، ثم يتم تجميعها إلى كود أو نوع آخر من الهياكل اعتمادًا على التنفيذ المستهدف.
pbtk (مجموعة أدوات Protobuf) هي مجموعة متكاملة من البرامج النصية، يمكن الوصول إليها عبر واجهة مستخدم رسومية موحدة، توفر ميزتين رئيسيتين:
استخراج هياكل Protobuf من البرامج، وتحويلها مرة أخرى إلى ملفات .proto قابلة للقراءة، مع دعم العديد من التطبيقات:
تحرير البيانات وإعادة إرسالها وتزويدها بالمعلومات المضللة (fuzzing) المرسلة إلى نقاط نهاية شبكة Protobuf، عبر واجهة رسومية مفيدة تتيح لك تحرير الحقول المباشرة لرسالة Protobuf وعرض النتيجة.
يتطلب PBTK Python ≥ 3.5 و PySide 6 و Python-Protobuf 3 ومجموعة من البرامج القابلة للتنفيذ (chromium, jad, dex2jar...) لتشغيل نصوص الاستخراج.
يمكن لمستخدمي Ubuntu تثبيته باستخدام snap:
$ sudo snap install pbtk
$ pbtk
يمكن لمستخدمي Archlinux التثبيت مباشرة من خلال الحزمة:
$ yay -S pbtk-git
$ pbtk
في معظم التوزيعات الأخرى، قد ترغب في تشغيله مباشرة:
# For Ubuntu/Debian testing derivates:
$ sudo apt install python3-pip git openjdk-8-jre python3-qtpy-pyside6
# Then, using UV:
$ sudo snap install --classic astral-uv
$ uv tool install pbtk
$ pbtk
# Or using pipx:
$ sudo apt install pipx
$ pipx install pbtk
$ pbtk
يتم دعم Windows أيضًا (مع نفس الوحدات المطلوبة). بمجرد تشغيل الواجهة الرسومية، يجب أن تحذرك بشأن ما ينقصك اعتمادًا على ما تحاول القيام به.
يمكن تشغيل الواجهة الرسومية عبر البرنامج النصي الرئيسي:
pbtk
يمكن أيضًا استخدام البرامج النصية التالية بشكل مستقل، بدون واجهة رسومية:
pbtk-jar-extract [-h] input_file [output_dir]
pbtk-from-binary [-h] input_file [output_dir]
pbtk-web-extract [-h] input_url [output_dir] # Needs update to work as of 2026
عند التثبيت من snap، تختلف الأوامر تمامًا:
pbtk.jar-extract [-h] input_file [output_dir]
pbtk.from-binary [-h] input_file [output_dir]
pbtk.web-extract [-h] input_url [output_dir]
يمكن تشغيل الواجهة الرسومية عبر البرنامج النصي الرئيسي:
uv sync # Download dependencies to the .venv folder
source .venv/bin/activate # Put the local scripts in $PATH for the current shell session
uv tool install -e . # Put the local scripts in $PATH all time
pbtk
يمكن أيضًا استخدام البرامج النصية التالية بشكل مستقل، بدون واجهة رسومية:
pbtk-jar-extract [-h] input_file [output_dir]
pbtk-from-binary [-h] input_file [output_dir]
pbtk-web-extract [-h] input_url [output_dir] # Needs update to work as of 2026
لنفترض أنك تقوم بالهندسة العكسية لتطبيق Android. لقد استكشفت التطبيق قليلاً باستخدام أداة التفكيك (decompiler) المفضلة لديك، واكتشفت أنه ينقل Protobuf كبيانات POST عبر HTTPS بطريقة نموذجية.
تفتح PBTK ويتم استقبالك بطريقة معبرة:

الخطوة الأولى هي تحويل ملفات .proto الخاصة بك إلى تنسيق نصي. إذا كنت تستهدف تطبيق Android، فإن إسقاط ملف APK والانتظار يجب أن يقوم بالعمل السحري! (إلا إذا كان تنفيذًا غريبًا حقًا)

بعد ذلك، تنتقل إلى ~/.pbtk/protos/<اسم تطبيقك> (إما عبر سطر الأوامر، أو الزر الموجود أسفل شاشة الترحيب لفتح متصفح الملفات، بالطريقة التي تفضلها). جميع ملفات .proto للتطبيق موجودة بالفعل هنا.
بالعودة إلى أداة التفكيك الخاصة بك، عثرت على الفئة التي تقوم ببناء البيانات المرسلة إلى نقطة نهاية HTTPS التي تهمك. تقوم بتسلسل رسالة Protobuf عن طريق استدعاء فئة مكونة من كود مولّد.

يجب أن يكون لهذه الفئة الأخيرة تطابق تام داخل دليل .proto الخاص بك (أي com.foo.bar.a.b سيتطابق مع com/foo/bar/a/b.proto). على أي حال، البحث عن اسمها (grep) يجب أن يمكنك من الإشارة إليها.
هذا رائع: الشيء التالي هو الانتقال إلى الخطوة 2، واختيار ملف .proto المدخل المطلوب، وتعبئة بعض المعلومات حول نقطة النهاية الخاصة بك.

يمكنك أيضًا إعطاء بعض عينات البيانات الأولية لـ Protobuf، التي تم إرسالها إلى نقطة النهاية هذه، والتي تم التقاطها عبر mitmproxy أو Wireshark، وتقوم بلصقها في نموذج مشفر بالنظام الست عشري (hex).
الخطوة 3 تتعلق بالجزء الممتع من النقر على الأزرار ورؤية ما يحدث! لديك عرض شجري يمثل كل حقل في هيكل Protobuf (الحقول المتكررة تنتهي بـ "+"، والحقول المطلوبة ليس لها مربعات اختيار).

فقط قم بتحويم المؤشر فوق حقل للحصول على التركيز. إذا كان الحقل من نوع عدد صحيح، استخدم عجلة الماوس لزيادة/تقليل قيمته. تظهر معلومات التعداد (enum) أيضًا عند التحويم.
ها هو الأمر! يمكنك تحديد معنى كل حقل بذلك. إذا قمت باستخراج ملفات .proto من كود مصغر (minified)، يمكنك إعادة تسمية الحقول وفقًا لما تلاحظه أنها تعنيه، عن طريق النقر على أسمائها.
هندسة عكسية سعيدة! 👌 🎉
يقوم PBTK بتخزين معلومات .proto المستخرجة في ~/.pbtk/protos/ (أو %APPDATA%\pbtk\protos على Windows).
يمكنك نقل البيانات إلى هذا الدليل أو منه، أو إعادة تسميتها، أو تحريرها، أو مسحها مباشرة من خلال متصفح الملفات ومحرر النصوص المعتادين، فهذه هي الطريقة المتوقعة ولن تتعارض مع PBTK.
يتم تخزين نقاط النهاية المستندة إلى HTTP في ~/.pbtk/endpoints/ ككائنات JSON. هذه الكائنات هي مصفوفات من أزواج معلومات الطلب/الاستجابة، والتي تبدو كالتالي:
[{
"request": {
"transport": "pburl",
"proto": "www.google.com/VectorTown.proto",
"url": "https://www.google.com/VectorTown",
"pb_param": "pb",
"samples": [{
"pb": "!....",
"hl": "fr"
}]
},
"response": {
"format": "other"
}
}]
يستخدم PBTK نوعين من الوحدات القابلة للتوصيل داخليًا: المستخلصات (extractors) والناقلات (transports).
يتم تعريف المستخلصات في src/pbtk/extractors/*.py. يتم تعريفها كطريقة مسبوقة بمزين (decorator)، على النحو التالي:
@register_extractor(name = 'my_extractor',
desc = 'Extract Protobuf structures from Foobar code (*.foo, *.bar)',
depends={'binaries': ['foobar-decompiler']})
def my_extractor(path):
# Load contents of the `path` input file and do your stuff...
# Then, yield extracted .protos using a generator:
for i in do_your_extraction_work():
yield proto_name + '.proto', proto_contents
# Other kinds of information can be yield, such as endpoint information or progress to display.
يتم تعريف الناقلات في src/pbtk/utils/transports.py. يتم تعريفها كفئة مسبوقة بمزين، على النحو التالي:
@register_transport(
name = 'my_transport',
desc = 'Protobuf as raw POST data',
ui_data_form = 'hex strings'
)
class MyTransport():
def __init__(self, pb_param, url):
self.url = url
def serialize_sample(self, sample):
# We got a sample of input data from the user.
# Verify that it is valid in the form described through "ui_data_form" parameter, fail with an exception or return False otherwise.
# Optionally modify this data prior to returning it.
bytes.fromhex(sample)
return sample
def load_sample(self, sample, pb_msg):
# Parse input data into the provided Protobuf object.
pb_msg.ParseFromString(bytes.fromhex(sample))
def perform_request(self, pb_data, tab_data):
# Perform a request using the provided URL and Protobuf object, and optionally other transport-specific side data.
return post(url, pb_data.SerializeToString(), headers=USER_AGENT)
يمكن أن تأتي التالية في الإصدارات المستقبلية:
لقد حاولت جاهدًا إنتاج كود قابل للقراءة ومعلق عليه بشكل شامل (باستثناء الأجزاء التي تصف نفسها بنفسها، مثل توصيل إشارات الواجهة الرسومية) لمعظم الوحدات، حتى تتمكن من المساهمة.
تم إصدار pbtk تحت ترخيص GNU GPL (أنا، بموجب هذا، إلخ.).
لا توجد قاعدة رسمية لحالة الأحرف لاسم المشروع، القاعدة هي فقط اتباع قلبك ❤