> **برنامهنویسها** همواره **تعاریف متفاوتی** از خیلی از جنبههای زندگی ارائه میکنند، یکی از این **تعاریف جدید،** معنای متفاوت [**آبگوشت** *(Abgoosht)*](https://fa.wikipedia.org/wiki/%D8%A2%D8%A8%DA%AF%D9%88%D8%B4%D8%AA) است! آنها به کدهایی که **سهوا** یا **عمدا** مبهم شده باشند، **آبگوشت** میگویند. با این تعریف، *آبگوشتینگ* به عملیات [**مبهمسازی** *(Obfuscation)*](https://en.wikipedia.org/wiki/Obfuscation_%28software%29) و *دیآبگوشتینگ* به عملیات **رفع ابهام** *(DeObfuscation)* گفته میشود.
**دیواین** *(Diwin)* که همچنان هنوز هم مثل قبل بر پیادهسازی **ایدههای عجیب** خود استوار است، اینبار در سری جدید [**مسابقات _#المپیکفناوری_ پردیس**](https://quera.org/events/techolympics-0407)، تصمیم گرفته تا **سطح امنیت مسابقات** امسال را به شدت و **به طرز ابلفضلیای** افزایش دهد تا جلوی تمام **تقلبها** و **نشت کدها** را در میانهی مسابقات بگیرد. او اما از روشی بسیار عجیبی برای این کار استفاده خواهد کرد، **درست کردن آبگوشت** از کدهای ارسال شده توسط شرکتکنندگان!
دیواین برای **آبگوشتینگ** *(Obfuscation)* کدهای شرکتکنندگان از **پارسر** *(Parser)* قدرتمندی به نام `abgoosht_parser` که از محصولات تولید داخل، توسط **المپیکفناوریون** *(Olampici Fanavarion)* برتر پارسال است، استفاده خواهد کرد اما از آنجایی که **دیواین** در این سری از **مسابقات المپیک فناوری،** برای حفاظت بیشتر مسابقات باید **بیشتر از همیشه** حواسش به همه باشد، تنها کدهای **پارسر** `abgoosht_parser` را در اختیار شما قرار داده است و شما وظیفه دارید تا تکنیکهای **آبگوشتینگ** مد نظر او را در این سوال پیادهسازی کنید.

# **پروژه اولیه**
برای دانلود **پروژهی اولیه** روی [این لینک](/contest/assignments/84123/download_problem_initial_project/305474/) کلیک کنید.
<details class="yellow">
<summary>
**ساختار فایلها**
</summary>
```
abgoosht-fanavari
├── <mark class="blue" title="طبق توضیحات قسمت راهاندازی پروژه این داکرفایل را بیلد کنید">Dockerfile</mark>
├── <mark class="green" title="این پوشه از قبل پیادهسازی شده است">abgoosht_parser</mark>
├── benchmarks
├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">gradio_app.py</mark>
├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">main.py</mark>
├── obfuscator
│ ├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">parser.py</mark>
│ ├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">generator.py</mark>
│ ├── techniques
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">alias_generator.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">dead_code.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">expr_complexifier.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">opaque_predicate.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">function_splitter.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">misleading_comments.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">flow_flattener.py</mark>
│ │ └── <mark class="orange" title="این فایل باید پیاده سازی شود">rename_vars.py</mark>
│ └── <mark class="green" title="این فایل از قبل پیادهسازی شده است">transformer.py</mark>
└── requirements.txt
```
</details>
<details class="grey">
<summary>
**راهاندازی پروژه**
</summary>
برای اجرای پروژه، باید **داکر، پایتون و ابزار** *pip* را از قبل نصب کرده باشید.
- ابتدا **پروژهی اولیه** را دانلود و از حالت فشرده خارج کنید.
- در پوشهی اصلی پروژه، یک **محیط مجازی پایتون** *(venv)* ایجاد و فعال کنید:
```bash terminal terminal
python -m venv venv
source venv/bin/activate # در ویندوز: venv\Scripts\activate
```
- دستور زیر را برای **نصب نیازمندیها** در پوشهی اصلی پروژه اجرا کنید:
```bash terminal terminal
pip install -r requirements.txt
```
- برای **اجرای رابط کاربری پروژه** که از قبل با استفاده از [*Gradio*](https://www.gradio.app/) طراحی شده است، دستور زیر را در مسیر پوشهی اصلی پروژه اجرا کنید:
```bash terminal terminal
python gradio_app.py
```
در صورت اجرای موفق، **یک لینک** در خروجی نمایش داده میشود که میتوانید **آن را در مرورگر باز کنید.**
- برای اجرای تستهای نمونهی پروژه، میتوانید از دستور زیر استفاده کنید:
```bash terminal terminal
python -m unittest discover tests
```
## **اجرای پروژه با داکر** *(Docker)*
در صورتی که **ابزار داکر** روی سیستم شما نصب است، میتوانید **پروژه** را به کمک داکرفایل آمادهی `Dockerfile` اجرا کنید:
- ابتدا **پروژه اولیه** را دانلود و از حالت فشرده خارج کنید.
- در پوشهی اصلی پروژه، دستور زیر را برای **ساخت ایمیج از روی داکرفایل** اجرا کنید:
```bash docker docker
docker build -t abgoosht-app .
```
- سپس برای اجرای پروژه، دستور زیر را وارد کنید:
```bash docker docker
docker run -p 7860:80 \
-v $(pwd)/examples:/app/examples \
-v $(pwd)/benchmarks:/app/benchmarks \
abgoosht-app
```
در صورت اجرای موفق، **رابط کاربری پروژه** از طریق آدرس http://localhost:7860 قابل دسترسی خواهد بود.
- برای **اجرای تستها** در محیط داکر، دستور زیر را اجرا کنید:
```bash docker docker
docker exec -it abgoosht-app python -m unittest discover tests
```
</details>
# **جزئیات پروژه**
در این پروژه، شما قرار است مجموعهای از **تکنیکهای مبهمسازی کدهای** *MiniC* را روی [**ساختار نحوی انتزاعی** *(AST)*](https://en.wikipedia.org/wiki/Abstract_syntax_tree) اعمال کنید. هدف اصلی، پیادهسازی ابزاری است که بتوانند **کدهای** *MiniC* را به صورت ایستا تحلیل کرده و تغییراتی مانند *افزودن کامنتهای بیمعنا، بازنویسی عبارات منطقی، افزودن انتسابهای زائد و بازنویسی جریان کنترل* را روی آن اعمال کنند. برای این کار، از *AST* تولیدشده توسط **پارسر** `abgoosht_parser` استفاده میکنید و با بهرهگیری از [**دیزاین پترن ویزیتور** *(Visitor)*](https://refactoring.guru/design-patterns/visitor)، **گرههای مختلف درخت** را پیمایش و تکنیکهای خواسته شده را روی **کدهای ورودی** اعمال میکنید.
<details class="blue">
<summary>
**معرفی زبان برنامهنویسی** *MiniC* **- همان** *C* **ولی کوچولوش!**
</summary>
**زبان برنامه نویسی** *MiniC* در واقع همان **زبان برنامه نویسی** *C* است، اما نسخهای **سادهتر** و **سبکشده** از آن محسوب میشود. این زبان **ساختار** و **قواعد** اصلی *C* را حفظ کرده اما برخی امکانات پیچیدهتر مانند **هدر فایلها** (`#include`)، **ساختارها** (`struct`) و **اشارهگرها** *(Pointers)* را ندارد. هدف از طراحی *MiniC* **سادهسازی زبان** و تمرکز بر **مفاهیم پایهای برنامهنویسی** است تا یادگیری و همچنین پیادهسازی **مفسر** یا **کامپایلر** برای آن راحتتر باشد.
در *MiniC* تنها **عناصر اصلی یک زبان برنامهنویسی** مانند **انواع دادهٔ ابتدایی** (`int`، `float`، `char`)، عملگرهای ریاضی و منطقی، **دستورات شرطی** (`if`، `else`) و **حلقهها** (`while`، `for`) پشتیبانی میشوند. برای ورودی و خروجی نیز به جای توابع کتابخانهای، معمولاً از دستورهای سادهتری مانند `print` یا `read` استفاده میشود. همین موضوع باعث میشود برنامهها **کوتاهتر** و **شفافتر** نوشته شوند و **پیچیدگیهای غیرضروری حذف گردند.**
کاربرد اصلی *MiniC* بیشتر در حوزههای **آموزشی** و **تحقیقاتی** مانند درسهای **طراحی زبانها و کامپایلر** است. با حذف ویژگیهای پیشرفته، این زبان بستری فراهم میکند تا دانشجو یا برنامهنویس بتواند بر اصول بنیادی مانند **تعریف متغیر، کنترل جریان برنامه و ساختار توابع** تمرکز کند. در نتیجه میتوان گفت *MiniC* نسخهای آموزشی و سادهشده از *C* است که **تنها بخشهای ضروری** و **بنیادین** آن را در بر میگیرد. در این سوال نیز **تضمین** خواهد شد که کدهایی که به عنوان **ورودی** به **مبهمساز** شما داده میشود از **نوع** *MiniC* هستند و **فاقد** پیچیدگیهای معمول زبان برنامه نویسی *C* میباشند. به مثالهای زیر از این زبان کوچولو، توجه کنید:
```c example1.mc c
int sum(int n) {
int s;
s = 0;
int i;
i = 1;
while (i <= n) {
s = s + i;
i = i + 1;
}
return s;
}
void main() {
int result;
result = sum(5);
print(result);
}
```
```c example2.mc c
int fib(int n) {
if (n <= 1) {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
}
void main() {
int i;
for (i = 0; i < 6; i = i + 1) {
print(fib(i));
}
}
```
```c example3.mc c
void main() {
int n;
read(n);
if (n % 2 == 0) {
print(0);
} else {
print(1);
}
}
```
</details>
<details class="grey">
<summary>
**معرفی آبگوشت پارسر** `abgoosh_parser`
</summary>
# **ساختار کلی پروژه** `abgoosht_parser`
**پروژهی** `abgoosht_parser` در واقع یک چارچوب ساده و قابل توسعه برای مبهمسازی و بازنویسی **برنامههای** *MiniC* است. این چارچوب، شامل اجزایی برای **تجزیه کد** *(Parsing)،* **تبدیل** *(AST Transforming)* و **تولید مجدد کد** *(Code Generation)* میباشد. ساختار پوشهبندی پروژه بهگونهای است که شفافیت ماژولها حفظ شده و شما به راحتی میتواند اجزای مختلف را گسترش دهید.
در هستهی این سیستم، ابزارهایی مانند `c_parser.py` و `c_ast.py` قرار دارند که مسئول ایجاد *AST* از کد *MiniC* و تعریف ساختار آن هستند. **درخت نحوی انتزاعی** *(AST)* در این پروژه بهشکل مجموعهای از کلاسهای پایتونی تعریف شده که بازتاب دقیق **ساختارهای نحوی زبان** *MiniC* است. **فایل** `c_parser.py`، **تجزیهگر اصلی** است که با استفاده از **ابزارهای** `lex` و `yacc` است. این فایلها با کمک `c_lexer.py` *(تحلیلگر واژگانی)* به ورودی زبان *MiniC* معنا میبخشند و با استفاده از `c_parser.py` آن را به **درختی نحوی انتزاعیاش** تبدیل میکنند. **ساختار کلی** `abgoosht_parser` به شکل زیر است:
```
abgoosht_parser
├── __init__.py
├── _ast_gen.py
├── _build_tables.py
├── _c_ast.cfg
├── ast_transforms.py
├── c_ast.py
├── c_generator.py
├── c_lexer.py
├── c_parser.py
├── ply
└── plyparser.py
```
**فایلهای** `c_lexer.py` و `c_parser.py` **هستهی اصلی تحلیل زبانی** را تشکیل میدهند؛ اولی وظیفهی شناسایی واژهها *(کلیدواژهها، عملگرها، شناسهها و...)* را دارد و دومی با تکیه بر گرامر **زبان** *MiniC* ساختار نحوی برنامه را ایجاد میکند. **درختهای نحوی** *(AST)* در قالب **کلاسهایی** که در `c_ast.py` تعریف شدهاند نمایش داده میشوند. این کلاسها خودکار از روی فایل پیکربندی `_c_ast.cfg` توسط اسکریپت `_ast_gen.py` تولید میشوند. اگر لازم باشد روی این درخت **تغییر** یا **مبهمسازی** صورت گیرد، **فایل** `ast_transforms.py` ابزارهای لازم را فراهم میکند.
در طرف دیگر، **فایل** `c_generator.py` وجود دارد که **مسیر معکوس** را طی میکند؛ یعنی *AST* کد را گرفته و دوباره به کد *MiniC* خوانا **بازتولید** میکند. ماژولهایی مثل `_build_tables.py` و `plyparser.py` نقش کمکی دارند و برای مدیریت جداول گرامری و تعامل سادهتر استفاده میشوند. به این ترتیب، **مجموعهی این فایلها** زنجیرهای کامل میسازند: از د*ریافت کد خام MiniC، تولید AST، اعمال تغییرات احتمالی روی آن و در نهایت بازگردانی یا تولید خروجی دلخواه.*
# **فایل** `generators.py` **و تبدیل AST به کد C**
یکی از اجزای مهم پروژه، **بخش تولید کد** یا *Generator* است. ماژول `c_generator.py` شامل **کلاسی** به نام `CGenerator` است که یک **ویزیتور** *AST* محسوب میشود. این کلاس تمام **رئوس** *AST* را پیمایش میکند و برای هر راس، کدی معادل در زبان *MiniC* تولید میکند. کدی که شما در پروژه اولیه دریافت میکنید شامل یک کلاس سادهتر به نام `CustomGenerator` است که از `CGenerator` ارثبری میکند و در این سوال، این فایل قابل تغییر نخواهد بود.
این بخش از پروژه از **دیزاین پترن ویزیتور** *(Visitor)* استفاده میکند. این الگو به ما اجازه میدهد عملیات مختلف *(مثلاً تولید کد، بررسی، تبدیل)* را روی کد آنها اعمال کنیم. هر راس در *AST* با **متدی از جنس** `visit_*` هندل میشود و این شیوهی به شما این امکان را خواهد داد که با پیادهسازی ویزیتورهای شخصیسازی شده، عملیات **ابهامسازی کد** را پیادهسازی کند.
```python generators.py python
from abgoosht_parser.c_generator import CGenerator
class CustomGenerator(CGenerator):
def __init__(self):
super().__init__()
self.indent_level = 0
def generate_code(ast):
generator = CustomGenerator()
return generator.visit(ast)
```
# **فایل** `parser.py` **و تبدیل کد C به AST**
در ابتدای هر پردازش، باید کد *MiniC* **تجزیه** شود. این مسئولیت بر عهدهی `c_parser.py` است که با استفاده از توابع `parse_code` و `parse_file` که **ساختار اولیه** `abgoosht_parser` قرار داده شدهاند، در واقع رابطی ساده برای فراخوانی این تجزیهگر فراهم میکنند. ورودی آنها کدی متنی به **زبان** *MiniC* است و خروجی، *AST* مربوط به کد به شکل یک **درخت** *AST* خواهد بود. **تبدیل کد** به *AST،* گام اول هر نوع **تحلیل** و **ابهامسازی** روی کد ورودی است. این *AST* تولید شده سپس توسط **تکنیکهای مختلف** که جلوتر توسط شما پیادهسازی میشوند، قابل **اصلاح** یا **بازنویسی** است.
```python parser.py python
from abgoosht_parser import c_parser, c_ast
def parse_code(code):
parser = c_parser.CParser()
ast = parser.parse(code)
return ast
def parse_file(filename):
with open(filename, 'r') as f:
code = f.read()
return parse_code(code)
```
# **فایل** `transformer.py` **تبدیل ساختار AST**
برای **دستکاری** *AST،* ابزار `Transformer` در **پروژه اولیه** تعریف شده است. این کلاس با دریافت لیستی از **تکنیکهای تبدیل** *(Transformation Techniques)،* آنها را به ترتیب روی *AST* اعمال میکند. **هر تکنیک،** یک **کلاس ویزیتور** جدید است که **راسهای خاصی** از *AST* را شناسایی و بازنویسی میکند. این طراحی مبتنی بر **الگوی طراحی استراتژی** *(Strategy Pattern)* است؛ چراکه هر تکنیک به عنوان یک *استراتژی متغییر* عمل میکند و **میتوان به دلخواه تکنیکها را اضافه، حذف یا جابجا کرد.**
**کلاس** `Transformer` باعث شده **ترکیب** و **اجرای چندین تکنیک** بهشکل ساده و پایپلاین ممکن شود. به عنوان مثال، میتوان *تکنیکی برای حذف کد مرده، تکنیکی برای تغییر نام متغیرها و تکنیکی برای بازآرایی بلوکهای شرطی* را به ترتیب اجرا کرد و در نهایت *AST* نهایی را تولید نمود.
```python transformer.py python
class Transformer:
def __init__(self, techniques=None):
self.techniques = techniques or []
def transform(self, ast):
current_ast = ast
for technique in self.techniques:
current_ast = technique.visit(current_ast)
if current_ast is None:
raise ValueError(f"Technique {technique.__class__.__name__} returned None")
return current_ast
def apply_transformations(ast, techniques):
transformer = Transformer(techniques)
return transformer.transform(ast)
```
</details>
<details class="green">
<summary>
**پیادهسازی آبگوشت** `obfuscator`
</summary>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *AliasGenerator* **و فایل** `alias_generator.py`
</summary>
**این تکنیک** روی بخشهایی از کد که **متغیرها** در آن تعریف شدهاند، عمل میکند. هر متغیری که تعریف میشود، دقیقا بعد از آن یک متغیر جدید با نام اصلی به علاوه پسوند `_alias` ساخته میشود. مثلاً اگر **متغیری به نام** `count` باشد، بلافاصله **متغیری به نام** `count_alias` تعریف میشود. **نوع داده** این متغیر جدید **دقیقاً همان نوع متغیر اصلی** است و **مقدار اولیهاش** به صورت مستقیم برابر با **همان متغیر اصلی** قرار میگیرد *(مثل یک اشارهگر به مقدار همان متغیر).*
```python obfuscator/techniques/alias_generator.py python
from abgoosht_parser.c_ast import NodeVisitor
class AliasGenerator(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `AliasGenerator` از **فایل** `alias_generator.py` هستید.
این عملیات باید **برای تمام متغیرهای تعریفشده در بدنه** توابع انجام میشود. پس از **اجرای این تکنیک،** کدی تولید میشود که در آن هر متغیر، یک **نسخه اضافی** با **نامی مشابه ولی پسوند** `_alias` دارد که دقیقا همان مقدار و نوع را نگه میدارد. این باعث میشود **تحلیل** و **درک نحوه** استفاده از متغیرها سختتر شود، زیرا چندین نام مختلف برای همان داده وجود دارد، **اما عملکرد برنامه کاملاً بدون تغییر باقی میماند.** به مثال زیر از این تکنیک ابهامسازی توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c alias_generator_example.mc c
int main(){
int a = 10;
int b = 20;
int c = a + b;
printf("Hello world\n");
return 0;
}
```
+ **کد مبهم شده با تکنیک** *AliasGenerator:*
```c alias_generator_example.mc c
int main(){
int a = 10;
int a_alias = a;
int b = 20;
int b_alias = b;
int c = a + b;
int c_alias = c
printf("Hello world\n");
return 0;
}
```
- پس از ابهام سازی **سه متغیر جدید** با **نامهای** `a_alias`,`b_alias` و `c_alias` به کد ساخته شده اضافه شدهاند که **مقادیری برابر با متغیر اصلی** خود دارند و **از یک نوع** هستند.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *ExprComplexifier* **و فایل** `expr_complexifier.py`
</summary>
**این تکنیک** روی عبارات محاسباتی در کد تمرکز دارد و تلاش میکند که **عبارات ساده** را به **شکلهای پیچیدهتر** ولی **معادل** تبدیل کند. هدف این است که نتیجه نهایی همان باشد **ولی ظاهر کد پیچیدهتر شود** و **خواندن آن سختتر شود.**
در این بخش **تنها دو مورد از اعمال ریاضی** مد نظر میباشند. در مواردی که **عملگر جمع** (`+`) در یک عبارت باینری دیده شود، آن عبارت به **فرم معادلی** تبدیل میشود که از عملیات بیت به بیت استفاده میکند: مقدار اصلی **به صورت** `(left ^ right) + ((left & right) << 1)` **جایگزین** میشود. در واقع عبارت `a + b` به ` (a XOR b) + ((a AND b) shifted left by 1)` تغییر میکند که **از نظر محاسباتی معادل جمع معمولی است اما نوشتار آن بسیار پیچیدهتر است.** در مواردی که **عمل ضرب** (`*`) بین یک **عدد صحیح** و **مقدار** `2` باشد، این عبارت با **یک شیفت چپ** (`<< 1`) معادل **جایگزین** میشود. **برای مثال،** `2 * x` **یا** `x * 2` **تبدیل به** `x << 1` **خواهد شد.**
```python obfuscator/techniques/expr_complexifier.py python
from abgoosht_parser.c_ast import NodeVisitor
class ExprComplexifier(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `ExprComplexifier` از **فایل** `expr_complexifier.py` هستید.
در نهایت **توجه داشته باشید** که از این تکنیک درهمسازی در این سوال **فقط این دو مورد تبدیل مورد نیاز است** و اگر **عملگرهای دیگری** دیده شوند یا شرایط فوق **برقرار نباشد،** عبارت **بدون تغییر** باقی میماند. به مثالهای زیر توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c expr_complexifier_example.mc c
int main(){
int ans = 2 + 5;
int output = ans * 2;
return 0;
}
```
+ **کد مبهم شده با تکنیک** *ExprComplexifier:*
```c expr_complexifier_example.mc c
int main(){
int ans = (2 ^ 5) + ((2 & 5) << 1);
int output = ans << 1;
return 0;
}
```
+ همانطور که مشاهده میکنید، پس از اعمال این روش ابهامسازی، **عبارات ریاضی** نسبت به کد اولیه **پیچیدهتر** شده **اما دقیقا از لحاظ مقدار اولیه یکسان میباشند.**
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *DeadCodeInserter* **و فایل** `dead_code.py`
</summary>
**این تکنیک** درون بلوکهای کد، بخصوص بخشهایی که چند دستور پشت سر هم قرار دارند، **کدهایی اضافه میکند که هیچگاه اجرا نمیشوند.** این کدهای اضافه در این سوال، **به شکل یک شرط** `if` **با شرط همیشه نادرست** (`if (0)`) و **یک حلقه** `for` **با شرط آغاز و پایانی که هیچگاه وارد حلقه نمیشود** (`for (;0;)`) هستند.
```python obfuscator/techniques/dead_code.py python
from abgoosht_parser.c_ast import NodeVisitor
class DeadCodeInserter(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `DeadCodeInserter` از **فایل** `dead_code.py` هستید.
این دو **کد مرده** *(Dead Code)* به شکل زیر خواهند بود:
+ **شرط** `if` **با مقدار و بدنه** `0` که **هیچگاه** اجرا **نخواهد شد** و تغییری در ساختار اصلی کد به وجود **نخواهد آورد:**
```
if (0)
{
0;
}
```
+ **حلقه** `for` با **بدنهای خالی** که **هیچگاه اجرا نخواهد شد** و این کد مرده نیز تغییری در ساختار اصلی کد به وجود **نیاورده** اما باعث **پیچیدهتر شدن** و **ناخواناتر شدن** کد میشود:
```
for (; 0;)
{
}
```
**توجه داشته باشید** که در تکنیکی که در این سوال پیادهسازی میکنید، باید **دقیقا در ابتدای هر بلاکی که دارای بدنه است** *(مانند for بالا بدنهاش خالی نیست)* **یک شرط مرده** و **دقیقا در انتهای بلاک یک شرط حلقه مرده** را اضافه کند. **به مثال زیر از ابهامسازی کد توجه کنید:**
+ **کد** *MIniC* **اولیه:**
```c dead_code_example.mc c
int main(){
int ans = 2 + 5;
int output = ans * 2;
if(ans > 10){
ans = 10;
}
print(output);
return 0;
}
```
+ **کد مبهم شده با تکنیک** *DeadCodeInserter:*
```c dead_code_example.mc c
int main(){
if (0)
{
0;
}
int ans = 2 + 5;
int output = ans * 2;
if(ans > 10){
if (0)
{
0;
}
ans = 10;
for (; 0;)
{
}
}
print(output);
return 0;
for (; 0;)
{
}
}
```
+ همانطور که مشاهده میگنید، در کد مبهم شده در ابتدای هر بلاک کد **یک شرط** `if` **مرده** و در انتهای هر بلاک کد **یک حلقه** `for` **مرده** درج شده است که **خوانایی کد را کاهش** اما **عملکرد کد** را نسبت به کد اولیه **بدون تغییر** نگه میدارد.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *FunctionSplitter* **و فایل** `function_splitter.py`
</summary>
**در این تکنیک مبهمسازی** باید ابتدا، **تابعهای کمی تا حدودی بزرگ** (یعنی توابعی که بیش از دو دستور دارند)، به **دو بخش تقریباً مساوی** تقسیم میشوند. **نیمهی اول** همان بدنهی اصلی تابع باقی میماند و **نیمهی دوم** در یک **تابع جدید** با **نامی مشتقشده** از نام **تابع اصلی** (دقیقا به شکل `<نام_تابع>_split_<شمارنده>`) قرار میگیرد. این تابع کمکی جدید باید تمامی پارامترهای تابع اصلی را به همراه متغیرهایی که در نیمه دوم استفاده شدهاند اما در نیمه اول تعریف شدهاند، به عنوان پارامتر دریافت کند.
```python obfuscator/techniques/function_splitter.py python
from abgoosht_parser.c_ast import NodeVisitor
class FunctionSplitter(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `FunctionSplitter` از **فایل** `function_splitter.py` هستید.
**توجه داشته باشید** که نام تابع کمکی و پارامترهای اضافه باید **دقیقاً مطابق این الگو** ساخته شوند تا در سیستم داوری قابل تشخیص باشند. **پارامترهای اضافه شده** باید از **نوع** و **مشخصات همان متغیرهای اصلی** کپی میشوند، بدون ایجاد تغییرات اضافهای که **در سیستم داوری مورد پذیرش قرار نخواهند گرفت.** تابع اصلی باید پس از اجرای نیمه اول، با ارسال پارامترهای لازم، تابع کمکی را فراخوانی کند. اگر **نوع بازگشتی تابع اصلی غیر** `void` باشد، **فراخوانی تابع کمکی** باید در **یک دستور بازگشت** (`return`) قرار گیرد، در غیر این صورت فقط فراخوانی به تنهایی در بدنه اضافه میشود.
**نتیجه نهایی،** کدی است که **ساختار توابع بزرگ** را به **مجموعهای از توابع کوچکتر** تقسیم میکند که با یکدیگر در تعاملاند. این کار باعث میشود درک جریان کنترل و ردیابی مقادیر در کد **دشوارتر** شود، بدون آنکه منطق برنامه تغییر کند. **به مثال زیر از این روش ابهامسازی توجه کنید:**
+ **کد** *MIniC* **اولیه:**
```c function_splitter_example.mc c
int add_and_print(int a, int b) {
int sum = a + b;
int square = sum * sum;
printf("sum: %d\n", sum);
printf("square: %d\n", square);
return sum;
}
```
+ **کد مبهم شده با تکنیک** *FunctionSplitter:*
```c function_splitter_example.mc c
int add_and_print_split_0(int a, int b, int sum, int square)
{
printf("sum: %d\n", sum);
printf("square: %d\n", square);
return sum;
}
int add_and_print(int a, int b)
{
int sum = a + b;
int square = sum * sum;
return add_and_print_split_0(a, b, sum, square);
}
```
+ در مثال بالا، **تابع اولیه** `add_and_print` به **دو تابع** تقسیم شده است. **تابع اولیه** `add_and_print` که **پارامترها** و **خروجیاش** تغییری **نکرده است** و **تابع** `add_and_print_split_0` که **نیمهی دوم** کدهای بدنهی تابع اولیه را داراست. به این صورت، کد اولیه به دو تابع تقسیم شده که **تحلیل** و **خواناییش** را **کاهش** میدهد.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *MisleadingComments* **و فایل** `misleading_comments.py`
</summary>
**در این تکنیک،** در هر بلوک کد که **مجموعهای از دستورات** را در خود دارد *(و بلوکی با بدنه خالی نیست)،* **دقیقاً یک کامنت گمراهکننده** از بین کامنتهای زیر اضافه خواهد شد. متن کامنت از بین قالبهای ثابت و مشخص انتخاب میشود و به شکل زیر خواهند بود:
+ `// optimization level={}`
+ `// todo: refactor this loop`
+ `// warning: potential overflow at line {}`
+ `// debug: value of x is unknown`
+ `// temporary hack, remove later`
همانطور که مشاهده میکنید، **کامنتهای اول** و **سوم** دارای یک *Placeholder* میباشند، که باید با **یک مقدار عددی** جایگزین شوند. **مقدار عددی** این شمارنده در ابتدا از مقدار `0` شروع شده و با **مشاهده هر بلاک کد که خالی نباشد،** مبهمسازی شما باید **به ترتیب** و **به صورت چرخشی** هر کدام از کامنتها را در **ابتدای آن بلاک کد** درج کرده و **مقدار شمارنده را یکی افزایش دهد.**
```python obfuscator/techniques/misleading_comments.py python
from abgoosht_parser.c_ast import NodeVisitor
class MisleadingComments(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `MisleadingComments` از **فایل** `misleading_comments.py` هستید.
این کامنتها **به صورت رشته متنی** در ابتدای هر بلاک کد به گونهای درج میشود که ترتیب اجرای دستورات **تغییر نکند.** هدف این تکنیک افزودن کامنتهایی است که هیچ ارتباط واقعی یا فنی با کد **ندارند** و صرفاً باعث گمراه کردن خواننده یا تحلیلگر کد میشوند، **بدون اینکه تاثیری روی عملکرد برنامه داشته باشند.** به مثال زیر توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c misleading_comments_example.mc c
void compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i;
}
if (sum > 50) {
printf("Large sum: %d\n", sum);
} else {
printf("Small sum: %d\n", sum);
}
while (sum > 0) {
sum--;
}
{
int temp = sum * 2;
printf("Temp: %d\n", temp);
}
}
int main() {
compute(10);
{
int a = 5;
int b = 10;
if (a < b) {
printf("a < b\n");
}
}
return 0;
}
```
+ **کد مبهم شده با تکنیک** *MisleadingComments:*
```c misleading_comments_example.mc c
void compute(int n)
{
// optimization level=5;
int sum = 0;
for (int i = 0; i < n; i++)
{
// optimization level=0;
sum += i;
}
if (sum > 50)
{
// todo: refactor this loop;
printf("Large sum: %d\n", sum);
}
else
{
// warning: potential overflow at line 2;
printf("Small sum: %d\n", sum);
}
while (sum > 0)
{
// debug: value of x is unknown;
sum--;
}
{
// temporary hack, remove later;
int temp = sum * 2;
printf("Temp: %d\n", temp);
}
}
int main()
{
// debug: value of x is unknown;
compute(10);
{
// warning: potential overflow at line 7;
int a = 5;
int b = 10;
if (a < b)
{
// todo: refactor this loop;
printf("a < b\n");
}
}
return 0;
}
```
+ در مثال بالا، **ترتیب چرخشی درج کامنتها** و **کامنتهایی** که دارای *Placeholder* هستند مشخص شده است. **توجه داشته باشید** که این ترتیب، **بر اساس ترتیب پیمایش** *dfs* ای بر روی **ساختار درخت** *AST* کد میباشد. به همین دلیل است که **اولین کامنت بیهوده افزوده شده، کامنت** `// optimization level=0;` میباشد.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *VariableRenamer* **و فایل** `rename_vars.py`
</summary>
**در این تکنیک،** تمامی **نامهای متغیرهای محلی** و **پارامترهای توابع** بهصورت کامل **بازنامگذاری** میشوند به طوری که **هر نام یکتا** به **یک نام جدید با طول دقیقا هشت حرف انگلیسی** *(حروف بزرگ و کوچک)* تبدیل میشود. **این بازنامگذاری ثابت است؛** یعنی هر بار که یک نام مشخص در کد دیده شود، به همان **نام جدید اختصاص یافته تغییر مییابد** و **تمامی ارجاعات** به آن متغیر در جایگاههای مختلف کد نیز **اصلاح** میشود. نکته مهم این است که **نام توابع تغییر نمیکند** و فقط **متغیرها** و **پارامترهای** توابع تحت این تبدیل قرار میگیرند.
```python obfuscator/techniques/rename_vars.py python
from abgoosht_parser.c_ast import NodeVisitor
class VariableRenamer(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `VariableRenamer` از **فایل** `rename_vars.py` هستید.
**نامهای جدید کاملاً تصادفی ساخته میشوند،** مثلاً به شکل `AbcDefGh`، اما در کل برنامه **ثابت** و **یکسان** باقی میمانند تا **رفتار کد حفظ شود** و فقط **ابهام در نامگذاری** متغیرها ایجاد شود. این روش باید بهگونهای پیادهسازی شود که **هیچ تغییری در ساختار** و **عملکرد اصلی** برنامه **رخ ندهد** و تنها باعث گمراهی خواننده کد از طریق تغییر اسامی متغیرها شود.
+ **کد** *MIniC* **اولیه:**
```c rename_vars_example.mc c
int add(int a, int b) {
int sum = a + b;
int square = sum * sum;
printf("sum=%d, square=%d\n", sum, square);
return sum;
}
int main() {
int x = 5;
int y = 10;
int result = add(x, y);
printf("result=%d\n", result);
return 0;
}
```
+ **کد مبهم شده با تکنیک** *VariableRenamer:*
```c rename_vars_example.mc c
int add(int hQZrHNIb, int aAblsZPU)
{
int nZqxayZQ = hQZrHNIb + aAblsZPU;
int KMgwRWmn = nZqxayZQ * nZqxayZQ;
printf("sum=%d, square=%d\n", nZqxayZQ, KMgwRWmn);
return nZqxayZQ;
}
int main()
{
int iNVTbmjf = 5;
int rcfEdJgf = 10;
int yhEbwcjU = add(iNVTbmjf, rcfEdJgf);
printf("result=%d\n", yhEbwcjU);
return 0;
}
```
+ همانطور که در مثال بالا مشاهده میکنید، **اسامی تمامی متغیرها** و **پارامترهای توابع** و **همچنین تمام استفادههای آنها** در کد بازنویسی شدهاند و به رشتههایی با **مقادیر تصادفی** تبدیل شدهاند تا **خوانایی کد را کاهش دهند.**
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *ControlFlowFlattener* **و فایل** `control_flow_flattener.py`
</summary>
**در این تکنیک مبهمسازی**، جریان کنترل در تابع اصلی (تابع `main`) به صورت عادی دنبال نمیشود، بلکه به کمک یک **متغیر وضعیت** *(State variable)* و یک **ساختار** `switch-case` **درون یک حلقهی بینهایت بازنویسی** میشود. در ابتدای بدنه تابع، **یک متغیر جدید** به نام `__cf_state` تعریف و مقدار اولیهی آن صفر قرار داده میشود. **هر دستور در بدنهی اصلی تابع** به یک `case` متناظر تبدیل میشود که در آن، ابتدا **دستور اصلی** اجرا شده و سپس **مقدار متغیر** `__cf_state` به **شمارهی دستور بعدی** تغییر میکند.
در این ساختار، یک `switch` **روی مقدار** `__cf_state` وجود دارد که مشخص میکند **کدام بخش از کد** باید اجرا شود. این `switch` **داخل یک حلقهی** `while(1)` قرار داده میشود تا پس از هر تغییر وضعیت، اجرای **دستورات بعدی** ادامه پیدا کند. در مواردی که یک دستور از نوع `return` باشد، دیگر نیازی به تغییر وضعیت نیست و همانجا جریان اجرا خاتمه پیدا میکند.
```python obfuscator/techniques/control_flow_flattener.py python
from abgoosht_parser.c_ast import NodeVisitor
class ControlFlowFlattener(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `ControlFlowFlattener` از **فایل** `control_flow_flattener.py` هستید.
به این ترتیب، **ساختار اصلی کد** به جای توالی سادهای از دستورات، به یک **ماشین حالت** *(State Machine)* تبدیل میشود که **دنبال کردن جریان اجرای آن برای انسان** و **ابزارهای تحلیل ایستا** بسیار دشوارتر است. این کار بدون تغییر در منطق برنامه، باعث **افزایش سطح ابهام** و **سختتر شدن درک اجرای واقعی کد** میشود. به مثال ساده زیر توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c control_flow_flattener_example.mc c
int main() {
int a = 5;
int b = 10;
return a + b;
}
```
+ **کد مبهم شده با تکنیک** *ControlFlowFlattener:*
```c control_flow_flattener_example.mc c
int main() {
int __cf_state = 0;
while (1) {
switch(__cf_state) {
case 0:
a = 5;
__cf_state = 1;
break;
case 1:
b = 10;
__cf_state = 2;
break;
case 2:
return a + b;
}
}
}
```
+ **این روش مبهمسازی،** کد بسیار سادهی اولیه را به **ماشین حالتی** تبدیل کرده است که **دقیقا همان عملکرد** را در سطح کد خواهد داشت اما **خوانایی** و **دنبال کردن جریان** اجرای کد را **بسیار پیچیده** کرده است.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *OpaquePredicate* **و فایل** `opaque_predicate.py`
</summary>
**در این تکنیک مبهمسازی** هر دستور **ساده** داخل بلاکهای کد (دستوراتی که جزو دستورات `if`, `while`, `return`, `break`, `continue`, `switch` و **تعریف متغیرها نیستند**) بهجای اینکه مستقیماً اجرا شود، داخل یک **شرطِ همیشهصادق** `1 == 1` قرار میگیرد تا خواندن و تحلیل کد سختتر شود. **همهٔ دستورات واجد شرایط** *(غیر از مواردی که پیشتر اشاره شد)* همیشه و بدون تغییر اضافهتری با یک `if` احاطه میشوند. شرط `if` مورد استفاده ثابت و ساده است (`1 == 1`) تا رفتار اجرا هیچگاه تغییر نکند ولی ساختار کد پیچیدهتر و پر از شاخههای بیاثر شود.
```python obfuscator/techniques/opaque_predicate.py python
from abgoosht_parser.c_ast import NodeVisitor
class OpaquePredicate(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `OpaquePredicate` از **فایل** `opaque_predicate.py` هستید.
تبدیل باعث میشود **بلوکهای کد** با شاخههای بیاثر و **شرطهای همیشهحقیقی** احاطه شوند؛ این امر خوانایی و تحلیل ایستا را **کاهش** میدهد بدون اینکه منطق یا جریان دادهٔ برنامه تغییر کند. چنین تغییری برای ابزارهای سادهٔ آنالیز یا بررسی دستی، ردیابی مسیرهای اجرا و یافتن رابطهٔ بین دستورها را **دشوارتر** میکند. **به مثال تغییر پیش و پس از اِعمال این تکنیک توجه کنید:**
+ **کد** *MIniC* **اولیه:**
```c opaque_predicate_example.mc c
int main() {
int a = 5;
int b = 10;
int c = a + b;
printf("%d\n", c);
return 0;
}
```
+ **کد مبهم شده با تکنیک** *OpaquePredicate:*
```c opaque_predicate_example.mc c
int main() {
int a = 5;
int b = 10;
int c = a + b;
if (1 == 1) {
printf("%d\n", c);
}
return 0;
}
```
+ **در کد ابهام شده،** میتوان مشاهده کرد که **تنها دستور** `printf` توسط یک شرط `if` **همیشه درست** ابهام شده و دستورات دیگر به دلیل اینکه **تعاریف متغیرها** هستند، **بی تغییر باقی ماندهاند.**
</details>
</details>
# **آنچه باید آپلود کنید**
+ **توجه**: سیستم داوری این سوال برای نمرهدهی، ابتدا با استفاده از **مبهمساز شما،** کدهای ورودی در هر تستکیس را مبهم کرده و سپس *AST* **کد مبهمشده توسط شما** را با *AST* **کدی که به درستی از قبل ابهام شده** مقایسه کرده و در صورتی که **شباهت** *AST* این دو کد، **اکیداً بیشتر از ۸۰ درصد باشد،** نمرهی آن تستکیس مشخص به کد ارسالی شما **تعلق خواهد گرفت.**
+ **توجه**: سیستم داوری در هر مرحله، **کدهای مبهمشده توسط شما** را اجرا کرده و از **عدم تغییر عملکرد** این کدها نسبت به **کدهای ورودی اولیه،** اطمینان حاصل میکند. در صورتی که **هر گونه تغییری** در روند اجرای **کدهای مبهمشده توسط شما** *(مثل کامپایل ارورها یا خروجیهای متفاوت)* رخ دهد، حتی در صورت برآورده شدن **شرط پیشین** و **شباهت بیش از ۸۰ درصدی،** ارسال شما برای تستکیس مشخص شده **امتیازی در بر نخواهد داشت و نمره صفر دریافت خواهد کرد.**
+ **توجه**: پس از پیادهسازی موارد خواسته شده، **کل فایلهای پروژه** را زیپ کرده و ارسال کنید.
+ **توجه**: شما مجاز به **افزودن فایل جدیدی** در این ساختار **نیستید** و تنها باید تغییرات را در فایلهای موجود اعمال کنید.
+ **توجه**: که نام فایل _Zip_ اهمیتی **ندارد**.
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.