ReverseEngineering – Telegram
ReverseEngineering
1.24K subscribers
40 photos
10 videos
55 files
666 links
Download Telegram
🔥 آرایه روی استک یکی از مهم ترین پایه های ریورس


چرا آرایه روی استک مهمه؟

چون در باینری‌ ها همیشه اینا رو میبینید:

buff[64]

arr[10]

int nums[5]

char name[32]


باید بفهمید:

چطور آدرس دهی میشن

چطور روی استک قرار میگیرن

چطور از طریق rbp-XX بهشون دسترسی داده میشه

چطور حلقه‌ ها روی آرایه اجرا میشن


ساختار آرایه روی استک

کد C:

void f() {
int arr[4];
}


نوع int = چهار بایت
4 تا int → می‌شه 16 بایت

در اسمبلی:

push rbp
mov rbp, rsp
sub rsp, 16 ; arr[4] = 16 bytes


محل ذخیره آرایه:

rbp-4 → arr[0]
rbp-8 → arr[1]
rbp-12 → arr[2]
rbp-16 → arr[3]


نوشتن مقدار ها داخل آرایه

کد C:

void f() {
int arr[4];
arr[2] = 7;
}


اسمبلی:

push rbp
mov rbp, rsp
sub rsp, 16

mov DWORD PTR [rbp-12], 7 ; arr[2]


چرا rbp-12
چون:

arr[0] → rbp-4
arr[1] → rbp-8
arr[2] → rbp-12
arr[3] → rbp-16


هر int چهار بایته = جمع هر ایندکس چهار تا چهار تا جلو میره


🔥 Arrays on the Stack

Arrays on the stack are one of the most important foundations of reverse

Why are arrays on the stack important?

Because in binaries you always see these:

buff[64]

arr[10]

int nums[5]

char name[32]


You need to understand:

How are they addressed

How are they placed on the stack

How are they accessed via rbp-XX

How are loops executed on arrays

Array structure on the stack

C code:

void f() {
int arr[4];
}


Type int = four bytes

4 to int → becomes 16 bytes

In assembly:

push rbp
mov rbp, rsp
sub rsp, 16 ; arr[4] = 16 bytes


Array storage location:

rbp-4 → arr[0]
rbp-8 → arr[1]
rbp-12 → arr[2]
rbp-16 → arr[3]


Writing values ​​into an array

C code:

void f() {
int arr[4];
arr[2] = 7;
}


Assembly:

push rbp
mov rbp, rsp
sub rsp, 16

mov DWORD PTR [rbp-12], 7 ; arr[2]

Why rbp-12
Why:

arr[0] → rbp-4
arr[1] → rbp-8
arr[2] → rbp-12
arr[3] → rbp-16


Each int is four bytes = the sum of each index goes forward by four

@reverseengine
1
بخش دهم بافر اورفلو


پیدا کردن دقیق نقطه کرش

تا الان فهمیدیم برنامه کجا کرش میکنه ولی نمیدونیم دقیقا کدوم بخش از ورودی ما باعث میشه RIP/ EIP overwrite بشه
الان میخوایم با یک روش دقیق بفهمیم که مثلا بعد از چند کاراکتر مقدار ریجستر EIP/RIP توسط ورودی ما کنترل میشه
این مرحله خیلی مهمه چون بدونش نمیتونیم شل‌کد اجرا کنیم


ساختن یک الگوی خاص برای تشخیص آفست

به جای اینکه هزار تا "A" پشت هم بفرستیم یک رشته Unique میسازیم
اینکار باعث میشه وقتی برنامه کرش کرد از روی مقدار رجیستر بفهمیم که آفست دقیق چنده

Python

from pwn import *

pattern = cyclic(300)
print(pattern)
این 300 تا کاراکتر الگوی خاص میده که هیچ تکراری توش نیست




اجرای برنامه با این الگو

الگو رو می‌ریزیم داخل برنامه:

./vuln $(python3 pattern.py)


برنامه کرش میکنه
حالا مقدار RIP/EIP رو چک میکنیم:

gdb:

(gdb) run <<< $(python3 pattern.py)
(gdb) info registers


مقدار RIP:

0x6161616c




پیدا کردن آفست دقیق

حالا این مقدار رو به ابزار سایکلیک میدیم تا بهمون بگه دقیقا چندمین کاراکتر بوده:

cyclic_find(0x6161616c)


خروجی:

112

یعنی دقیقا بعد از 112 کاراکتر ما میتونیم رجیستر رو کنترل کنیم



Part 10 Buffer Overflow



Finding the Exact Offset

So far we have understood where the program crashes, but we do not know exactly which part of our input causes the RIP/EIP to be overwritten

Now we want to find out in an exact way, for example, after how many characters the value of the EIP/RIP register is controlled by our input

This step is very important because without it we cannot run the shellcode

Creating a special pattern to detect the offset

Instead of sending a thousand "A"s in a row, we create a Unique string

This will allow us to find out the exact offset from the register value when the program crashes

Python

from pwn import *

pattern = cyclic(300)
print(pattern)

These 300 characters give a special pattern that has no repetitions

Executing the program with this pattern

We put the pattern into the program:

./vuln $(python3 pattern.py)


The program crashes
Now the value of the RIP/EIP We check:

gdb:

(gdb) run <<< $(python3 pattern.py)
(gdb) info registers


RIP value:

0x6161616c


Finding the exact offset

Now we give this value to the cyclic tool to tell us exactly what character it was:

cyclic_find(0x6161616c)


Output:

112

That is, exactly after 112 characters we can check the register

@reverseengine
5
Calling Convention

توضیح:
قانونیه که میگه آرگومان‌ ها چطور به تابع داده میشن و نتیجه چطور برمیگرده

وقتی دارید مهندسی معکوس میکنی اگه Convention رو نشناسید اصلا نمیفهمید تابع چی از کجا ورودی میگیره و چی رو تغییر میده



سه Convention مهم برای مهندسی معکوس

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


System V AMD64 برای لینوکس و مک

شش آرگومان اول داخل رجیسترها:

RDI, RSI, RDX, RCX, R8, R9


بقیه آرگومان‌ها روی استک

return value → RAX



اگه کد لینوکسه نگاهتون اول باید بره سراغ RDI و RSI %90 آرگومان‌ ها اونجاست




Windows x64 Convention

چهار آرگومان اول در رجیستر ها:

RCX, RDX, R8, R9

بقیه آرگومان‌ها روی استک

return RAX



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


Stack-based (cdecl / stdcall / fastcall قدیمی‌ تر

اینا تو x86 زیاد بودن تو x64 کمتر
ولی هنوز هستت

همه آرگومان‌ ها روی استک

return
در RAX یا EAX

Caller یا Callee
استک رو تمیز میکنه بسته به نوع



اگر دیدید قبل از call کلی push push push انجام شد تقریبا یعنی Convention قدیمیه



چطور توی دیس‌اسمبلی Convention رو تشخیص بدیم (Detect)


رجیستر هایی که قبل از call مقدار میگیرن

RDI/RSI
قطعا System V

RCX/RDX
تقریبا همیشه Windows x64


push
پشت هم احتمالا stack-based

prologue


بعضی compilerها قبل از شروع تابع shadow space میذارن فقط تو ویندوز

sub rsp, 0x20


یعنی:

اینجا ویندوزه Convention: Windows x64



Calling Convention

Explanation:
A rule that says how arguments are given to a function and how the result is returned

When you are reverse engineering, if you do not know the Convention, you will not understand at all what the function takes input from, where it comes from, and what it changes

Three important conventions for reverse engineering

You only need to master these three, the rest are either old or rarely used

System V AMD64 for Linux and Mac

The first six arguments are in the registers:

RDI, RSI, RDX, RCX, R8, R9


The rest of the arguments are on the stack

return value → RAX

If it is Linux code, your first look should be at RDI and RSI, 90% of the arguments are there

Windows x64 Convention

The first four arguments are in the registers:

RCX, RDX, R8, R9


The rest of the arguments are on the stack

return RAX

Windows only provides four registers The rest goes to the stack

Stack-based (older cdecl / stdcall / fastcall

This was more in x86, less in x64

But it's still there

All arguments on the stack

return
in RAX or EAX

Caller or Callee
Cleans the stack depending on the type

If you see a whole push push push done before the call, it almost means the Convention is old

How to detect the Convention in disassembly

Registers that get values ​​before the call

RDI/RSI
Definitely System V

RCX/RDX
Almost always Windows x64

push
Back to back probably stack-based

prologue


Some compilers put a shadow space before the function starts, only in Windows

sub rsp, 0x20


Meaning:

Here is Windows Convention: Windows x64

@reverseengine
1
ReverseEngineering
Calling Convention توضیح: قانونیه که میگه آرگومان‌ ها چطور به تابع داده میشن و نتیجه چطور برمیگرده وقتی دارید مهندسی معکوس میکنی اگه Convention رو نشناسید اصلا نمیفهمید تابع چی از کجا ورودی میگیره و چی رو تغییر میده سه Convention مهم برای مهندسی معکوس…
مثال تشخیص Calling Convention

دیس‌اسمبلی:

mov rdi, rax
mov rsi, rbx
call func


این خط داریم دو آرگومان به func میدیم چون RDI/RSI هست پس کد لینوکسه و
Convention = System V


Example of Calling Convention Detection

Disassembly:

mov rdi, rax
mov rsi, rbx
call func


In this line, we are giving two arguments to func because it is RDI/RSI, so it is Linux code and Convention = System V

@reverseengine
1
vmp-3.5.1.zip
20.2 MB
VMProtect Source Code (Leaked 07.12.2023)

intel.cc and processors.cc included


mirror:
https://github.com/jmpoep/vmprotect-3.5.1

@reverseengine
1