در علوم کامپیوتر، کد خود تغییر دهنده کدی است که دستورالعملهای خود را تغییر میدهد در حالی که دارد اجرا میشود - معمولاً برای کاهش طول مسیر دستور و بهبود عملکرد یا به سادگی برای کاهش کد مشابه تکرارشده تا نگهداری آسانتر بشود. خود-تغییر دهندگی یک جایگزین برای روش «تنظیم پرچم» و برنامه مشروط شاخهای است که در درجه اول برای کاهش تعداد دفعاتی که یک شرایط باید مورد آزمایش قرار گیرد، استفاده میشود. این اصطلاح معمولاً فقط برای کدی استفاده میشود که در آن خود تغییر دهندگی عمدی و با قصد است، نه در مواردی که کد به صورت تصادفی به علت خطایی مانند سرریز بافر(buffer overflow) تغییر میکند.
این روش غالباً برای استفاده مشروط از کد تست / اشکال زدایی کد بدون نیاز به هزینه اضافی محاسباتی برای هر چرخه ورودی / خروجی استفاده میشود.
تغییرات ممکن است انجام شود:
در حالت دیگر، تغییرات ممکن است بهطور مستقیم خودشان به دستورالعملهای کد ماشین اعمال بشوند، با پوشاندن یا جایگزینی دستورالعملهای جدید بر روی دستورالعملهای در حال حاضر (به عنوان مثال: تغییر مقایسه و branch به branch بدون شرط یا " NOP ") انجام میشود.
در مجموعه دستورالعمل IBM / 360 و Z / Architecture دستور EXECUTE (EX) بهطور منطقی بایت دوم دستورالعمل هدف خود را با ۸ بیت کم ارزش ثبات ۱، قرار میدهد. این اثر خود تغییر دهندگی را نسان میدهد، اگر چه دستورالعمل واقعی در حافظه تغییر نمیکند.
خود-تغییر دهندگی را میتوان به روشهای مختلفی بسته به زبان برنامهنویسی و پشتیبانی آن از اشاره گرها و / یا دسترسی به کامپایلر پویا یا موتورهای مترجم انجام داد:
کد خود تغییر دهندگی برای پیادهسازی در هنگام استفاده از زبان اسمبلی بسیار ساده است. دستورالعملها میتوانند بهطور پویا در حافظه ایجاد شوند (یا در موارد دیگر بر روی کدهای موجود در ذخیرهسازی برنامههای محافظت نشده)، در توالی معادل با آنهایی که کامپایلر استاندارد ممکن است به عنوان کد هدف تولید کند. با پردازندههای مدرن، ممکن است عوارض جانبی ناخواسته بر روی حافظه نهان CPU وجود داشته باشد که باید در نظر گرفته شود. این روش اغلب برای آزمایش شرایط «اولین بار» مورد استفاده قرار میگیرد، همانطور که در این نمونه زبان اسمبلی مناسب IBM / 360 توضیح داده شدهاست. این روش با استفاده از پوشش دستور برای کاهش طول مسیر دستورالعمل به N × 1) -1) که در آن N که تعداد رکوردها در فایل است. (-۱ سرباری برای انجام پوشش است)، استفاده میشود.
SUBRTN NOP OPENED FIRST TIME HERE?
* The NOP is x'4700'<Address_of_opened>
OI SUBRTN+1,X'F0' YES, CHANGE NOP TO UNCONDITIONAL BRANCH (47F0...)
OPEN INPUT AND OPEN THE INPUT FILE SINCE IT'S THE FIRST TIME THRU
OPENED GET INPUT NORMAL PROCESSING RESUMES HERE
...
کد جایگزین ممکن است شامل هر بار آزمودن «پرچم» باشد. دستور پرش بدون قید و شرط کمی سریعتر از دستور مقایسه ای است، و همچنین باعث کاهش طول مسیر کلی میشود. در سیستم عاملهای بعدی برای برنامههایی که در حافظه محافظت شده قرار دارند، این تکنیک نمیتواند مورد استفاده قرار گیرد و به جای آن از تغییر اشاره گر به زیر برنامه استفاده خواهد شد. اشاره گر در حافظه پویا قرار دارد و میتواند پس از گذراندن اولین مرحله برای دور زدن OPEN تغییر یابد (ابتدا باید یک اشاره گر به جای یک پرش مستقیم بارگذاری شود و به زیر برنامه ارتباط پیدا کند که N دستورالعمل را به طول مسیر اضافه میکند - اما یک کاهش متناظر با N برای پرش بدون قید و شرط وجود دارد که دیگر لازم نیست).
در زیر نمونه ای از زبان اسمبلی Zilog Z80 است. این کد ثبات B را از مقدار ۰ به ۵ افزایش میدهد. دستور مقایسه ای "CP" در هر حلقه تغییر میکند.
;==================================================================== ==
ORG 0H
CALL FUNC00
HALT
;==================================================================== ==
FUNC00:
LD A,6
LD HL,label01+1
LD B,(HL)
label00:
INC B
LD (HL),B
label01:
CP $0
JP NZ,label00
RET
;==================================================================== ==
کد خود تغییر دهنده گاهی اوقات برای غلبه بر محدودیتهای مجموعه دستورها ماشین مورد استفاده قرار میگیرد. به عنوان مثال، در مجموعه دستورها Intel 8080، نمیتوان یک بایت را از یک پورت ورودی که توسط یک ثبات مشخص شدهاست را وارد کرد. پورت ورودی به صورت استاتیکی در خود دستور، به عنوان بایت دوم دستور دو بایتی رمزگذاری میشود. با استفاده از کد خود تغییر دهنده، میتوان محتویات ثبات را در بایت دوم دستور ذخیره کرد، سپس دستور اصلاح شده را برای رسیدن به اثر مورد نظر، اجرا میکند.
برخی از زبانهای کامپایل شده به صراحت اجازه میدهند کد خود را تغییر دهید. به عنوان مثال، فعل ALTER در COBOL ممکن است به عنوان دستور پرشی اجرا شود که در حین اجرای برنامه اصلاح میشود.[۱] یکی از روشهای برنامهریزی دسته ای استفاده از کد خودمحور است.[۲] Clipper و SPITBOL نیز امکانات را برای تغییر صریح خود فراهم میکنند. کامپایلر Algol در سیستمهای B6700 یک رابط را برای سیستم عامل ارائه داد که در آن اجرای کد میتواند یک رشته متن یا یک فایل دیسک نامگذاری شده را به کامپایلر Algol منتقل کند و سپس قادر به فراخوانی نسخه جدیدی از یک روش بود.
با زبانهای تفسیر شده، "کد ماشین" متن منبع است و ممکن است حساس به ویرایش بر روی on-the-fly باشد: در SNOBOL، جملات مرجع که اجرا میشوند، عناصر یک آرایه متنی هستند. زبانهای دیگر مانند Perl و Python به برنامهها برای ایجاد کد جدید در زمان اجرا و اجرای آن با استفاده از یک تابع eval، اجازه میدهند، اما اجازه تغییر در کد فعلی قابل را نمیدهند. تغییر غلط (حتی اگر هر کدام از ماشینها واقعاً رونویسی شوند) با تغییر اشاره گرهای تابع به دست میآید، همانطور که در این مثال جاوااسکریپت داریم:
var f = function (x) {return x + 1};
// assign a new definition to f:
f = new Function('x', 'return x + 2');
ماکروهای Lisp همچنین اجازه میدهند که کد در زمان اجرا تولید شود بدون آن که به تجزیه رشته حاوی متن برنامه نیاز باشد.
زبان برنامهنویسی Push یک سیستم برنامهنویسی ژنتیکی است که بهطور ویژه برای ایجاد برنامههای خود تغییر دهنده طراحی شدهاست. در حالی که یک زبان سطح بالا نیست، زبان سطح پایینی هم در حد زبان اسمبلی نیست.[۳]
قبل از ظهور تعدادی از ویندوزها، سیستمهای command-line ممکن است یک سیستم منو که شامل اصلاح یک اسکریپت فرمان اجرایی است را ارائه دهند. فرض کنید یک فایل اسکریپت DOS (یا "دسته ای") Menu.bat شامل موارد زیر باشد:
برچسب. ShowMenu.exe
پس از شروع Menu.bat از خط فرمان، دستور ShowMenu یک منو بر روی صفحه با اطلاعات کمکی ممکن، استفاده از مثالها و… را ارائه میدهد. در نهایت کاربر یک انتخاب را انجام میدهد که برای اجرا نیاز به یک فرمان somename دارد: ShowMenu پس از بازنویسی فایل Menu.bat که حاوی دستورهای زیر است، وجود دارد
ShowMenu.exe CALL C: \ دستورها \ somename بوت GOTO StartAfresh
از آنجا که فرمان مترجم DOS یک فایل اسکریپت را کامپایل و سپس اجرا نمیکند و قبل از شروع اجرای آن تمام فایل را به حافظه نمیبخشد و هنوز هم بر روی محتوای یک بافر رکورد وابسته نیست، زمانی که دستور ShowMenu خارج میشود، فرمان مترجم، فرمان جدیدی را برای اجرا پیدا میکند (اسکریپت فایل somename را، در یک مکان دایرکتوری و از طریق یک پروتکل که برای ShowMenu شناخته شدهاست، فراخوانی میکند) و بعد از آن فرمان کامل شود، به آغاز فایل اسکریپت بازمیگردد و ShowMenu را برای انتخاب بعدی، دوباره فعال میکند. باید گزینه منو را ترک کنید، فایل میبایست به حالت اصلی آن بازنویسی شود. اگر چه این حالت شروع هیچ استفاده ای برای برچسب ندارد، یا مقدار معادل آن متن لازم است، زیرا مترجم فرمان DOS مکان فرمان بعدی را هنگامی که فرمان بعدی را اجرا میکند به یاد میآورد، بنابراین فایل دوباره نوشته شده باید هماهنگی برای نقطه شروع فرمان بعدی را حفظ کند تا در واقع شروع فرمان بعدی باشد.
بجز راحتی یک سیستم منو (و ویژگیهای کمکی احتمالی)، این روش به این معنی است که سیستم ShowMenu.exe زمانی که فرمان انتخاب شده فعال نیست، مزیت قابل توجهی در هنگام محدود بودن حافظه دارد.
مترجمهای جدول کنترل میتوانند در یک معنا "خود تغییر داده شده" با مقادیر داده استخراج شده از ورودیهای جدول (به جای اینکه بهطور خاص دستهبندی شده در جملههای شرطی به فرم "IF inputx = 'yyy'") در نظر گرفته شود.
بعضی از روشهای دسترسی IBM بهطور سنتی برنامههای کانال خود تغییر دهنده را استفاده میکنند، جایی که یک مقدار مانند آدرس دیسک، در یک ناحیه اشاره شده توسط برنامه کانال، که از طریق یک فرمان کانال بعدی برای دسترسی به دیسک استفاده میشود، خوانده میشود.
IBM SSEC در ماه ژانویه سال ۱۹۴۸ نشان داد که توانایی تغییر دستورهایش را دارد یا در غیر این صورت با آنها دقیقاً مانند داده رفتار کند. با این حال، این قابلیت به ندرت در عمل کاربرد دارد.[۴] در اولین روزهای رایانهها، کد خود تغییر دهنده اغلب برای کاهش استفاده از حافظه محدود یا بهبود عملکرد یا به هر دو دلیل استفاده میشد. همچنین گاهی اوقات برای پیادهسازی توابع زیر برنامه ای استفاده میشد و هنگامی برمیگشت که مجموعه دستورها تنها پرشی ساده یا رد شدن از دستورها برای تغییر دادن کنترل جریان، باشند. این کاربرد هنوز در برخی معماری معین ultra-RISC مناسب است، حداقل از لحاظ نظری؛ به عنوان مثال معماری one instruction set computer نیز از کد خود تغییر دهنده برای پیادهسازی توابع زیر برنامه، استفاده میکرد.
کد خود تغییر دهنده را میتوان برای اهداف مختلف استفاده کرد:
مثالd از Pseudocode :
repeat N times {
if STATE is 1
increase A by one
else
decrease A by one
do something with A
}
کد خود تغییر دهنده، در این مورد، به سادگی موضوع بازنویسی حلقه مانند این است:
repeat N times {
''increase'' A by one
do something with A
when STATE has to switch {
replace the opcode "increase" above with the opcode to decrease, or vice versa
}
}
توجه داشته باشید که تعویض دو حالتی OPCode به راحتی میتواند به عنوان "xor var" در آدرس با مقدار" (opcodeOf (Inc) xor opcodeOf (dec " نوشته شود.
انتخاب این راه حل باید به مقدار N و فرکانس تغییر حالت بستگی داشته باشد.
فرض کنید مجموعه ای از آمار مانند میانگین، بیشینه، موقعیت بیشینه، انحراف استاندارد و … برای برخی از مجموعه دادههای بزرگ محاسبه میشود. در یک وضعیت کلی، ممکن است یک حالت مربوط به وزن با داده وجود داشته باشد، بنابراین هر x i با aw i همراه است و به جای آزمودن حضور وزن در هر مقدار شاخص، ممکن است دو نسخه از محاسبه، یکی برای استفاده با وزن و یکی برای استفاده بدون وزن، با یک آزموندن در آغاز کار، موجود باشد. حال یک گزینه دیگر را در نظر بگیرید که هر مقدار ممکن است با یک عدد بولین همراه باشد تا نشان دهد که آیا این مقدار باید حذف شود یا خیر. این روش میتواند با تولید چهار دسته از کد، برای هر permutation و نتایج نفوذ کد، مورد استفاده قرار گیرد. در عوض، وزن و آرایههای جست و جوی میتوانند به یک آرایه موقت (با وزن صفر برای مقادیری که باید پر شوند)، با هزینه پردازش، ترکیب شوند و هنوز هم باد کردن وجود دارد. با این حال، با تغییر کد، به الگوی محاسبه آمار میتواند کد مورد نظر که برای پرش از مقادیر ناخواسته و برای اعمال وزن است، اضافه شود. تستهای تکراری از گزینهها وجود نخواهد داشت و به آرایه دادهها یک بار دسترسی انجام میشود، همچنین وزن و آرایههای جستجو میتواند به همین شکل باشد، اگر شامل کد باشند باشد.
کد خود تغییر دهنده برای مخفی کردن کپی حفاظت دستورها در برنامههای مبتنی بر دیسک ۱۹۸۰ برای سیستم عاملهایی مانند IBM PC و Apple II استفاده شدهاست. به عنوان مثال، در IBM PC (یا سازگار)، دستور دستیابی درایو فلاپی دیسک که int 0x13 میباشد در تصویر برنامه اجرایی نمایش داده نخواهد شد، اما بعد از اجرای برنامه، در تصویر حافظه اجرایی نوشته میشود.
کد خود تغییر دهنده گاهی اوقات توسط برنامههایی استفاده میشود که نمیخواهند حضورشان را نشان دهند، مانند ویروسهای کامپیوتری و برخی ازshellcodeها. ویروسها و کد شلها که از کد خود تغییر دهنده استفاده میکنند، عمدتاً این کار را با کد پلی مورفیک انجام میدهند. تغییر یک قطعه کد در حال اجرا نیز در حملات خاص، مانند سرریز بافر استفاده میشود.
سیستمهای یادگیری ماشین سنتی دارای الگوریتم یادگیری ثابت و پیش برنامهریزی شده برای تنظیم پارامترهایشان می باشند. با این حال، از دهه ۱۹۸۰، Jürgen Schmidhuber چند سیستم خود تغییر دهنده را با توانایی تغییر الگوریتم یادگیری خود، منتشر کردهاست. این سیستمها از خطر خودکفائی فاجعه آمیز جلوگیری میکنند، از این طریق اطمینان حاصل میکنند که خود تغییرات تنها در صورتی قابل قبول خواهند بود که با توجه به آمادگی کاربر، خطا یا عملکرد پاداش مفید باشد.[۶]
به دلیل پیامدهای امنیتی کد خودمحور، تمام سیستم عاملهای اصلی مراقب حذف چنین آسیبپذیری به محض شناسایی آنها هستند. نگرانی معمولاً این نیست که برنامهها عمداً خودشان را تغییر دهند، اما میتوانند از طریق سوء استفاده از آنها سوءاستفاده کنند.
به عنوان نتیجهٔ مشکلاتی که میتواند توسط این سوء استفادهها ایجاد شود، یک ویژگی OS به نام W ^ X (برای "نوشتن xor execute") توسعه داده شدهاست که به برنامه اجازه نمیدهد که هیچ صفحهٔ حافظه را هم قابل خواندن و هم قابل اجرا کند. بعضی از سیستمها از یک صفحه که قابل نوشتن است جلوگیری میکنند تا به حالت قابل اجرا درآید، حتی اگر اجازه نوشتن حذف شود. سیستمهای دیگر " درهای عقب " را ارائه میدهند که اجازه میدهد چندین صفحه بندی از یک صفحهٔ حافظه دارای مجوزهای متفاوت باشد. یک راه نسبتاً قابل حمل برای دور زدن W ^ X این است که یک پرونده با تمام مجوزها ایجاد کنید، سپس فایل را به حافظه دو بار نگاشت کنید. در لینوکس، میتوان از یک پرچم حافظه مشترک SysV ناموجود استفاده کرد تا به حافظه مشترک قابل اجرا بدون نیاز به ایجاد یک فایل رسید. [نیازمند منبع] صرفنظر از موارد فوق، در سطح فراگیر، برنامهها همچنان میتوانند رفتار خود را با تغییر دادههای ذخیره شده در جاهای دیگر تغییر دهند یا از طریق استفاده از پلی مورفیسم.
در معماریهای بدون دادههای مرتبط و حافظه پنهان دستورالعمل (برخی از هستههای ARM و MIPS)، هماهنگ سازی حافظه پنهان باید به وضوح توسط کد تغییر دهنده انجام شود (حافظه پنهان دادهٔ تخلیه و حافظهٔ پنهان دستورالعمل بیاعتبار برای محدوده حافظه تغییر داده شده).
در بعضی موارد، بخشهای کوتاهی از کد خود تغییر دهنده به آرامی روی پردازندههای مدرن اجرا میشود. این به این دلیل است که یک پردازنده مدرن معمولاً سعی میکند بلوکهای کد را در حافظه پنهان خود نگه دارد. هر بار که برنامه بخشی از خود را بازنویسی میکند، قسمت بازنویسی شده باید دوباره در حافظه پنهان بارگذاری شود، که باعث یک تأخیر جزئی میشود، اگر کدهای تغییر داده همان خط حافظهٔ پنهان را با کد تغییری به اشتراک گذارند، همانند مواردی که حافظه تغییر داده شده را آدرس درون چند بایت در یکی از کدهای تغییر داده شدهاست.
مسئله عدم اعتبار حافظهٔ پنهان در پردازندههای مدرن معمولاً بدین معنی است که کد خود تغییر دهنده هنوز هم سریع تر خواهد بود تنها زمانی که تغییر به ندرت رخ میدهد، مانند تعویض حالت درون حلقه داخلی. [نیازمند منبع] بیشتر پردازندههای مدرن قبل از اجرای آن، کد ماشین را بارگذاری میکنند، بدین معنی که اگر دستورالعملی که خیلی نزدیک اشارهگر دستورالعمل است، تغییر داده شده باشد، پردازنده متوجه نمیشود، بلکه کد را قبل از اینکه آن را تغییر دهد اجرا میکند. صف ورودی پیشفرض (PIQ) را ببینید. پردازندههای PC باید به کد خود تغییر دهنده را به درستی برای دلایل سازگاری عقبی انجام دهند، اما در انجام این کار بسیار دور از حالت بهینهاند.[نیازمند منبع]
سنتز هسته ارائه شده در دکتری پایاننامه[۷] هنری Massalin، یک کرنل Unix کوچک است که رویکرد ساختار یافته یا حتی شی گرا را به کد خود تغییر میدهد، جایی که کد برای تک تک quajectsها مانند filehandles ایجاد میشود؛ تولید کد برای کارهای خاص اجازه میدهد که هسته Synthesis به (به عنوان مترجم JIT ممکن است) تعدادی از بهینهسازیها را اعمال کند مانند حذف ثابت ثابت یا مشترک زیر بیان.
هسته Synthesis بسیار سریع بود، اما بهطور کامل در اسمبلی نوشته شده بود. عدم وجود قابلیت حمل و نقل مانع از ایدههای بهینهسازی Massalin از هر هسته تولیدی شدهاست. با این حال، ساختار تکنیکها نشان میدهد که آنها میتوانند با یک زبان سطح بالاتری پیادهسازی شوند، اگرچه پیچیدهتر از زبانهای سطح متوسط موجود هستند. چنین زبان و کامپایلر میتواند اجازهٔ توسعه سریعتر سیستم عاملها و برنامههای کاربردی را دهد.
پل هابرلی و بروس کارش با «حاشیه سازی» کد خود تغییر دهنده و بهینهسازی بهطور کلی به نفع کاهش هزینههای توسعه مخالفت کردند.[۸]
کد خودتغییر داده شده برای خواندن و نگهداری سختتر است، زیرا دستورالعملها در فهرست برنامهٔ منبع، لزوماً دستورالعملهایی نیستند که اجرا میشوند. خود تغییری که شامل تعویض اشاره گرهای عملکرد است ممکن است به صورت معکوس نباشد، اگر روشن باشد که نام توابع نامیده میشود، متغیرهایی برای توابع شناسایی شدهاست.
کد خود تغییر دهنده میتواند به عنوان کد بازنویسی شود که پرچم و شاخهها را به دنبالههای جایگزین بر اساس نتیجه آزمایش تست میکند، اما کد خود تعییر دهنده معمولاً سریعتر اجرا میشود.
در پردازندههای مدرن با یک خط لوله دستورالعمل، کد ای که خود را به صورت متناوب تغییر میدهد، ممکن است به آرامی اجرا شود، در صورتی که دستورالعملهایی را که پردازشگر از حافظه به خط لوله خواندهاست، تغییر دهد. در بعضی از این پردازندهها، تنها راه برای اطمینان از این که دستورالعملهای تغییر داده شده به درستی اجرا میشوند، این است که خط لوله تخلیه شود و دوباره دستورالعملها را بخواند.
در برخی از محیطها، کد خود تغییر دهنده نمیتواند مورد استفاده قرار گیرد، مانند موارد زیر:
The SSEC was the first operating computer capable of treating its own stored instructions exactly like data, modifying them, and acting on the result.
{{cite journal}}
: Cite journal requires |journal=
(help)