حل وظيفة مع pwnable.kr 17 - memcpy. محاذاة البيانات

صورة

في هذه المقالة ، سنتعامل مع محاذاة البيانات ، ونحل أيضًا المهمة السابعة عشرة من موقع pwnable.kr .

المعلومات التنظيمية
خاصةً لأولئك الذين يرغبون في تعلم شيء جديد وتطويره في أي من مجالات أمن المعلومات والحاسوب ، سأكتب وأتحدث عن الفئات التالية:

  • PWN.
  • التشفير (التشفير) ؛
  • تقنيات الشبكات (الشبكة) ؛
  • عكس (الهندسة العكسية) ؛
  • إخفاء المعلومات (Stegano) ؛
  • بحث واستغلال مواطن الضعف WEB.

بالإضافة إلى ذلك ، سوف أشارك تجربتي في الطب الشرعي للكمبيوتر ، وتحليل البرامج الضارة والبرامج الثابتة ، والهجمات على الشبكات اللاسلكية وشبكات المناطق المحلية ، وإجراء عمليات pentests واستغلال الكتابة.

حتى تتمكن من معرفة المقالات الجديدة والبرامج والمعلومات الأخرى ، أنشأت قناة في Telegram ومجموعة لمناقشة أي مشاكل في مجال التصنيف الدولي للأمراض. أيضًا ، سأدرس شخصيًا طلباتك الشخصية وأسئلتك واقتراحاتك وتوصياتك شخصيًا وسأجيب على الجميع .

يتم توفير جميع المعلومات للأغراض التعليمية فقط. لا يتحمل مؤلف هذا المستند أية مسؤولية عن أي ضرر يلحق بشخص ما نتيجة استخدام المعرفة والأساليب التي تم الحصول عليها نتيجة لدراسة هذا المستند.

محاذاة البيانات


تعد محاذاة البيانات في ذاكرة الوصول العشوائي للكمبيوتر ترتيبًا خاصًا للبيانات الموجودة في الذاكرة للوصول بشكل أسرع. عند العمل مع الذاكرة ، تستخدم العمليات كلمة الآلة كوحدة رئيسية. يمكن أن يكون للأنواع المختلفة من المعالجات أحجام مختلفة: واحد ، اثنان ، أربعة ، ثمانية ، إلخ. بايت. عند حفظ كائنات في الذاكرة ، قد يحدث أن يتجاوز بعض الحقول حدود الكلمات هذه. يمكن لبعض المعالجات العمل مع البيانات غير المحاذاة لفترة أطول من البيانات المحاذاة. ولا يمكن للمعالجات غير المعقدة عمومًا العمل مع البيانات غير المحاذاة.

من أجل تخيل أفضل نموذج للبيانات المحاذاة وغير المحاذاة ، خذ بعين الاعتبار مثال على الكائن التالي - بنية البيانات.

struct Data{ int var1; void* mas[4]; }; 

نظرًا لأن حجم المتغير int في x32 و x 64 المعالجات ليس 4 بايت ، وقيمة void * variable هي 4 و 8 بايت ، على التوالي ، سيتم تمثيل هذه البنية لمعالجات x32 و x64 في الذاكرة كما يلي.

صورة

لن تعمل معالجات X64 مع مثل هذا الهيكل ، لأن البيانات غير محاذاة. لمحاذاة البيانات ، من الضروري إضافة حقل 4 بايت آخر إلى الهيكل.

 struct Data{ int var1; int addition; void* mas[4]; }; 

وبالتالي ، سيتم محاذاة بيانات بنية البيانات لمعالجات x64 في الذاكرة.

صورة

حل وظيفة Memcpy


نضغط على أيقونة توقيع memcpy وقيل لنا إننا بحاجة إلى الاتصال عبر SSH بضيف كلمة المرور.

كما أنها توفر شفرة المصدر.
 // compiled with : gcc -o memcpy memcpy.c -m32 -lm #include <stdio.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <sys/mman.h> #include <math.h> unsigned long long rdtsc(){ asm("rdtsc"); } char* slow_memcpy(char* dest, const char* src, size_t len){ int i; for (i=0; i<len; i++) { dest[i] = src[i]; } return dest; } char* fast_memcpy(char* dest, const char* src, size_t len){ size_t i; // 64-byte block fast copy if(len >= 64){ i = len / 64; len &= (64-1); while(i-- > 0){ __asm__ __volatile__ ( "movdqa (%0), %%xmm0\n" "movdqa 16(%0), %%xmm1\n" "movdqa 32(%0), %%xmm2\n" "movdqa 48(%0), %%xmm3\n" "movntps %%xmm0, (%1)\n" "movntps %%xmm1, 16(%1)\n" "movntps %%xmm2, 32(%1)\n" "movntps %%xmm3, 48(%1)\n" ::"r"(src),"r"(dest):"memory"); dest += 64; src += 64; } } // byte-to-byte slow copy if(len) slow_memcpy(dest, src, len); return dest; } int main(void){ setvbuf(stdout, 0, _IONBF, 0); setvbuf(stdin, 0, _IOLBF, 0); printf("Hey, I have a boring assignment for CS class.. :(\n"); printf("The assignment is simple.\n"); printf("-----------------------------------------------------\n"); printf("- What is the best implementation of memcpy? -\n"); printf("- 1. implement your own slow/fast version of memcpy -\n"); printf("- 2. compare them with various size of data -\n"); printf("- 3. conclude your experiment and submit report -\n"); printf("-----------------------------------------------------\n"); printf("This time, just help me out with my experiment and get flag\n"); printf("No fancy hacking, I promise :D\n"); unsigned long long t1, t2; int e; char* src; char* dest; unsigned int low, high; unsigned int size; // allocate memory char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); size_t sizes[10]; int i=0; // setup experiment parameters for(e=4; e<14; e++){ // 2^13 = 8K low = pow(2,e-1); high = pow(2,e); printf("specify the memcpy amount between %d ~ %d : ", low, high); scanf("%d", &size); if( size < low || size > high ){ printf("don't mess with the experiment.\n"); exit(0); } sizes[i++] = size; } sleep(1); printf("ok, lets run the experiment with your configuration\n"); sleep(1); // run experiment for(i=0; i<10; i++){ size = sizes[i]; printf("experiment %d : memcpy with buffer size %d\n", i+1, size); dest = malloc( size ); memcpy(cache1, cache2, 0x4000); // to eliminate cache effect t1 = rdtsc(); slow_memcpy(dest, src, size); // byte-to-byte memcpy t2 = rdtsc(); printf("ellapsed CPU cycles for slow_memcpy : %llu\n", t2-t1); memcpy(cache1, cache2, 0x4000); // to eliminate cache effect t1 = rdtsc(); fast_memcpy(dest, src, size); // block-to-block memcpy t2 = rdtsc(); printf("ellapsed CPU cycles for fast_memcpy : %llu\n", t2-t1); printf("\n"); } printf("thanks for helping my experiment!\n"); printf("flag : ----- erased in this source code -----\n"); return 0; } 


صورة

عند الاتصال ، نرى الشعار المقابل.

صورة

دعونا نعرف ما هي الملفات الموجودة على الخادم ، وكذلك ما هي الحقوق التي لدينا.

صورة

لدينا ملف التمهيدي. بعد قراءتها ، علمنا أن البرنامج يعمل على المنفذ 9022.

صورة

اتصل بالمنفذ 9022. نقدم لنا تجربة - مقارنة الإصدار البطيء والسريع من memcpy. بعد ذلك ، سيقوم البرنامج بإدخال رقم في فترة زمنية معينة وإصدار تقرير حول مقارنة الإصدارات البطيئة والسريعة من الوظيفة. هناك شيء واحد: التجارب 10 والتقارير - 5.

صورة

دعنا نرتب لماذا. ابحث عن المكان في الكود لمقارنة النتائج.

صورة

كل شيء بسيط ، أول ما يسمى slow_memcpy ، ثم fast_memcpy. ولكن في تقرير البرنامج ، هناك استنتاج حول الإصدار البطيء للدالة ، وعندما يتم استدعاء التنفيذ السريع ، يتعطل البرنامج. دعونا نرى رمز التنفيذ السريع.

صورة

يتم النسخ باستخدام وظائف المجمع. نحدد بواسطة الأوامر أن هذا هو SSE2. كما هو مذكور هنا : يستخدم SSE2 ثمانية سجلات 128 بت (xmm0 إلى xmm7) المضمنة في بنية x86 مع مقدمة ملحق SSE ، كل منها يعامل كقيمتين متتاليتين بدقة الفاصلة العائمة. علاوة على ذلك ، يعمل هذا الرمز مع البيانات المحاذاة.

صورة

صورة

وبالتالي ، عند التعامل مع البيانات غير المحاذية ، قد يتعطل البرنامج. يتم تنفيذ المحاذاة بواسطة 128 بت ، أي 16 بايت ، لذلك يجب أن تكون الكتل تساوي 16. نحتاج إلى معرفة عدد البايتات الموجودة بالفعل في الكتلة الأولى على الكومة (دع X) ثم يجب علينا كل نقل البرنامج أكبر عدد من البايتات (السماح Y) لذلك X + Y)٪ 16 كانت 0.

نظرًا لأن جميع العمليات تشغل كتل الكومة التي تتكون من مضاعفات اثنين ، يتم تكرارها على X كـ 2 ، 4 ، 8 ، إلخ حتى 16.

صورة

صورة

كما ترون ، مع X = 4 ، يتم تشغيل البرنامج بنجاح.

صورة

نحصل على قذيفة ، وقراءة العلم ، والحصول على 10 نقطة.

صورة

يمكنك الانضمام إلينا على Telegram . في المرة القادمة سوف نتعامل مع تجاوز سعة الكومة.

Source: https://habr.com/ru/post/ar462895/


All Articles