ReverseEngineering
1.32K subscribers
50 photos
11 videos
106 files
888 links
Download Telegram
Process API
برنامه‌ ها چطور با سیستم‌ عامل صحبت میکنن
تا اینجا فهمیدیم سیستم‌ عامل میتونه Process بسازه اجرا کنه و بین اونا جا به‌ جا بشه

اما یک سوال مهم:

برنامه‌ ها چطور به سیستم‌ عامل میگن که یک Process جدید بساز یا یک برنامه رو اجرا کن؟

آیا مستقیما با کرنل صحبت میکنن؟
نه
برنامه‌ها از چیزی به نام API (Application Programming Interface) استفاده میکنند

API
رو میتونیم مثل یک واسطه بین برنامه و سیستم‌ عامل در نظر بگیریم

API
یعنی چی؟
فرض کنید به یک رستوران رفتید
شما مستقیم وارد آشپزخانه نمیشید تا غذا درست کنید
سفارش رک به گارسون میدید گارسون اون رو به آشپز میرسونه و بعد غذا رو براتون میارن

در این مثال:

آشپز = Kernel

گارسون = API

مشتری = برنامه

برنامه هم به همین شکل درخواستش رو از طریق API به سیستم‌ عامل میفرسته

Process API
چیه؟
Process API
مجموعه‌ای از توابع که به برنامه اجازه میده Processها رو مدیریت کنه

مثلا:

ساختن یک Process جدید
اجرای یک برنامه
منتظر موندن تا پایان یک Process
تموم شدن به یک Process

مهم‌ترین توابع در لینوکس

در لینوکس بیشتر با این سه تابع آشنا میشید:
fork()
وظیفه‌اش ساختن یک Process جدیده
وقتی fork() اجرا میشه سیستم‌عامل از Process فعلی یک کپی میسازه

بعد از اون دو Process وجود دارند:

Parent (والد)

Child (فرزند)

هر دو از همون نقطه به اجرای خودشون ادامه میدن

exec()
بعضی وقتا نمیخایم فقط یک کپی از Process داشته باشیم میخایم برنامه دیگه ای اجرا بشه

اینجاست که exec() وارد عمل میشه


exec()
برنامه فعلی رو با یک برنامه جدید جایگزین میکنه

مثلا یک Shell بعد از گرفتن دستور کاربر با exec() برنامه‌ ای مثل ls یا cat رو اجرا میکنه

wait()
اگر Process والد بخاد صبر کنه تا Process فرزند کارش تموم بشه از wait() استفاده میکنه
بدون wait() ممکنه والد زودتر ادامه پیدا کنه و ترتیب اجرای برنامه به‌ هم بخوره


یک مثال واقعی:

فرض کنید در ترمینال مینویسید:
python script.py

پشت صحنه تقریبا این اتفاق‌ ها میوفته:

Shell
یک Process جدید با fork() ایجاد میکنه

Process
فرزند با exec() برنامه Python رو اجرا میکنه

Process
والد با wait() منتظر میمونه تا اجرای اسکریپت تموم بشه

همه این مراحل در کسری از ثانیه انجام میشن

چرا این موضوع مهمه؟

اگر روی لینوکس برنامه‌ ها یا بدافزار ها رو تحلیل کنید به مرتب با این توابع رو به‌ رو میشید همچنین در ویندوز هم مفاهیم مشابه ای وجود دارن فقط اسم توابع فرق میکنه برای مثال ایجاد Process در ویندوز معمولا با توابعی مانند

CreateProcess
انجام میشه اما ایده اصلی همونه:

درخواست از سیستم‌ عامل برای ساخت و مدیریت یک Process


Process API
راه ارتباط برنامه با سیستم‌ عامل برای مدیریت Process هاست

سه تابع مهم:

fork() → ساختن Process جدید

exec() → اجرای یک برنامه جدید

wait()
منتظر موندن برای تموم شدن Process فرزند

اگر این سه تابع رو خوب بفهمید درک نحوه اجرای برنامه‌ ها داخل لینوکس و حتی مفاهیم مشابه در ویندوز براتون بسیار راحت‌ تره
👍1
ReverseEngineering
Process API برنامه‌ ها چطور با سیستم‌ عامل صحبت میکنن تا اینجا فهمیدیم سیستم‌ عامل میتونه Process بسازه اجرا کنه و بین اونا جا به‌ جا بشه اما یک سوال مهم: برنامه‌ ها چطور به سیستم‌ عامل میگن که یک Process جدید بساز یا یک برنامه رو اجرا کن؟ آیا مستقیما با…
Process API How do programs talk to the operating system
So far we have understood that the operating system can create, run, and switch between processes

But one important question:

How do programs tell the operating system to create a new process or run a program?

Do they talk directly to the kernel?

No
Programs use something called API (Application Programming Interface)

We can think of API
as an intermediary between the program and the operating system

What does API
mean?
Suppose you went to a restaurant
You didn't go directly into the kitchen to prepare the food
You would give the order to the waiter, the waiter would give it to the chef, and then they would bring the food to you

In this example:

Chef = Kernel

Waiter = API

Customer = Program

The program also sends its request to the operating system via API

What is Process API?

Process API A set of functions that allow a program to manage processes

For example:

Creating a new process

Executing a program

Waiting for a process to finish

Exiting a process

Most important functions in Linux

In Linux, you will be most familiar with these three functions:

fork()
Its function is to create a new process

When fork() is executed, the operating system makes a copy of the current process

After that, there are two processes:

Parent

Child

Both continue their execution from the same point

exec()
Sometimes we don't want to have just a copy of the process, we want another program to run

This is where exec() comes in

exec()
Replaces the current program with a new program

For example, a shell executes a program like ls or cat after receiving a user command with exec()

wait()
If the parent process wants to wait until the process The child uses wait() when it finishes its work

Without wait(), the parent might continue earlier and the execution order of the program might be disrupted

A real-world example:

Suppose you type in the terminal:
python script.py

Behind the scenes, this is what happens:

Shell
creates a new Process with fork()

Process
the child executes the Python program with exec()

Process
the parent waits with wait() until the script is finished

All of these steps are done in a fraction of a second

Why is this important?

If you analyze programs or malware on Linux, you will encounter these functions regularly. There are also similar concepts in Windows, only the names of the functions are different. For example, creating a Process in Windows is usually done with functions like

CreateProcess
, but the main idea is the same:

Requesting the operating system to create and manage a Process

Process API

The way a program communicates with the operating system to manage Processes

Three important functions:

fork() Create a new Process

exec() Run a new program

wait() Wait for a child Process to finish

If you understand these three functions well, it will be much easier for you to understand how to run programs in Linux and even similar concepts in Windows
بخش بیست و چهارم بافر اورفلو


Fuzzing
شکار باگ بدون اینکه خط به خط کد رو بخونیم


تا اینجا خودمان با تحلیل کد و اسمبلی دنبال باگ می‌گشتیم
ولی اگر برنامه چند میلیون خط کد داشته باشه چی

اینجاست که Fuzzing وارد میشه
Fuzzing
به جای اینکه ما دنبال باگ بگردیم خودش هزار بار یا حتی میلیون‌ ها ورودی مختلف به برنامه میده تا ببیند برنامه کرش میکنه یا نه
Fuzzing یعنی چی
به زبان ساده
یک ابزار به صورت خودکار ورودی‌ های مختلف تولید میکنه و به برنامه میده
اگر برنامه
کرش کنه
هنگ کنه
رفتار غیرعادی داشته باشه
ابزار اون ورودی رو ذخیره میکنه تا بعدا بررسی کنیم

یک مثال ساده:

فرض کنید برنامه فقط یک رشته از کاربر بگیره

#include <stdio.h>

int main() {

char input[64];

fgets(input,sizeof(input),stdin);

printf("%s",input);

return 0;
}


یک Fuzzer ممکنه این ورودی‌ ها رو امتحان کنه

AAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAA

123456789

!@#$%^&*

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA



چرا Fuzzing مهمه؟

چون انسان نمیتونه میلیون‌ ها ورودی رو امتحان کنه
ولی Fuzzer این کار رو در مدت کوتاهی انجام میده
به همین دلیل خیلی از آسیب‌پذیری‌ های معروف دنیا اولین بار با Fuzzing پیدا شدن

انواع Fuzzing:

Dumb Fuzzing
ساده‌ترین حالت
فقط داده‌ های تصادفی به برنامه میده
هیچ اطلاعی از ساختار برنامه نداره

Smart Fuzzing
ساختار ورودی رو میشناسه

مثلا اگر برنامه فایل PNG باز میکنه
ورودی‌هایی شبیه PNG تولید میکنه
در نتیجه شانس پیدا کردن باگ بیشتر میشه

Coverage Guided Fuzzing
این روش خیلی محبوبه
ابزار بررسی میکنه هر ورودی باعث اجرای کدوم قسمت‌ های برنامه شده
اگر ورودی جدید مسیر جدیدی از کد رو اجرا کنه
همون مسیر رو بیشتر بررسی میکنه
به همین دلیل خیلی سریع‌ تر از روش‌ های ساده باگ پیدا میکنه

ابزارهای معروف

چند ابزار معروف که تقریباً هر Reverse Engineer باید اسمشون رو بدونه

AFL++
libFuzzer
Honggfuzz


این ابزار ها سال‌ هاست برای پیدا کردن باگ‌های حافظه استفاده میشن

موقع مهندسی معکوس چرا مهمه؟

فرض کنید یک باینری دارید و هیچ سورسی از اون موجود نیست

میتونید اون رو Fuzz کنید
اگر کرش کرد
همن ورودی رو داخل GDB یا IDA بررسی کنید

و قدم به قدم علت کرش رو پیدا میکنید
به همین دلیل Fuzzing و Reverse Engineering مکمل هم هستن


Fuzzing
یعنی به جای اینکه خودتون حدس بزنید چه ورودی باعث باگ میشه یک ابزار هزار بار یا میلیون‌ها ورودی مختلف رو امتحان میکنه هر جا برنامه رفتار غیرعادی داشت
همان نقطه تبدیل به هدف تحلیل مهندسی معکوس میشه

تمرین:

یک برنامه ساده که از ورودی کاربر استفاده میکنه بنویسید بعد فکر کنید اگر قرار بود یک Fuzzer برای اون بنویسید چه نوع ورودی‌ هایی رو امتحان میکردید

@reverseengine
ReverseEngineering
بخش بیست و چهارم بافر اورفلو Fuzzing شکار باگ بدون اینکه خط به خط کد رو بخونیم تا اینجا خودمان با تحلیل کد و اسمبلی دنبال باگ می‌گشتیم ولی اگر برنامه چند میلیون خط کد داشته باشه چی اینجاست که Fuzzing وارد میشه Fuzzing به جای اینکه ما دنبال باگ بگردیم…
Part 24 Buffer Overflow


Fuzzing
Bug hunting without reading the code line by line

So far we have been looking for bugs ourselves by analyzing the code and assembly

But what if the program has several million lines of code

This is where Fuzzing comes in

Fuzzing
Instead of us looking for bugs, it gives the program thousands or even millions of different inputs to see if the program crashes or not

What does Fuzzing mean

In simple terms
A tool automatically generates different inputs and gives them to the program

If the program

Crash
Hangs

Or behaves abnormally
The tool saves that input for later review

A simple example:

Suppose the program only takes a string from the user

#include <stdio.h>

int main() {

char input[64];

fgets(input,sizeof(input),stdin);

printf("%s",input);

return 0;

}


A Fuzzer might try these inputs

AAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

123456789

!@#$%^&*

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA


Why is Fuzzing Important?

Because humans cannot try millions of inputs
But Fuzzer does this in a short time
That is why many of the world's famous vulnerabilities were first found with Fuzzing

Types of Fuzzing:

Dumb Fuzzing
The simplest
Only gives random data to the program
It has no information about the structure of the program

Smart Fuzzing
It knows the structure of the input

For example, if the program opens a PNG file
It produces inputs similar to PNG
As a result, the chances of finding a bug increase

Coverage Guided Fuzzing
This method is very popular
The tool checks which parts of the program each input causes to be executed
If the new input executes a new path of code
It checks the same path more
That is why it finds bugs much faster than simple methods

Famous tools

A few famous tools that almost every Reverse Engineer should know their names

AFL++
libFuzzer
Honggfuzz


These tools have been used for years to find Memory bugs are used

Why is it important when reverse engineering?

Suppose you have a binary and no source is available

You can fuzz it

If it crashes

Examine the same input in GDB or IDA

And you will find the cause of the crash step by step

That is why Fuzzing and Reverse Engineering are complementary

Fuzzing

Instead of guessing what input causes the bug, a tool tries thousands or millions of different inputs. Wherever the program behaves abnormally

That point becomes the target of reverse engineering analysis

Exercise:

Write a simple program that uses user input. Then think about what kind of inputs you would try if you were to write a fuzzer for it

@reverseengine
Dissecting_the_Dark_Web_Reverse_Engineering_the_Tools_of_the_Underground.pdf
21.7 MB
D I S S E C T I N G T H E
DARK WEB

R e v e r s e E n g i n e e r i n g t h e To o l s
of the Underground Economy

@reverseengine
8 countries. 8 critical sectors. One APT

Everyone talks about killing two birds with one stone
Operation Olalampo proved that Charming Kitten (Iranian APT) could hit 8 birds with one stone.

Egypt, Saudi Arabia, UAE, Turkey, Hungary, Turkmenistan, Israel, and South America.

Government, Healthcare, Financial Services, Energy, Education, Telecommunications, Defense, and Industrial.

GitHub Repository:

https://github.com/S3N4T0R-0X0/APTs-Adversary-Simulation/tree/main/Iranian%20APT/Charming%20Kitten

@reverseengine
این یکی از مهم‌ترین بخش‌ های Heap هست اگر این مفهوم رو خوب یاد بگیرید خیلی از رفتارهای malloc() و free() براتون قابل پیش‌ بینی میشه

Bin چیه و Allocator حافظه‌های آزاد
رو چطور مدیریت میکنه؟

در پست قبل گفتیم وقتی free() رو صدا میزنیم معمولا حافظه فورا به سیستم‌ عامل برنمیگرده

سوال اینجاست:

پس این حافظه کجا میره؟

اینجاست که مفهوم Bin وارد میشه

Bin یعنی چی؟

به زبان ساده Bin مثل یک انبار یا لیست انتظار برای Chunk های آزاده
وقتی یک Chunk آزاد میشه Allocator اون رو دور نمیندازه بلکه داخل یکی از Bin ها قرار میده تا اگر بعدا برنامه دوباره به حافظه‌ ای با اندازه مشابه نیاز داشت همون Chunk رو دوباره استفاده کنه

این کار باعث میشه:

سرعت malloc() بیشتر بشه
تعداد درخواست‌ ها از سیستم‌ عامل کمتر بشه

مصرف حافظه بهینه‌ تر بشه
چرا چند نوع Bin وجود داره؟
همه‌ی Chunk ها اندازه یکسانی ندارن
مثلا ممکنه یک برنامه هم 32 بایت حافظه بخاد هم 512 بایت و هم چند کیلوبایت
اگر همه‌ی Chunk ها داخل یک لیست قرار بگیرن پیدا کردن Chunk مناسب زمان زیادی میبره به همین دلیل Allocator اونا رو بر اساس اندازه دسته‌ بندی میکنه

انواع Bin در glibc

در نسخه‌های امروزی glibc معمولاً با این Binها رو به‌ رو میشیم:
🔹 Tcache
جدیدترین و سریع‌ترین بخش
هر Thread یک Tcache مخصوص خودش داره وقتی Chunkهای کوچیک آزاد میشن معمولا اول وارد Tcache میشن تا اگر دوباره همون اندازه نیاز شد خیلی سریع استفاده بشن
هدف اصلی Tcache افزایش سرعت برنامه هست

🔹 Fast Bin
برای Chunkهای کوچیک استفاده میشه
ویژگی مهم Fast Bin اینه که هنگام آزاد شدن Chunkها عملیات ادغام (Coalescing) بلافاصله انجام نمیشه
این موضوع سرعت رو بالا میبره اما مدیریت حافظه رو کمی پیچیده‌ تر میکنه


🔹 Unsorted Bin
وقتی بعضی Chunk ها آزاد میشن اول وارد Unsorted Bin میشن
بعدا Allocator تصمیم میگیره اونها رو به Bin مناسب منتقل کنه
میشه گفت Unsorted Bin یک محل موقت برای Chunk های آزاده


🔹 Small Bin
برای Chunk هایی با اندازه مشخص و نسبتا کوچیک استفاده میشه
چون اندازه‌ ها مشخصن پیدا کردن Chunk مناسب سریع انجام میشه

🔹 Large Bin
برای Chunk های بزرگ‌ تر استفاده میشه
اینجا Allocator باید دقت بیشتری داشته باشه تا بهترین Chunk رو برای درخواست جدید انتخاب کنه

چرا شناخت Bin ها مهمه؟
چون رفتار malloc() و free() کاملا به همین ساختارها وابسته ست
وقتی یک برنامه حافظه درخواست میکنه Allocator اول بررسی میکنه که آیا داخل Bin ها Chunk مناسبی وجود داره یا نه
اگر پیدا بشه همون رو برمیگردونه
اگر پیدا نشه از Heap یا در بعضی شرایط از سیستم‌ عامل حافظه جدید میگیره

آیا همیشه از سیستم‌ عامل حافظه جدید گرفته میشه؟ خیر

در بیشتر مواقع Allocator سعی میکنه از حافظه‌ هایی که قبلا آزاد شدن استفاده کنه
به همین دلیل ممکن است دو بار malloc() پشت سر هم همون آدرسی رو برگردونه که قبلا با free() آزاد شده بود این رفتار کاملا طبیعیه و برای افزایش کارایی انجام میشه


Bin
ها محل نگهداری Chunk های آزادن Allocator با استفاده از اونها تلاش میکنه بدون درخواست مداوم حافظه از سیستم‌ عامل حافظه‌ های آزاد شده رو دوباره استفاده کنه همین طراحی باعث شده Heap هم سریع‌ تر باشد و هم پیچیده‌ تر از Stack

@reverseengine
ReverseEngineering
این یکی از مهم‌ترین بخش‌ های Heap هست اگر این مفهوم رو خوب یاد بگیرید خیلی از رفتارهای malloc() و free() براتون قابل پیش‌ بینی میشه Bin چیه و Allocator حافظه‌های آزاد رو چطور مدیریت میکنه؟ در پست قبل گفتیم وقتی free() رو صدا میزنیم معمولا حافظه فورا به…
This is one of the most important parts of the Heap. If you learn this concept well, many of the behaviors of malloc() and free() will be predictable for you.

What is Bin and how does Allocator manage freed memory?

In the previous post, we said that when we call free(), the memory is usually not returned to the operating system immediately.

The question is:

So where does this memory go?

This is where the concept of Bin comes in.

What does Bin mean?

In simple terms, Bin is like a warehouse or waiting list for freed Chunks.

When a Chunk is freed, the Allocator does not throw it away, but places it in one of the Bins so that if the program needs memory of the same size again later, it can reuse the same Chunk.

This will:

Increase the speed of malloc().

Reduce the number of requests from the operating system.

Optimize memory usage.

Why are there several types of Bins?

Not all chunks are the same size
For example, a program may need 32 bytes of memory, 512 bytes, or a few kilobytes
If all the chunks are in a list, finding the right chunk will take a long time, so the Allocator sorts them by size

Types of Bins in glibc

In today's versions of glibc, we usually encounter these bins:

🔹 Tcache
The newest and fastest part
Each thread has its own Tcache. When small chunks are freed, they usually enter Tcache first so that if the same size is needed again, they can be used very quickly
The main purpose of Tcache is to increase the speed of the program

🔹 Fast Bin
It is used for small chunks
The important feature of Fast Bin is that when the chunks are freed, the coalescing operation is not performed immediately
This increases speed, but makes memory management a little more complicated

🔹 Unsorted Bin
When some chunks are freed, they enter Tcache first Unsorted Bin
Later, the Allocator decides to move them to the appropriate Bin
You can say that the Unsorted Bin is a temporary place for free Chunks

🔹 Small Bin
Used for Chunks of a specific and relatively small size
Since the sizes are specific, finding the appropriate Chunk is done quickly

🔹 Large Bin
Used for larger Chunks
Here the Allocator must be more careful to choose the best Chunk for the new request

Why is it important to know the Bins?

Because the behavior of malloc() and free() is completely dependent on these structures
When a program requests memory, the Allocator first checks whether there is a suitable Chunk in the Bins or not
If it is found, it returns it
If not found, it gets new memory from the Heap or in some cases from the operating system

Is new memory always taken from the operating system? No

In most cases, the Allocator tries to use previously freed memory

That is why malloc() may return the same address twice in a row that was previously freed with free(). This behavior is completely normal and is done to increase performance

Bins are where freed chunks are stored. The Allocator uses them to try to reuse freed memory without constantly requesting memory from the operating system. This design makes the Heap both faster and more complex than the Stack

@reverseengine