جستجو

در این پست میخوام Buffer overflow  رو به صورت کامل شرح بدم قبلشم بگم این مطالب به ساده ترین شکل ممکن بیان شده اگه کسی ذهنش نمیکشه ۲ - ۳ بار بخونه اگه نفهمید دنبال هک نباشه چون یاد نمیگیره >>>
بخش اول: اصول مقدماتی

پشته
اکثرا باید بدانید که پشته چیست.ولی اندکی یاد آوری در مورد پشته و ذکر انواع آن خالی از لطف نیست.
یک تعریف انتزاعی از پشته این است که پشته ساختمان داده ای است که اطلاعات (از هر نوعی) به ترتیب معکوس ورود از ان خارج میشوند :

یعنی آخرین ورودی , اولین خروجی است.به همین علت به این سیستم ورودی و خروجی , سیستم LIFO اطلاق میشودکه خلاصه شده عبارت Last In First Out میباشد.
پشته یکی از پر کاربرد ترین ساختار ها در دنیای کامپیوتر و برنامه سازی از پایین ترین سطح تا بالا ترین سطح میباشد و به همین علت در همه ماشینها (سی پی یو ها) دستوالعملهای خاصی برای کار کردن با پشته سیستم تدارک دیده اند. این دستورالعملها عبارتند از دستور PUSH و دستور POP که اگر کسی به زبان اسمبلی برنامه نوشته باشد صد درصد با آنها اشنایی دارد و میداند که کارشان چیست.
دستورالعملهای مربوط به پشته و مفهوم آنها :
1- PUSH : یعنی وارد کردن یک عنصر به پشته
2-POP : یعنی خارج کردن یک عنصر از پشته

اگر کمی فکر کنیم به این نتیجه میرسیم که پشته نیز در حافظه جای میگیرد و دارای حد بالا و حد پایین است.این حد بالا و حد پایین پشته توسط سیستم عامل نگهداری میشود و دارای مقدار ثابتی است (به طور معمول) ولی تعداد آیتمهای داخل پشته ثابت نیست بنابر این از کجا میتوانیم بفهمیم که پشته تا کجای ان پر است.برای اینکار در سیستم یک ثبات در نظر گرفته میشود که نشان دهنده این است که پشته تا کجای آن پر است و هر وقت که عنصری وارد پشته شود و یا از آن خارج شود در این ثبات , تغییراتی اعمال میگردد. در بسیاری از سیستمها این ثبات با نام SP : stack pointer معروف است:

انواع پشته از دید آدرس دهی در حافظه

اگر اندکی تامل کنیم متوجه میشویم که بر اساس ساختار و نحوه آدرس دهی حافظه کامپیوتر میتوانیم دو نوع پشته داشته باشیم که عبارتند از :
1-پشته کاهشی
2-پشته افزایشی

1-پشته افزایشی :
در این مدل از پشته , پشته از آدرس پایین حافظه شروع به بزرگ شدن میکند.در این مدل با اضافه شدن یک آیتم به پشته در جهت ایندکس خانه های حافظه از پایین به بالا حرکت میکنیم.
در ساده ترین تفسیر با اضافه شدن هر عنصر به پشته , یک واحد به اشاره گر بالای پشته اضافه میشود و با برداشته شدن هر عنصر , یک واحد از این اشاره گر کم میشود:

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

نمیتوان گفت که کدامیک از این دو بهتر است.هر نوع دارای کاربردهای گوناگونی هستند.از نظر بنده پشته نوع دوم یعنی پشته کاهشی دارای کاربرد بیشتری در پیاده سازیها میباشد.برای مثال در پیاده سازی پردازنده های زیر از متد کاهشی برای پیاده سازی دستورالعملهای پشته استفاده شده است :
1-Intel
2-Motorola
3-SPARC
4-MIPS

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

1-پشته اجرایی Executable stack
2-پشته غیر اجرایی Non executable stack

پشته اجرایی نوعی پشته است که سیستم میتواند در صورت وجود کدهای اجرایی در پشته , آنها را مشابه کدهای موجود در سگمنت کد ,اجرا کند

در پشته غیر اجرایی , حتی اگر کدهای اجرایی در پشته باشند , سیستم مجاز نیست آنها را اجرا کند و در صورت تلاش برای اجرای کدها در سگمنت پشته خطایی از طرف سیستم عامل نمایش داده میشود.
محبوبیت پشته اجرایی , بیشتر از پشته غیر اجرایی است به طوری که در پیاده سازی بسیاری از سیستم عاملهای معروف از پشته اجرایی استفاده شده است. برای مثال :
1-M$ Windows series
2-Unix (*nix series)
3-Linux (All distributions such as RedHat,Mandrake)
4-Some other well known Operating Systems

از پشته غیر اجرایی به ندرت برای پیاده سازی سیستم های عامل استفاده شده و معروف ترین گونه ای که در بازار موجود است , سیستم عامل IRIX است.
شاید دلیل محبوبیت کم پشته غیر اجرایی , مشکلات پیاده سازی و استفاده از آن در سطح سیستم و برنامه سازی است.

انواع پشته از لحاظ استفاده از SP
پشته از دیدگاه نحوه استفاده از اشاره گر بالای پشته نیز انواع متفاوتی دارد.در پیاده سازی میتوانیم به دو روش از اشاره گر بالای پشته استفاده کنیم که عبارتند از :
1-SP نشان دهنده بالاترین عنصر پشته است
2-SP نشان دهنده اولین جای خالی در پشته است
در نوع اول وقتی که پشته خالی است SP مساوی مقدار 1- یا یک مقدار غیر معتبر پیش فرض است که به این معنی است که هیچ عنصری در پشته وجود ندارد.
در نوع دوم وقتی که پشته خالی است , SP مساوی 0 یا مقداری است که نشان دهنده اولین خانه خالی در پشته است:

ذکر یک نکته در اینجا حائز اهمیت است که اگر تا اینجا در متن دقت کرده باشید همه جا از واژه واحد و خانه برای پشته استفاده شده است و هیچ جایی از واژه بایت برای ارجاع به سلولهای پشته استفاده نشده.دلیل این مطلب این است که در پیاده سازیهای سی-پی-یو ها و سیستم عاملهای مختلف برای خانه های پشته اندازه های مختلفی در نظر گرفته شده.به طور استاندارد اندازه هر خانه از پشته , یک کلمه در نظر گرفته میشود که اندازه هر کلمه از یک ماشین به ماشین دیگر متفاوت است.در بعضی از ماشینها , اندازه کلمه 3 بایت , در بعضی 4 بایت و در گستره وسیعی اندازه هر کلمه 2 بایت است و این به صورت یک استاندارد در آمده است.از آنجا که در حال حاضر بیش از 95% سیستمهای کامپیوتری در جهان از استاندارد IBM تبعیت میکنند و اندازه ه کلمه در این استاندارد 2 بایت است , ما هم به طور پیش فرض اندازه هر کلمه را 2 بایت در نظر خواهیم گرفت.ذکر این نکته هم حائز اهمیت است که چون بیشتر ما از سیستمهای x86 استفاده میکنیم , به طور پیش فرض , سیستم هدف خود را یک سیستم x86 در نظر خواهیم گرفت و تنوع خود را بر روی سیستمهای عامل متمرکز خواهیم نمود.
ثباتهایی که برای پشته تعبیه شده اند.
همانگونه که اشاره شد , در هر ماشین برای کار با پشته , دارای دستورالعملها و ثباتهایی هستیم که به دو دستور العمل کار با پشته اشاره کردیم و توضیحی در مورد آنها ارائه کردیم.حال میپردازیم به اینکه در سری x86 چه ثباتهایی برای پشته تعبیه شده است.در سری x86 برای کار با پشته , دارای ثباتهای زیر هستیم :
1-ثبات SP : اشاره گر بالای پشته
2-ثبات SS : ثبات سگمنت پشته
3-ثبات FP : ثبات اشاره گر فریم
4-ثبات BP : یک ثبات همه منظوره که بیشتربرای کاربا پشته به کار میرود
با ثبات اول که همان SP هست از قبل اشنایی دارید و میدانید که کاربرد آن چیست.
ثبات دوم , یعنی SS , ثبات سگمنت پشته میباشد که نشان دهنده ابتدای سگمنت پشته(یا بهتر بگوییم انتها)
است.اگر واقع بین باشیم این ثبات نشان دهنده حد پایین پشته است.
ثبات سوم یا همان FP بیشتر توسط سیستم عامل برای مدیریت فرایند ها به کار میرود و در سطح برنامه های کاربردی زیاد کاربرد ندارد.پشته سیستم عامل به طور منطقی , به ازای هر فرایند , به قسمتهایی تقسیم میشود که به هر یک از آنها یک فریم اطلاق میشود و در داخل هر فریم اطلاعات مربوط به فرایند متناظر با آن قرار دارد.
ثبات چهارم ,BP, بیشتر نقش آچار فرانسه را دارد و برای فعل و انفعالات برنامه کاربردی با سیستم به کار میرود.
مقادیر ثباتهای پشته نسبت به SS در نظر گرفته میشود. یعنی مقداری که در ثباتهای مربوط به پشته قرار میگیرد یک آدرس مطلق حافظه نیست و نسبت به فاصله ای که از ثبات سگمنت دارند مقادیر را نگه میدارند و اگر بخواهیم آدرس واقعی خانه ای از پشته که SP به آن اشاره میکند را بدست آوریم بایستی اندکی محاسبات انجام دهیم.
مثال :
SS=0FA800h
SP=100h
0FA800h + 100H =0FA900h=آدرس فیزیکی
به این معنی که خانه 100 هکز یا 256ام پشته در آدرس 0FA900h حافظه است.
نکته :
هر برنامه ای , به هر زبانی که نوشته میشود دارای پشته است.اگر به زبان اسمبلی برنامه نوشته باشد میدانید که در ابتدای برنامه یا باید یک سگمنت پشته توسط پیش پردازنده SEGMENT تعریف کنیم و آدرس آن را توسط پیش پردازنده ASSUME در ثبات سگمنت پشته قرار دهیم و یا اینکه توسط پیش پردازنده .STACK اندازه پشته را اعلان کنیم تا اسمبلر بقیه کارها را انجام دهد.
تعریف پشته در زبان اسمبلی:

مثال اول
   

مثال دوم

ASSUME CS:CODE,DS:DATA,SS:STACK

CODE SEGMENT
MAIN PROC NEAR
....
MAIN ENDP
CODE ENDS
.MODEL SMALL

.STACK 256

.DATA
A DW 24
B DB ?

.CODE
MAIN PROC NEAR
.....
MAIN ENDP
END
    .MODEL SMALL
STACK SEGMENT PARA STACK 'stack'
DB 256 DUP(?)
STACK ENDS

DATA SEGMENT
A DW 24
B DB ?
DATA ENDS

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

بافر
بافر به طور عام به حافظه ای اطلاق میشود که برای نگهداری اطلاعات به کار میرود.در واقع از یک دید خاص میتوان گفت که بافر همان متغیرهای برنامه است .برای مثال یک متغیر از نوع intدر زبان سی یک بافر 2 بایتی است که برای نگهداری اطلاعات عددی ازآن استفاده میشود.میتوانیم آرایه ای از int ها را هم به عنوان یک بافر در نظر بگیریم.متغیرهای رشته ای هم که درواقع آرایه ای از کاراکتر ها هستند,نیز نوعی بافر هستند.کار اصلی ما با نوع آخر یعنی بافرهای رشته ای است.
همانگونه که باید بدانید در زبان سی , برای نشان دادن انتهای رشته از کاراکتر NULL یا همان پوچ که کد اسکی آن صفر است استفاده میشود.برای مثال اگر شکل زیر را به عنوان قسمتی از حافظه در نظر بگیریدکه متغیر رشته ای str در آن قرار دارد,اگر فرض کنیم که متغیر حاوی مقدار "String" باشد , در آن صورت خواهیم داشت :

این کاراکتر که در انتهای رشته قرار میگیرد نشان دهنده حد انتهای رشته هست.اگر فنی تر نگاه کنیم , در زبان سی به طور کلی آرایه ها توسط آدرس ابتدای آنها مشخص میشوند.یعنی اگر در این مثالی که آورده ایم , STRاشاره گری است به آدرس ابتدای رشته در حافظه که در مثال ما مساوی است با 23.پس داریم :

ساختار آرایه رشته در حافظه
STR="String"    

رشته ای که در حافظه است
STR=23    

قرار میگیردSTRمقداری که در متغیر
*STR='S'    

محتوای بایتی که متغیر به آن اشاره میکند.در واقع اولین خانه آرایه
*(STR+1)='t'    

دومین خانه آرایه
*(STR+2)='r'    

سومین خانه آرایه
*(STR+3)='i'    

چهارمین خانه آرایه
*(STR+4)='n'
   

پنجمین خانه آرایه
*(STR+5)='g'
   

ششمین خانه آرایه
*(STR+6)=Null
   

آخرین خانه آرایه

همانگونه که میدانید در زبان سی میتوانیم توسط عملگر * به محتوای یک آدرس از حافظه به طور مستقیم دسترسی داشته باشیم و در این مثال نیز برای نشان دادن محتوای حافظه از آن استفاده شده است.اگر یک واحد به متغیری که حاوی آدرس است اضافه کنیم , در حافظه به اندازه یک خانه به طول نوع متغیر حاوی ادرس به جلو حرکت میکنیم.به عنوان مثال اگر متغیر ptrبه عنوان یک اشاره گر به integer تعریف شده باشد و به خانه ای از حافظه اشاره کند , حافظه (ptr+1) به اندازه 2 بایت جلوتر از مقدار اول آن خواهد بود , چرا که نوع داده ای integer 2 بایت طول دارد :
...
int *ptr; //An integer type pointer declaration
int arr[2]; //An integer array of two cells
arr[0]=10; //Initializing array's first cell
arr[1]=20; //Initializing arrays secong cell
ptr=arr; // pointing ptr to start point of array
printf("*(ptr)=%d ",*(ptr));
printf("*(ptr+1)=%d ",*(ptr+1));
getch();
...

تمامی آنچه که گفته شد در این مثال آورده شده.
روشی که گفته شد , یکی از روشهایی بود که برای نمایش انتهای رشته ها به کار میرود.دربرخی از زبانهای برنامه سازی از روشی دیگر برای اینکار استفاده میشود و آن روش هم به این ترتیب است که اولین خانه آرایه برای نگهداری طول رشته ای که در آرایه نگهداری میشود , رزرو میشود.به این شکل :

این روش در زبانهایی چون پاسکال و ویژوال بیسیک به کار رفته است.
هر روش دارای مزایا و معایبی است که میتوان برای نمونه به موارد زیر اشاره کرد :
در روش اول این محدودیت رو داریم که نمیتوانیم از کاراکتر NULLدر داخل رشته استفاده کنیم ولی در روش دوم چنین محدودیتی نداریم.
در روش دوم بسته به اندازه حافظه ای که برای نگهداری طول رشته در نظر گرفته میشود محدودیت داریم ولی در روش اول چنین محدودیتی نداریم.برای مثال اگر برای نگهداری طول رشته , یک بایت در نظر گرفته شود , طول رشته ها محدود به 255 کاراکتر میشود.
متغیر ها در کجای حافظه قرار میگیرند (از لحاظ منطقی)
در هر زبان برنامه سازی بسته به مورد کاربرد , میتوانیم متغیر های نوع دینامیک و استاتیک داشته باشیم.
متغیرهای برنامه :
1-استاتیک
2-دینامیک

1- متغیرهای نوع استاتیک
این نوع متغیرها هنگام شروع فرایند یا همان برنامه در سگمنت Data یا داده که قبلا توضیح داده شده , آدرس دهی میشوند. متغیرهای نوع استاتیک , وابسته به بلوک نیستند و در حین اجرای برنامه همواره مقادیر خود را حفظ میکنند و اگر از تابع و یا بلوکی که در ان تعریف شده اند خارج شویم و دوباره برگردیم همان مقادیر دفعه قبل خود را حفظ میکنند.
2-متغیرهای نوع دینامیک
این گونه از متغیر ها فقط در محدوده بلوکی که تعریف میشوند مفهوم دارند و فقط در بلوکی که تعریف شده اند مقادیر خود را حفظ میکنند و اگر از بلوک خارج شده و دوباره بر گردیم مقادیر قبلی خود را نخواهند داشت.این نوع از متغیر ها , هنگام فراخوانی فرایند یا تابع , در پشته برنامه آدرس دهی میشوند و مقادیرشان را هم در همانجا یعنی پشته نگهداری میکنند و وقتی که از بلوک خارج شویم از بین میروند.


متغیرهای دینامیک و استاتیک

Static
   

Dynamic
void StaticFunc()
{
static int sv=0;//Static variable
sv++;
printf("sv=%d",sv);
}
//*************************
void main()
{
StaticFunc();
StaticFunc();
}     void DynFunc()
{
int dv=0; //Dynamic variable
dv++;
printf("dv=%d",dv);
}
//*************************
void main()
{
DynFunc();
DynFunc();
}

خروجی
   

خروجی
sv=1
sv=2     dv=1
dv=1

در مثالی که برای شما آورده شدفرض شده که تابع را دوبار فراخوانی میکنیم و اگر دقت کنید متغیر استاتیک مقدار قبلی خود را حفظ میکند.

هیپ(Heap) چیست ؟
غیر از این دو مورد که ذکر شد یک جای دیگر هم برای تخصیص حافظه وجود دارد و آن هم هیپ (Heap) سیستم است. وقتی که توسط دستوراتی مثل malloc یا alloc یا ... حافظه ای را از سیستم میگیریم , این حافظه در هیپ سیستم اختصاص داده میشود.در واقع از هیپ برای تخصیص حافظه به صورت دینامیک استفاده میشود.
این نکته هم باید ذکر شود که در هر برنامه ای لزوما هیپ نداریم و وجود آن بستگی دارد به زبانی که برای برنامه نویسی استفاده شده است.
به طور استاندارد دو نوع هیپ برای برنامه در نظر گرفته میشود که عبارتند از :
انواع هیپ
1-هیپ نزدیک Near heap
2-هیپ دور Far heap

اندازه هیپ نزدیک محدود به 32 کیلوبایت است و برای رفع محدودیت از هیپ دور استفاده میشود و تفاوت آنها در نحوه آدرس دهی است.
طرف حساب ما متغیرهای نوع دینامیک هستند که در پشته سیستم ادرس دهی میشوند .


کاربردهای پشته
تا حالا باید فهمیده باشید یکی از کاربردهای پشته تخصیص حافظه برای متغیرهای نوع دینامیک هست.
پشته یک کاربرد دیگر هم دارد که عبارت است از پاس کردن پارامتر ها به توابع.
هیچ تا حالا به این فکر کرده اید که وقتی که یک روال را در یک برنامه فراخوانی میکنید , مقادیر پارامتر ها یا همان آرگومانها چگونه به روال تحویل داده میشوند. بله.آرگومانهای روال هم توسط پشته به روال تحویل داده میشوند. وقتی که روال فراخوانی میشود , ابتدا پارامتر ها در پشته قرار داده میشود و بعد از یک سری کارهای مقدماتی , کنترل اجرا به روال منتقل میشود.در ابتدای روال, آرگومانها از پشته برداشته میشوند و سپس بقیه کد ها اجرا میشوند.
غیر از این دومورد کاربرد , پشته یک کاربرد مهم دیگر نیز دارد که یکی از اهداف اصلی ماست.
کاربرد سوم پشته برای ذخیره کردن آدرس بازگشت یا return address هنگام فراخوانی یک روال است. حال این آدرس بازگشت چه هست؟

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

پس درحالت کلی پشته سه کاربرد مهم برای سیستم دارد که عبارتنداز :
1-پاس کردن پارامتر ها
2-نگهداری آدرس بازگشت
3-آدرس دهی متغیرهای دینامیک

یک مفهوم که زیاد اسم آن آمد ولی در مورد ان صحبت نشد , فرایند است.فرایند چیست؟

فرایند؟
فرایند یا process در یک تعریف کلی یک برنامه است که اجرا میشود و در تعریف تخصصی تر , فرایند قطعه برنامه ای است که یک وظیفه(Task) بر عهده دارد وتوسط سیستم عامل مدیریت میشود.
هر فرایند از لحاظ منطقی به سه قسمت تقسیم میشود که به شرح زیر هست :

1- Text یا متن فرایند : این قسمت حاوی کدهای اجرایی فرایند است . این بخش توسط سیستم عامل قفل میشود و هرگونه تلاش برای دسترسی به این قسمت (چه خواندن و چه نوشتن) منجر به بروز خطای "تخطی از سگمنت بندی" یا Segmentation violation"" از طرف سیستم عامل میشود.فقط سیستم عامل اجازه دسترسی به این قسمت را دارد(به قول معروف, فقط در مود هسته(Kernel mode) مجاز به دسترسی به این بخش هستیم).

2-Data یا داده های فرایند : این همان سگمنت داده ها است که برای متغیر های استاتیک و متغیرهای با مقدار اولیه (Initialized) از آن استفاده میشود.

3-Stack یا پشته : پشته برنامه که برای سه منظور اصلی که قبلا به آن اشاره شد از آن استفاده میشود.البته ممکن است به غیر از اینها کاربردهای دیگری برای پشته داشته باشیم , ولی این سه مورد ذکر شده , موارد اصلی هستند.

نحوه مدیریت فرایند ها
لابد این اصل رامیدانید که هر برنامه ای که باید اجراشود به سه منبع اصلی 1-حافظه 2-پردازنده 3-زمان نیازمند است و کار تخصیص این منابع و مدیریت آنها توسط سیستم عامل انجام میگیرد.در سیستم عاملهای اولیه وقتی که قرار بود برنامه ای اجرا شود , خود برنامه ,به طور فیزیکی به حافظه دسترسی داشت و در فضای آدرسی که وابسته بود به اندازه حافظه کامپیوتر , میتوانست عملیات انجام دهد.ولی در سیستم ها عامل مدرن مثل ویندوز و یونیکس و لینوکس دیگر چنین کاری انجام نمیگیرد و هیچ برنامه ای به طور مستقیم به حافظه دسترسی ندارد و تمام تراکنشها با حافظه توسط سیستم عامل انجام میگیرد . دز این نوع از سیستم های عامل از تکنیک حافظه مجازی استفاده میشود, به این صورت که مقداری حافظه به صورت فیزیکی روی کامپیوتر نصب شده و بقیه حافظه هم به صورت یک فایل مبادله ای یا اصطلاحاً SWAP روی هارد دیسک در نظر گرفته میشود.در نتیجه هر برنامه ای که اجرا میشود دارای 4 گیگابایت فضای آدرس دهی خواهد بود.با تکیه بر این تکنیک دیگر هیچ وقت , هیچ برنامه ای با کمبود حافظه مواجه نمیشود و چونکه همه تراکنشهای حافظه ای به واسطه سیستم عامل انجام میگیرد , برنامه نمیتواند بفهمد که آیا آدرسی که الان میخواهد مقدار آن را بخواند و یا مقداری در آن بنویسد , در حافظه اصلی است یا حافظه معاوضه ای.در واقع این نوعی تجرید از حافظه توسط سیستم عامل است.سیستم عامل همه درخواست های حافظه را توسط نقشه ای که دارد , نگاشت میکند و در زمان لازم مقداری از حافظه را به دیسک انتقال میدهد و از دیسک به حافظه میبرد.
البته همه این 4گیگابایت برای همه برنامه ها قابل دسترسی نیست. برخی جاها از این فضای
میان فرایند ها به اشتراک گذاشته میشود و برخی جاها فقط برای یک فرایند خاص در نظر گرفته میشود.برای این چهار گیگا بایت که از آدرس 0x00000000 تا آدرس 0xFFFFFFFF ایندکس خورده , یک سازماندهی معین وجود دارد که به شرح زیر است :
سازماندهی فضای آدرس دهی توسط ویندوز
محدوده آدرس
    مورد استفاده
0x00000000 تا 0x0000FFFF     NULL pointer assignments
0x00100000 تا 0x7FFEFFFF
    Processes user space
قسمتی که پروسه ها وِDll ها لود میشوند.هر کدی که اینجا لود شود میتواند اجرا شود.دسترسی به جایی که در آن کد لود نشده باعث بروز خطای Access violation میشود
0x7FFF0000 تا 0x7FFFFFFF
    Bad pointer assignments
در صورت هر گونه تلاش برای دسترسی به این آدرس با خطای Access violation مواجه خواهید شد.
0x80000000 تا 0xFFFFFFFF
   

رزرو شده برای سیستم عامل.در این قسمت راه اندازهای ابزار و کدهای سطح هسته قرار میگیرد.در صورت تلاش برای دسترسی به این قسمت از طرف برنامه کاربری ,غیر هسته (ring3) , خطای Access violation بروز خواهد کرد.

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

بافر اورفلو چیست؟ Buffer Overflow
تا حالا همه فهمیده اید بافر یک متغیر محلی اتوماتیک است که در پشته برنامه آدرس دهی شده و برای نگهداری اطلاعات از آن استفاده میشود.
اگر بخواهیم در یک بافر مثلا 10 بایتی به اندازه 100 بایت اطلاعات را قرار دهیم معلوم است که از حدود آن بافر خارج شده ایم و در این حالت گوییم که بافر اور فلو شده است.

انواع اوورفلو
حال ممکن است این بافر که ما آن را اور فلو کرده ایم در پشته باشد (متغیر های اتوماتیک محلی) و یا اینکه در سگمنت داده برنامه باشد (متعیرهای استاتیک و متغیرهای سراسری , مقدار دهی اولیه شده) و یا اینکه در هیپ برنامه باشد.در نتیجه میتوانیم تقسیم بندی زیر را در مورد انواع اور فلو داشته باشیم :
انواع اوورفلو
1-اوورفلو در سگمنت داده
2-اوورفلو در پشته برنامه
3-اوورفلو در هیپ برنامه

اگر به یاد داشته باشید در بخش قبل گفته شد که کار ما با متغیر های محلی اتوماتیک است که در پشته آدرس دهی میشوند و در نتیجه , جریانی که ما بررسی خواهیم کرد , اور فلو در پشته برنامه است.
در قسمت قبل , ساختار یک فرایند را از لحاط منطقی توضیح دادیم وشکلی از نحوه سازماندهی فرایند برای شما ارئه کردیم.کاربردهای پشته هم که ذکر شد.حال میخواهیم به این نکته بپردازیم که وقتی که یک رول فراخوانی میشود , در پشته چه اتفاقی می افتد.

یک اوورفلو ساده
در این قسمت میخواهیم به شما نشان دهیم که کی اوور فلو اتفاق می افتد.برای راحتی در تفهیم مساله فرض کنید که روال count را که اعلان آن به شکل زیر است فراخوانی کرده ایم :
void Count(char ch,char *strng)
{
char temp[200];
int i,c=0;
strcpy(temp,strng);
for(i=0;i
if(temp[i]==ch)c++;
printf(" Number of %c 's is : %d",ch,c);
return;
}

کار این روال این است که یک کاراکتر و یک رشته را به عنوان پارامترهای اول و دوم میگیرد و تعداد کاراکتر ها را در رشته پیدا کرده و چاپ میکند.
پس داریم :
1-پارامتر اول از نوع char
2-پارامتر دوم از نوع رشته (اشاره گر)

حتما به اینجا سری بزنید           http://best-packing.blogfa.com


Posted by علیرضا at 18 | Link To This Post ID 1020 | Topic : آموزش ضد هک