Reverse Code Engineering

逆向工程代码 обратная код машиностроения

Reverse Code Engineering

逆向工程代码 обратная код машиностроения

Reverse Code Engineering

Share what I know............... learn what I don’t

آخرین نظرات

۱۰ مطلب با کلمه‌ی کلیدی «تشخیص کدهای اسمبلی» ثبت شده است

با سلام خدمت دوستان.

خوب همونطور که در پست های قبل مشاهده کردید اکثرا به مبحث "تشخیص ساختارهای برنامه نویسی در اسمبلی" پرداخته شده, سعیم بر این بود تا بتونم این سری آموزشی رو به اتمام برسونم و خوشبختانه هم تموم شد. 

همونطور که میدونید اینها مباحث فصل هفتم کتاب Practical Malware Analysis هستش. ولی کامل نیست و برای کامل شدن این قضیه من یه سری فایل اجرایی قرار میدم که نه تنها شامل مباحث گفته شده هستش بلکه ساختارهای دیگه ای رو هم شامل میشه و شما میتونید با آنالیز اونها توانایی خودتون رو توی این زمینه افزایش بدید. این فایل ها course بندی شده از ساده به سخت.

دانلود

یه سری فایل دیگه هم بود که من فراموش کرده بودم, ولی الان لینک دانلودش رو گذاشتم

دانلود2

موفق باشید.


لیست های پیوندی ساختارهای داده ای هستند که محتوی یک زنجیره ای از رکوردهای داده هستند, و هر رکورد شامل یک فیلد هستش که محتوی یک ارجاع (پیوند) به رکورد بعدی در این زنجیره هستش.

عمده اصلی استفاده از لیست های پیوندی نسبت به آرایه این است که ترتیب آیتم های پیوندی می تواند متفاوت از ترتیبش در آیتم های داده ای نگهداری شده در حافظه یا دیسک باشد.

ازینرو لیست های پیوندی اجازه قرار دادن یا حذف کردن یک گره رو در هر نقطه از لیست را می دهند.

 

به کد زیر توجه کنید:

 

struct node

{

   int x;

   struct node * next;

};

 

typedef struct node pnode;

 

void main()

{

   pnode * curr, * head;

   int i;

 

   head = NULL;

 

   for(i=1;i<=10;i++) (1)

   {

      curr = (pnode *)malloc(sizeof(pnode));

      curr->x = i;

      curr->next  = head;

      head = curr;

   }

 

   curr = head;

 

   while(curr) (2)

   {

      printf("%d\n", curr->x);

      curr = curr->next ;

   }

}


ساختارها (یا بصورت اختصار struct ها) شبیه آرایه ها هستند با این تفاوت که شامل انواع مختلفی از عناصر هستند. بد افزار نویس ها ازین برای اطلاعات گروهی استفاده می کنند. گاهی وقتا استفاده از ساختار ها بجای یه سری از متغیرهای مختلف مفید واقع میشه و کارو راحت می کنه مخصوصا اگه به گروهی از متغیرها برای یه کار نیاز داشته باشیم.توابع API ویندوز هم اکثر اوقات از ساختارها استفاده می کنند.


struct my_structure { 

     int x[5];

     char y;

     double z;

};

 

struct my_structure *gms; 

 

void test(struct my_structure *q)

{

     int i;

     q->y = 'a';

     q->z = 15.6;

     for(i = 0; i<5; i++){

           q->x[i] = i;

     }

}

 

void main()

{

     gms = (struct my_structure *) malloc(

     sizeof(struct my_structure));

     test(gms);

}

از آرایه ها برای تعریف یک مجموعه منظم از عناصر داده ای مشابه استفاده می شود.

بد افزارها گاهی اوقات از آرایه برای اشاره به رشته هایی که محتوی چندین hostname که بعنوان گزینه هایی برای ارتباط استفاده می شود, استفاده می کنند.


دو آرایه در مثال زیر تعریف شده که هر دو در هنگام تکرار حلقه تنظیم می شوند.

آرایه a بصورت محلی و آرایه b بصورت عمومی تعریف شده که این تفاوت را در کد دیس اسمبل شده مشاهده خواهید کرد.


int b[5] = {123,87,487,7,978};

void main()

{

   int i;

   int a[5];

 

   for(i = 0; i<5; i++)

   {

      a[i] = i;

      b[i] = i;

   }

}


توی اسمبلی آرایه ها با استفاده از آدرس مبنا بعنوان یک نقطه ی شروع قابل دستیابی هستند.سازه هر عنصر همیشه واضع نیست اما آنرا میتوان با توجه به چگونگی تنظیم شدن اندیس آرایه متوجه شد.


00401006        mov     [ebp+var_18], 0

0040100D        jmp     short loc_401018

0040100F loc_40100F:

0040100F        mov     eax, [ebp+var_18]

00401012        add     eax, 1

00401015        mov     [ebp+var_18], eax

00401018 loc_401018:

00401018        cmp     [ebp+var_18], 5

0040101C        jge     short loc_401037

0040101E        mov     ecx, [ebp+var_18]

00401021        mov     edx, [ebp+var_18]

00401024        mov     [ebp+ecx*4+var_14], edx 1(1)

00401028        mov     eax, [ebp+var_18]

0040102B        mov     ecx, [ebp+var_18]

0040102E        mov     dword_40A000[ecx*4], eax 2(2)

00401035        jmp     short loc_40100F


در اینجا آدرس مبنای آرایه b برابر است با dword_40A000 و آدرس مبنای آرایه a برابر با var_14 میباشد.بخاطر اینکه هر دو آرایه عددی هستند, هر عنصر 4 بایت سایز دارد.

هر چند دستورات (1) و (2) برای دسترسی به دو آرایه متفاوت هستند در هر دو مورد ecx بعنوان اندیس استفاده شده است, که در 4 ضرب می شود تا سایز هر عنصر حساب شود. مقدار نتیجه به آدرس مبنای آرایه برای دسترسی به عنصر آرایه مدنظر جمع می شود.

سوییچ خیلی زیاد توسط برنامه نویس ها (مخصوصا بدافزارنویس ها) برای گرفتن تصمیم هایی مبتنی بر یک کاراکتر یا عدد استفاده می شود.

دستور سوییچ به دو صورت رایج کامپایل می شود: استفاده از استایل if یا جداول پرش.


قبل از اینکه بخوایم انواع فراخوانی رو بررسی کنیم یه توضیح اجمالی در مورد حافظه و stack میدم تا پیش زمینه لازم رو داشته باشید.

حافظه اصلی یا همون رم سیستم برای یک برنامه به 4 قسمت اصلی زیر تقسیم میشه

قسمتی که برای ما در اینجا مهمه Stack هستش.
پشته به عبارت کلی قطعه ای از حافظه هستش که برای نگهداری اطلاعات تابع از جمله متغیرهای محلی , کنترل جریان و... استفاده میشه. با push میتوان عنصری داخل پشته قرار داد و با pop میتوان عنصری از داخل پشته برداشت کرد یا باصطلاح بازیابی کرد.
همونطور که میدونید ساختار پشته بصورت LIFO است یعنی اگه ما 1 , 2 و 3 رو به ترتیب درون پشته قرار دهیم (push) , اولین عنصری که میتونیم برداشت کنیم pop) 3) خواهد بود پس هر عنصری جدید روی عنصر ماقبل خودش قرار میگیره.
در معماری x86 پشته built-in پشتیانی میشه و رجیسترهایی هم که برای این کار هستند ESP و EBP هستند.
رجیستر ESP به بالای پشته اشاره میکنه یعنی آخرین عنصر, و با push یا pop کردن مقدار این رجیستر تغییر می کند.
از EBP هم بعنوان آدرس مبنا مورد استفاده قرار میگیره , تا از این طریق بشه متغیرهای محلی و پارامترها رو پیدا کرد.
پشته از آدرس های بالا به پایین تخصیص داده میشود به این صورت که آخرین عنصر آدرسش کوچکتر از اولین عنصر هستش.

حلقه ها خیلی زیاد توی برنامه نویسی بکار میرن و یادگیری شناسایی اونها یکی از کارهای مهم میتونه باشه.

یکی ازین حلقه ها FOR هستش که خیلی زیاد استفاده میشه و 4تا قسمت داره:

مقداردهی اولیه

مقایسه

دستورات اجرایی

افزایش یا کاهش

 

یه نمونه از دستور for

int i;

 

for(i=0; i<100; i++)

{

   printf("i equals %d\n", i);

}

توی برنامه نویسی از دستور if برای تغییر روند اجرا مبتنی بر یک شرط خاص استفاده میشه که ما میخوایم اینجا به بررسی کدهای دیس اسمبل تولید شده با دستور if بپردازیم.

در زیر کد زبان سی و همچنین کد دیس اسمبل شده ی اون رو مشاهده میکنید توجه کنید که دستور if در اینجا با پرش شرطی jnz پیاده سازی شده البته توی کدهای دیس اسمبل شده این یادتون باشه که هر پرش شرطی دلیل بر وجود دستور  if نیست.


int x = 1;

int y = 2;

 

if(x == y){

      printf("x equals y.\n");

}

else{

      printf("x is not equal to y.\n");

}

کد دیس اسمبل شده برنامه بالا:

00401006        mov     [ebp+var_8], 1

0040100D        mov     [ebp+var_4], 2

00401014        mov     eax, [ebp+var_8]

00401017        cmp     eax, [ebp+var_4]  (1)

0040101A        jnz     short loc_40102B  (2)

0040101C        push    offset aXEqualsY_ ; "x equals y.\n"

00401021        call    printf

00401026        add     esp, 4

00401029        jmp     short loc_401038  (3)

0040102B loc_40102B:

0040102B        push    offset aXIsNotEqualToY ; "x is not equal to y.\n"

00401030        call    printf

انواع مختلفی از عملیات ریاضی رو میشه توی زبان سی پیاده سازی کرد در اینجا هم ما چند نمونه از این عملیات رو مثل جمع و تفریق, - - , ++ و % رو مورد بررسی قرار میدیم.

کد برناممون هم بصورت زیر هستش:

int a = 0;

int b = 1;

a = a + 11;

a = a - b;

a--;

b++;

b = a % 3;

اکثر مواقع نیاز نیست که ما تک تک دستورات رو آنالیز کنیم (مگر در مواقع ضروری), چونکه این کار واقعا خسته کننده هستش و ممکنه یه برنامه هزارها یا حتی میلیون ها دستور دیس اسمبل شده داشته باشه.پس برای رفع این مشکل ما میایم و دستورات رو بصورت گروهی بررسی میکنیم تا یه تصویر کلی از عاملیت برنامه داشته باشیم و بعد تنها روی دستورات خاصی که مد نظرمون هستش زوم می کنیم و آنالیز رو انجام میدیم.

همانطور که میدونید بیشتر نرم افزارها با زبان های سطح بالایی مثل C یا C++ توسعه داده میشند و اگه ما با دستورات اسمبلی تولید شده توسط کامپایلرهای این زبانها برای هر سازه آشنا باشیم پس می تونیم خیلی راحتتر کدهای دیس اسمبل شده ی برنامه های نوشته شده با این زبان هارو بررسی کنیم. -- خودمم نفهمیدم چی گفتم :) (منظورم از سازه همون دستورات برنامه نویسی استفاده شده توی کد هستش,مثل حلقه, عملیات محاسباتی و...)

پس ما قراره که سازه های زبان C رو در اسمبلی مورد بررسی قرار بدیم.مثلا حلقه ها, تعریف انواع متغیرها و... البته به این نکته هم توجه داشته باشید که نسخه کامپایلر و تنظیماتش هم روی کد اسمبلی تولید شدش تاثیر میزاره و همه مشابه هم نیستند.