ar
Feedback
ReverseEngineering

ReverseEngineering

الذهاب إلى القناة على Telegram
1 265
المشتركون
لا توجد بيانات24 ساعات
-37 أيام
+730 أيام
أرشيف المشاركات
Enumerating Windows Clipboard History in PowerShell https://devblogs.microsoft.com/oldnewthing/20230303-00/?p=107894 @reverseengine

مثال تشخیص 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

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

بخش نهم بافر اورفلو
پیدا کردن دقیق نقطه کرش تا الان فهمیدیم برنامه کجا کرش میکنه ولی نمیدونیم دقیقا کدوم بخش از ورودی ما باعث میشه 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 9 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

Repost from Fuzzing ZONE

Dump Steal and Decrypt Google Chrome Passwords (Windows) https://github.com/thatskriptkid/ChromePass @reverseengine

🔥 آرایه روی استک یکی از مهم ترین پایه های ریورس چرا آرایه روی استک مهمه؟ چون در باینری‌ ها همیشه اینا رو میبینید:
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

قسمت نهم بافر اورفلو محافظ‌ها و راه‌های مقابله با بافر اورفلو تو این قسمت میخوایم یاد بگیریم وقتی میخواید بافر اورفلو بزنید چه چیزایی جلوتونو میگیرن چطور بفهمیم فعالن و چرا اصلا کار رو سخت میکنن این بخش خیلی مهمه چون بدون درک همین‌ ها اصلا exploit نوشتن معنی نداره Stack Canary یه مقدار رندوم میاد بین بافر و return address قرار میگیره کاری که میکنه اینه هرچی رو استک خراب بشه اول این مقدار تغییر میکنه وقتی تابع میخواد برگرده چک میکنه کاناری خراب شده یا نه خراب شده = برنامه میپره بیرون و نمیذاره به return address دست بزنید چطور بفهمیم کاناری فعاله فقط با کامپایل کردن برنامه میفهمید
gcc -fstack-protector-all file.c -o out
یا موقع آنالیز باینری checksec ./out اگر نوشت Canary: Yes یعنی فعاله NX یا DEP NX یعنی Memory eXecute نشه یعنی استک و هیپ فقط برای داده‌ ن نه برای اجرای کد قبلا shellcode رو میذاشتید تو استک و مستقیم اجرا می‌کردید الان با NX دیگه نمیذاره چطور بفهمیم NX فعاله checksec ./out اگر نوشت NX: enabled یعنی این مسیر بسته است یعنی نمیشه shellcode رو مستقیم اجرا کرد و باید برید سمت ret2libc یا ROP ASLR Random کردن آدرس‌ذهای حافظه libc هر بار میره یه جای جدید heap یه جای جدید استک یه جای جدید این باعث میشه exploit ثابت ننویسید چون آدرس win یا system یا /bin/sh هر بار عوض میشه چطور بفهمیم ASLR فعاله cat /proc/sys/kernel/randomize_va_space اگر 2 بود یعنی کاملا فعاله PIE Position Independent Executable یعنی خود باینری هم هر بار تو یه جای جدید لود میشه بدتر از ASLR چون دیگه حتی آدرس تابع‌ های داخل خود برنامه هم ثابت نیست چطور بفهمیم PIE فعاله checksec ./out اگر نوشت PIE: enabled یعنی برنامه آدرس ثابت نداره CF Control Flow Integrity این یکی خیلی جدید تره واقعا میگه هرجا خواستید بپرید باید از یه لیست معتبر باشه غیر از اون اجازه نمیده خیلی سخت‌تره دور زدنش و تو real-world معمولا تو مرورگرها و کرنل استفاده میشه Part 9 Buffer Overflow Protectors and ways to deal with buffer overflow In this part, we want to learn what things stop you when you want to overflow a buffer How to know if they are active And why they make it difficult at all This part is very important because without understanding these, there is no point in writing an exploit Stack Canary A random value is placed between the buffer and the return address What it does is that whenever the stack is corrupted, this value changes first When the function wants to return, it checks if the canary is corrupted or not Corrupted = the program exits and does not let you touch the return address How to know if the canary is active You can only find out by compiling the program
gcc -fstack-protector-all file.c -o out
Or during binary analysis checksec ./out If it says Canary: Yes means active NX or DEP NX means Memory eXecute Not possible That means the stack and heap are only for data, not for execution Code Previously you put the shellcode on the stack and executed it directly Now it doesn't work with NX How to tell if NX is enabled checksec ./out If it says NX: enabled, this path is closed That means you can't execute the shellcode directly and you have to go to ret2libc or ROP ASLR Randomizing memory addresses libc goes to a new location every time heap to a new location stack to a new location This prevents you from writing a fixed exploit Because the address of win or system or /bin/sh changes every time How to tell if ASLR is enabled cat /proc/sys/kernel/randomize_va_space If it was 2, it means it's fully enabled PIE Position Independent Executable That means the binary itself is loaded in a new location every time Worse than ASLR Because even the addresses of the functions inside the program itself are no longer fixed How to tell if PIE is enabled checksec ./out If it says PIE: enabled, it means The program does not have a fixed address CF Control Flow Integrity This one is much newer It actually says that wherever you want to jump, it must be from a valid list It does not allow anything else It is much harder to bypass and in the real-world it is usually used in browsers and kernels @reverseengine

خوندن آرگومان‌ ها از استک وقتی زیاد باشن در x86-64 SysV ABI رجیستر های آرگومان‌ ها به ترتیب:
1RDI 2 RSI 3 RDX 4 RCX 5 R8 6 R9
اما اگر تعداد آرگومان‌ ها بیشتر از 6 تا بشه بقیه روی استک قرار میگیره تابع 8 آرگومانی کد C:
int f(int a, int b, int c, int d, int e, int f, int g, int h) { return h + a; }
ارگومان‌ها: محل آرگومان
a RDI b RSI c RDX d RCX e R8 f R9 g [rbp+16] h [rbp+24]
فریم استک:
[rbp+16] → 7th argument [rbp+24] → 8th argument
اسمبلی:
f: push rbp mov rbp, rsp mov eax, DWORD PTR [rbp+24] ; h add eax, edi ; + a pop rbp ret
یعنی: h از روی استک خونده میشه a در رجیستر edi هست خروجی در eax Reading arguments from the stack when there are many In x86-64 SysV ABI The argument registers are in order:
1RDI 2 RSI 3 RDX 4 RCX 5 R8 6 R9
But if the number of arguments is more than 6, the rest are placed on the stack 8-argument function C code:
int f(int a, int b, int c, int d, int e, int f, int g, int h) { return h + a; }
Arguments: Argument location
a RDI b RSI c RDX d RCX e R8 f R9 g [rbp+16] h [rbp+24]
Stack frame:
[rbp+16] → 7th argument [rbp+24] → 8th argument
Assembly:
f: push rbp mov rbp, rsp mov eax, DWORD PTR [rbp+24] ; h add eax, edi ; + a pop rbp ret
That is: h is read from the stack a is in the edi register output in eax @reverseengine

چرا گجت‌ یک دفعه کرش میکنن؟ خیلی وقتا ROP Chain رو درست می‌نویسید آفست‌ها هم درسته ولی برنامه بعد از یکی‌ دو گجت کرش میکنه 90 درصد مواقع دلیلش اینه: دارید ROP رو داخل Red Zone میذارید یعنی شما فکر میکنی RSP اینجاست ولی داده‌ هایی که تزریق کردید تو 160 بایت پایین‌ تره جایی که اصلا تضمین نشده سالم بمونه یه تابع داخلی صدا زده می‌شه → Red Zone رو overwrite میکنه → ROP میره هوا یه interrupt میاد → سیستم‌ عامل اون فضا رو میگیره کرش میکنه حتی یه printf ساده → Red Zone رو زیر پا میذاره → گجت بعدی اجرا نمیشه ساده‌ترین نمونه کرش در ROP برنامه بعد از اجرای اولین گجت روی گجت دوم سیگنال میگیره و از بین میره چون payload داخل Red Zone بوده و libc اون محدوده رو با داده‌های خودش پر کرده Why do gadgets crash all at once? Many times you write the ROP Chain correctly, the offsets are correct, but the program crashes after one or two gadgets 90% of the time the reason is this: You are putting the ROP in the Red Zone That is, you think the RSP is here, but the data you injected is 160 bytes below where it is not guaranteed to be intact An internal function is called → It overwrites the Red Zone → ROP goes to the air An interrupt comes → The operating system takes that space and crashes Even a simple printf → It violates the Red Zone → The next gadget does not run The simplest example of a crash in ROP After executing the first gadget, the program receives a signal on the second gadget and dies Because the payload was in the Red Zone and libc filled that area with its own data @reverseengine

چرا Exploit Dev به Red Zone حساسه؟ اگر روی استک payload میزارید shellcode/ROP ممکنه داخل Red Zone باشه کامپایلر RSP رو تغییر نداده ولی داده‌ای که تزریق کرد داخل همون محدوده است پس: اگر تابع دیگه ای صدا زده بشه داده پاک میشه اگر interrupt بشه کرش میکنه چند تا payloadها با stomp شدن Red Zone از کار میوفتن خیلی از فراخوانی‌ های داخلی libc اولین کاری که میکنن اینه که از Red Zone استفاده بکنن اگر ROP Chain شما داخل این محدوده باشه overwrite میشه کرش Stack Alignment وابسته به Red Zone اگر اشتباهی فکر کنید فضای زیر RSP امن نیست ممکنه SUB/ADD اشتباه بزنی و استک misalign میشه Misalignment: سیستم‌ کال کرش کنه libc کرش کنه ROP Chain درست اجرا نشه مثال عملی کد C
#include <stdio.h> void test() { char *p = (char *)__builtin_frame_address(0); printf("RSP: %p\n", p); char buffer[32]; buffer[0] = 'A'; printf("buffer: %p\n", buffer); } int main() { test(); return 0; }
آدرس buffer پایین RSP است اما RSP کم نشده! این یعنی buffer داخل Red Zone هست نکته طلایی: کامپایلر حتی بدون رزرو استک در Red Zone متغیر میزاره Why is Exploit Dev sensitive to Red Zone? If you put the shellcode/ROP payload on the stack, it may be in the Red Zone The compiler did not change the RSP, but the data it injected is in the same range So: If another function is called, the data will be cleared If it is interrupted, it will crash Some payloads will fail when the Red Zone is stomped Many internal libc calls do the first thing they do is use the Red Zone If your ROP Chain is in this range, it will be overwritten and crash Stack Alignment Dependent on the Red Zone If you mistakenly think that the space below the RSP is not safe, you may make a SUB/ADD mistake and the stack will be misaligned Misalignment: System call crashes libc crashes ROP Chain does not execute correctly Practical example C code
#include <stdio.h> void test() { char *p = (char *)__builtin_frame_address(0); printf("RSP: %p\n", p); char buffer[32]; buffer[0] = 'A'; printf("buffer: %p\n", buffer); } int main() { test(); return 0; }
The buffer address is below RSP But RSP is not decreased! This means the buffer is in the Red Zone Golden tip: The compiler places variables in the Red Zone even without stack reservation @reverseengine

Local Variables روی استک وقتی تابع متغیر محلی داره کامپایلر اون رو روی استک میزاره ساختار فریم استک کامل بعد از اجرای prologue:
push rbp mov rbp, rsp sub rsp, X ; ایجاد فضا برای متغیرهای محلی
ساختار میشه:
[ rbp+16 ] آرگومان سوم [ rbp+8 ] آدرس برگشت [ rbp+0 ] RBP قبلی [ rbp-8 ] متغیر محلی 1 [ rbp-16 ] متغیر محلی 2
مثال:
int foo(int x) { int t = x + 3; return t * 2; }
اسمبلی:
foo: push rbp mov rbp, rsp sub rsp, 16 ; فضای متغیر محلی mov DWORD PTR [rbp-4], edi ; t = x add DWORD PTR [rbp-4], 3 ; t = x + 3 mov eax, DWORD PTR [rbp-4] add eax, eax ; eax = t * 2 leave ret
نکات مهم: متغیر t روی استک قرار میگیره آدرسش rbp-4 چون int هست سایز 4 بایت eax خروجی تابع Local Variables on the Stack When a function has a local variable, the compiler pushes it onto the stack Complete stack frame structure After executing the prologue:
push rbp mov rbp, rsp sub rsp, X ; Create space for local variables
The structure becomes:
[ rbp+16 ] Third argument [ rbp+8 ] Return address [ rbp+0 ] Previous RBP [ rbp-8 ] Local variable 1 [ rbp-16 ] Local variable 2
Example:
int foo(int x) { int t = x + 3; return t * 2; }
Assembly:
foo: push rbp mov rbp, rsp sub rsp, 16 ; Local variable space mov DWORD PTR [rbp-4], edi ; t = x add DWORD PTR [rbp-4], 3 ; t = x + 3 mov eax, DWORD PTR [rbp-4] add eax, eax ; eax = t * 2 leave ret
Important notes: The variable t is placed on the stack, its address is rbp-4 Because it is an int, its size is 4 bytes eax is the output of the function @reverseengine

Android App Reverse Engineering https://youtu.be/BijZmutY0CQ @reverseengine