کد خودتغییردهنده

در علوم کامپیوتر، **کد خود تغییر دهنده** کدی است که دستورالعمل‌های خود را تغییر می‌دهد در حالی که دارد اجرا می‌شود - معمولاً برای کاهش طول مسیر دستور و بهبود عملکرد یا به سادگی برای کاهش کد مشابه تکرارشده تا نگهداری آسان‌تر بشود. خود-تغییر دهندگی یک جایگزین برای روش «تنظیم پرچم» و برنامه مشروط شاخه‌ای است که در درجه اول برای کاهش تعداد دفعاتی که یک شرایط باید مورد آزمایش قرار گیرد، استفاده می‌شود. این اصطلاح معمولاً فقط برای کدی استفاده می‌شود که در آن خود تغییر دهندگی عمدی و با قصد است، نه در مواردی که کد به صورت تصادفی به علت خطایی مانند سرریز بافر(buffer overflow) تغییر می‌کند.

این روش غالباً برای استفاده مشروط از کد تست / اشکال زدایی کد بدون نیاز به هزینه اضافی محاسباتی برای هر چرخه ورودی / خروجی استفاده می‌شود.

تغییرات ممکن است انجام شود:
* **فقط در طی راه اندازی اولیه** - براساس پارامترهای ورودی است (زمانی که فرایند بیشتر به عنوان پیکربندی نرم‌افزار توصیف می شود و در اصطلاحات سخت‌افزار، به تنظیم کردن jumperها برای صفحات مدار چاپی تقریباً مشابه است). تغییر اشاره گرهای ورودی برنامه یک روش غیرمستقیم معادل خود تغییر دهندگی است، اما نیاز به وجود یک یا بیشتر مسیرهای دستورالعملی جایگزین دارد که افزایش حجم برنامه را شاهد خواهیم بود.
* **در طول اجرا** ("on-the-fly") - بر اساس شرایط خاص برنامه است که در طی اجرای رسیده‌است.

در حالت دیگر، تغییرات ممکن است به‌طور مستقیم خودشان به دستورالعمل‌های کد ماشین اعمال بشوند، با پوشاندن یا جایگزینی دستورالعمل‌های جدید بر روی دستورالعمل‌های در حال حاضر (به عنوان مثال: تغییر مقایسه و branch به branch بدون شرط یا " NOP ") انجام می‌شود.

در مجموعه دستورالعمل IBM / 360 و Z / Architecture دستور EXECUTE (EX) به‌طور *منطقی* بایت دوم دستورالعمل هدف خود را با ۸ بیت کم ارزش ثبات ۱، قرار می‌دهد. این اثر خود تغییر دهندگی را نسان می‌دهد، اگر چه دستورالعمل واقعی در حافظه تغییر نمی‌کند.

## کاربرد در زبان‌های سطح پایین و سطح بالا
خود-تغییر دهندگی را می‌توان به روش‌های مختلفی بسته به زبان برنامه‌نویسی و پشتیبانی آن از اشاره گرها و / یا دسترسی به کامپایلر پویا یا موتورهای مترجم انجام داد:
* **پوشاندن دستورالعمل‌های موجود** (یا بخشی از دستورالعمل‌هایی مانند opcode, register, flags یا آدرس) یا
* **ایجاد مستقیم دستورالعمل‌های کامل** یا توالی دستورالعمل‌ها در حافظه
* **ایجاد یا تغییر عبارات [[کد منبع]]** که با یک مینی کامپایل یا یک تفسیر یا ترجمه پویا صورت می‌گیرد (نگاه کنید به eval)
* **ایجاد یک برنامه کامل به صورت پویا** و سپس اجرای آن

### زبان اسمبلی
کد خود تغییر دهندگی برای پیاده‌سازی در هنگام استفاده از زبان اسمبلی بسیار ساده است. دستورالعمل‌ها می‌توانند به‌طور پویا در حافظه ایجاد شوند (یا در موارد دیگر بر روی کدهای موجود در ذخیره‌سازی برنامه‌های محافظت نشده)، در توالی معادل با آن‌هایی که کامپایلر استاندارد ممکن است به عنوان کد هدف تولید کند. با پردازنده‌های مدرن، ممکن است عوارض جانبی ناخواسته بر روی حافظه نهان CPU وجود داشته باشد که باید در نظر گرفته شود. این روش اغلب برای آزمایش شرایط «اولین بار» مورد استفاده قرار می‌گیرد، همان‌طور که در این نمونه زبان اسمبلی مناسب IBM / 360 توضیح داده شده‌است. این روش با استفاده از پوشش دستور برای کاهش طول مسیر دستورالعمل به N &#xD7; 1) -1) که در آن N که تعداد رکوردها در فایل است.
(-۱ سرباری برای انجام پوشش است)، استفاده می‌شود.

 ``

کد جایگزین ممکن است شامل هر بار آزمودن «پرچم» باشد. دستور پرش بدون قید و شرط کمی سریعتر از دستور مقایسه ای است، و همچنین باعث کاهش طول مسیر کلی می‌شود. در سیستم عامل‌های بعدی برای برنامه‌هایی که در حافظه محافظت شده قرار دارند، این تکنیک نمی‌تواند مورد استفاده قرار گیرد و به جای آن از تغییر اشاره گر به زیر برنامه استفاده خواهد شد. اشاره گر در حافظه پویا قرار دارد و می‌تواند پس از گذراندن اولین مرحله برای دور زدن OPEN تغییر یابد (ابتدا باید یک اشاره گر به جای یک پرش مستقیم بارگذاری شود و به زیر برنامه ارتباط پیدا کند که N دستورالعمل را به طول مسیر اضافه می‌کند - اما یک کاهش متناظر با N برای پرش بدون قید و شرط وجود دارد که دیگر لازم نیست).

در زیر نمونه ای از زبان اسمبلی Zilog Z80 است. این کد ثبات B را از مقدار ۰ به ۵ افزایش می‌دهد. دستور مقایسه ای "CP" در هر حلقه تغییر می‌کند. 
```nasm
;====================================================================  ==
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
;====================================================================  ==
```

کد خود تغییر دهنده گاهی اوقات برای غلبه بر محدودیت‌های مجموعه دستورها ماشین مورد استفاده قرار می‌گیرد. به عنوان مثال، در مجموعه دستورها '''اینتل ۸۰۸۰''' نمی‌توان یک بایت را از یک پورت ورودی که توسط یک ثبات مشخص شده‌است را وارد کرد. پورت ورودی به صورت استاتیکی در خود دستور، به عنوان بایت دوم دستور دو بایتی رمزگذاری می‌شود. با استفاده از کد خود تغییر دهنده، می‌توان محتویات ثبات را در بایت دوم دستور ذخیره کرد، سپس دستور اصلاح شده را برای رسیدن به اثر مورد نظر، اجرا می‌کند.

### زبانهای سطح بالا
برخی از زبان‌های کامپایل شده به صراحت اجازه می‌دهند کد خود را تغییر دهید. به عنوان مثال، فعل ALTER در **[[کوبول]]** ممکن است به عنوان دستور پرشی اجرا شود که در حین اجرای برنامه اصلاح می‌شود. یکی از روش‌های برنامه‌ریزی دسته ای استفاده از کد خودمحور است. Clipper و SPITBOL نیز امکانات را برای تغییر صریح خود فراهم می‌کنند. کامپایلر Algol در سیستم‌های B6700 یک رابط را برای سیستم عامل ارائه داد که در آن اجرای کد می‌تواند یک رشته متن یا یک فایل دیسک نام‌گذاری شده را به کامپایلر Algol منتقل کند و سپس قادر به فراخوانی نسخه جدیدی از یک روش بود.

با زبان‌های تفسیر شده، "کد ماشین" متن منبع است و ممکن است حساس به ویرایش بر روی on-the-fly باشد: در **[[اسنوبول]]**، جملات مرجع که اجرا می‌شوند، عناصر یک آرایه متنی هستند. زبان‌های دیگر مانند Perl و Python به برنامه‌ها برای ایجاد کد جدید در زمان اجرا و اجرای آن با استفاده از یک تابع eval، اجازه می‌دهند، اما اجازه تغییر در کد فعلی قابل را نمی‌دهند. تغییر غلط (حتی اگر هر کدام از ماشین‌ها واقعاً رونویسی شوند) با تغییر اشاره گرهای تابع به دست می‌آید، همان‌طور که در این مثال جاوااسکریپت داریم: 
```javascript
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 شامل موارد زیر باشد:
: StartAfresh repeat N times {
  if STATE is 1
  increase A by one
  else
  decrease A by one
  do something with A
 }
کد خود تغییر دهنده، در این مورد، به سادگی موضوع بازنویسی حلقه مانند این است:
  ``
توجه داشته باشید که تعویض دو حالتی 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 باید به کد خود تغییر دهنده را به درستی برای دلایل سازگاری عقبی انجام دهند، اما در انجام این کار بسیار دور از حالت بهینه‌اند.

### هسته Synthesis Massalin
سنتز هسته ارائه شده در دکتری پایان‌نامه هنری Massalin، یک کرنل Unix کوچک است که رویکرد ساختار یافته یا حتی شی گرا را به کد خود تغییر می‌دهد، جایی که کد برای تک تک quajectsها مانند filehandles ایجاد می‌شود؛ تولید کد برای کارهای خاص اجازه می‌دهد که هسته Synthesis به (به عنوان مترجم JIT ممکن است) تعدادی از بهینه‌سازی‌ها را اعمال کند مانند حذف ثابت ثابت یا مشترک زیر بیان.

هسته Synthesis بسیار سریع بود، اما به‌طور کامل در اسمبلی نوشته شده بود. عدم وجود قابلیت حمل و نقل مانع از ایده‌های بهینه‌سازی Massalin از هر هسته تولیدی شده‌است. با این حال، ساختار تکنیک‌ها نشان می‌دهد که آن‌ها می‌توانند با یک زبان سطح بالاتری پیاده‌سازی شوند، اگرچه پیچیده‌تر از زبان‌های سطح متوسط موجود هستند. چنین زبان و کامپایلر می‌تواند اجازهٔ توسعه سریعتر سیستم عامل‌ها و برنامه‌های کاربردی را دهد.

پل هابرلی و بروس کارش با «حاشیه سازی» کد خود تغییر دهنده و بهینه‌سازی به‌طور کلی به نفع کاهش هزینه‌های توسعه مخالفت کردند.

## مزایا
* می‌توان راه‌های سریع برای اجرای برنامه ایجاد کرد و برخی از شاخه‌های شرطی تکراری دیگر را کاهش داد.
* کد خود تغییر دهنده می‌تواند کارایی الگوریتمی را بهبود بخشد.

## معایب
کد خودتغییر داده شده برای خواندن و نگهداری سخت‌تر است، زیرا دستورالعمل‌ها در فهرست برنامهٔ منبع، لزوماً دستورالعمل‌هایی نیستند که اجرا می‌شوند. خود تغییری که شامل تعویض اشاره گرهای عملکرد است ممکن است به صورت معکوس نباشد، اگر روشن باشد که نام توابع نامیده می‌شود، متغیرهایی برای توابع شناسایی شده‌است.

کد خود تغییر دهنده می‌تواند به عنوان کد بازنویسی شود که پرچم و شاخه‌ها را به دنباله‌های جایگزین بر اساس نتیجه آزمایش تست می‌کند، اما کد خود تعییر دهنده معمولاً سریعتر اجرا می‌شود.

در پردازنده‌های مدرن با یک خط لوله دستورالعمل، کد ای که خود را به صورت متناوب تغییر می‌دهد، ممکن است به آرامی اجرا شود، در صورتی که دستورالعمل‌هایی را که پردازشگر از حافظه به خط لوله خوانده‌است، تغییر دهد. در بعضی از این پردازنده‌ها، تنها راه برای اطمینان از این که دستورالعمل‌های تغییر داده شده به درستی اجرا می‌شوند، این است که خط لوله تخلیه شود و دوباره دستورالعمل‌ها را بخواند.

در برخی از محیط‌ها، کد خود تغییر دهنده نمی‌تواند مورد استفاده قرار گیرد، مانند موارد زیر:
* نرم‌افزاری کاربردی که تحت یک سیستم عامل اجرا می‌شود با امنیت سخت‌افزار W ^ X نمی‌تواند دستورالعمل‌هایی را در صفحات مجاز به نوشتن. تنها سیستم عامل مجاز به هر دو دستورالعمل نوشتن به حافظه و بعد از آن دستورالعمل‌ها را اجرا کند.
* بسیاری از میکروکنترلرهای معماری هاروارد نمی‌توانند دستورالعملهایی را در حافظه خواندن و نوشتن اجرا کنند، اما فقط دستورالعملهایی را در حافظه ای که نمی‌توان آن را نوشت، ROM یا حافظه فلش‌های غیر خودپرداز.
* یک برنامه چند رشته‌ای ممکن است چندین رشته را اجرا کند که قسمت مشابهی از کد خود تغییر دهنده را اجرا کرده باشد، احتمالاً به خطاهای محاسباتی و خرابی‌های برنامه منجر می‌شود.

## جستارهای وابسته
* کارایی الگوریتمیک
* بیانیه ایوال
* کامپایل آنی: این روش اغلب می‌تواند بسیاری از مزایای کد خود تغییر دهنده را (به جز اندازهٔ حافظه) بدون هیچ گونه معایبی به کاربر بدهد.
* حذف کد پویا مرده
* هم‌تمثیلی
* PCASTL
* کوین (محاسبات)
* خود تکرار
* بازتاب (علم کامپیوتر)
* پچ میمون: یک اصلاح برای کد اجرا است که کد منبع اصلی برنامه را تحت تأثیر قرار نمی‌دهد
* برنامه‌نویسی توسعه پذیر: یک پارادایم برنامه‌نویسی که در آن یک زبان برنامه‌نویسی می‌تواند نحو خود را تغییر دهد
* ویروس کامپیوتری
* خود میزبانی

## منابع

## پیوند به بیرون
* Using self-modifying code under Linux
* Self-modifying C code
* Certified Self-Modifying Code

رده:پارادایم‌های برنامه‌نویسی
رده:صفحات با ترجمه بازبینی‌نشده