A Year of Hacking with LLMs
https://sites.google.com/site/zhiniangpeng/blogs/Hacking-with-LLMs-Eng
@reverseengine
https://sites.google.com/site/zhiniangpeng/blogs/Hacking-with-LLMs-Eng
@reverseengine
Google
A Year of Hacking with LLMs
This blog is a summary of my talk at the Offbyone 2026 cybersecurity conference. It records some of my thoughts as a cybersecurity researcher after spending a year using LLMs for research.
Slides: https://github.com/edwardzpeng/presentations/tree/main/offbyone%202026…
Slides: https://github.com/edwardzpeng/presentations/tree/main/offbyone%202026…
Flicker and fall: rooting the Philips Hue Bridge both remotely and wirelessly
https://blog.thalium.re/posts/rooting-the-philips-hue-bridge-remotely-and-wirelessly
@reverseengine
https://blog.thalium.re/posts/rooting-the-philips-hue-bridge-remotely-and-wirelessly
@reverseengine
THALIUM
Flicker and fall: rooting the Philips Hue Bridge both remotely and wirelessly
The Philips Hue Bridge is the control center of the Hue lighting system. As part of Pwn2Own Ireland 2025, we identified several bugs which allowed fully compromising the bridge locally or remotely, including a vulnerability chain in the HomeKit component…
Silverseal is a Linux framework containing a bootkit, rootkit loader and a rootkit
https://github.com/Idov31/Silverseal
@reverseengine
https://github.com/Idov31/Silverseal
@reverseengine
GitHub
GitHub - Idov31/Silverseal: Silverseal is a Linux framework containing a bootkit, rootkit loader and a rootkit
Silverseal is a Linux framework containing a bootkit, rootkit loader and a rootkit - Idov31/Silverseal
Golang loader that uses vulnerable drivers to terminate EDR and antivirus processes before dropping Formbook
https://youtu.be/Wz6ROkJ3AZg?si=cO8uan3vRo7NTZZs
https://youtu.be/Wz6ROkJ3AZg?si=cO8uan3vRo7NTZZs
YouTube
Golang BYOVD Malware Loader and Vulnerable Driver Analysis (Stream - 08/09/2026)
In this stream we analyze a Golang loader that drops and exploits drivers (also known as Bring Your Own Vulnerable Driver or BYOVD) to terminate antivirus and EDR processes prior to downloading and executing a Formbook payload. We also analyze the drivers…
Bhatia_Sumit,_Gabhane_Chetan_Reverse_Engineering_with_Terraform.pdf
3.9 MB
Reverse Engineering with Terraform
Packt.Mobile.App.Reverse.Engineering.pdf
17.3 MB
Mobile App Reverse Engineering: Get started with discovering, analyzing, and exploring the internals of Android and iOS apps
@reverseengine
@reverseengine
Working, tested code for every chapter of the book The Art of Exploit Development: Vulnerability Research and Exploitation on Today's Hardened Systems.
https://github.com/KazamaDono/taoxd
https://github.com/KazamaDono/taoxd
GitHub
GitHub - KazamaDono/taoxd: The Art of Exploit Development companion code.
The Art of Exploit Development companion code. Contribute to KazamaDono/taoxd development by creating an account on GitHub.
Windows Exploitation Techniques: Dangling COM Object Registrations
https://projectzero.google/2026/09/windows-dangling-com.html
https://projectzero.google/2026/09/windows-dangling-com.html
projectzero.google
Windows Exploitation Techniques: Dangling COM Object Registrations
This short blog post is about abusing a privilege escalation bug that Microsoft recently fixed in...
BigDiskBuste
Windows Defender Update Denial of Service Vulnerability.
https://github.com/MSNightmare/BigDiskBuster
Windows Defender Update Denial of Service Vulnerability.
https://github.com/MSNightmare/BigDiskBuster
GitHub
GitHub - MSNightmare/BigDiskBuster: Windows Defender Update Denial of Service Vulnerability
Windows Defender Update Denial of Service Vulnerability - MSNightmare/BigDiskBuster
قسمت سی و دوم بافر اورفلو
Program Slicing
فقط کدهایی که واقعا به درد تحلیل میخورن
یعنی یک داده رو از نقطه ورود تا محل استفاده دنبال کردیم
حالا فرض کنید برنامه چند هزار دستور داره
قرار نیست همه رو خط به خط بخونیم
Program Slicing
کمک میکنه فقط قسمت هایی از برنامه رو که روی یک مقدار مشخص تأثیر دارن جدا کنیم
Program Slicing
فرض کنید یک مقدار داریم
user_input
و میخوایم بدونیم چه قسمتهایی از برنامه روی این مقدار تأثیر میذارن
مثلا:
Input
↓
Function A
↓
Function B
↓
Function C
↓
Crash
به جای بررسی کل برنامه
فقط همین مسیر رو بررسی میکنیم
دو نوع اصلی
Forward Slice
از یک مقدار شروع میکنیم و جلو میریم
سؤال اصلی
این مقدار کجا استفاده میشه
مثلا:
input
↓
buffer
↓
memcpy
↓
target
Backward Slice
از یک نقطه مهم شروع میکنیم و به عقب برمیگردیم
مثلا Crash داریم
Crash
↑
memcpy
↑
buffer
↑
input
سوال اصلی
این مقدار از کجا اومده
یک مثال ساده:
فرض کنید کد اینه
C
int process(int input)
{
int a = input;
int b = a + 10;
int c = b * 2;
int unrelated = 500;
return c;
}
اگر هدف ما مقدار
c باشهلازم نیست
unrelated رو بررسی کنیمچون روی
c تاثیری ندارهSlice
تقریبا این بخشه
input
↓
a
↓
b
↓
c
در Reverse Engineering چرا مهمه؟
فرض کنید یک Crash دارید
و Stack Trace به یک تابع بزرگ میرسه
مثلا:
process_packet()
این تابع ممکنه صدها خط کد داشته باشه
ولی Crash فقط به یک مقدار خاص وابسته باشه
مثلا:
length
حالا به جای بررسی کل تابع
مسیر
length رو دنبال میکنیمlength
↓
check
↓
calculation
↓
buffer operation
↓
Crash
وابستگی دادهای
مثلا:
C
int size = input_length;
int copy_size = size + 8;
memcpy(buffer, input, copy_size);
اینجا
copy_size به size وابسته استو
size هم به input_lengthپس مسیر داده اینه
input_length
↓
size
↓
copy_size
↓
memcpy
این دقیقا چیزی هست که موقع Slicing میخوایم پیدا کنیم
وابستگی کنترلی هم مهمه
گاهی مقدار مستقیما منتقل نمیشه ولی روی تصمیم برنامه تاثیر میذاره
مثلا:
C++
if (size > 100)
{
process_large_input();
}
اینجا
size تعیین میکنه کدوم مسیر اجرا بشهپس علاوه بر Data Dependency
یک Control Dependency هم داریم
در Ghidra یا IDA
وقتی یک متغیر یا Register مهم پیدا کردید
میتونی References و مسیرهای
استفاده از اون رو بررسی کنید
مثلا:
Variable
↓
References
↓
Functions
↓
Instructions
بعد کم کم Slice موردنظر رو تشکیل میدید
در ابزارهای پیشرفتهتر هم میشه این کار رو به شکل خودکارتر انجام داد
تفاوت Data Flow و Slicing
خیلی ساده
Data Flow
میگه داده چطور حرکت میکنه
Program Slicing
میگه برای تحلیل یک مقدار خاص دقیقا کدوم قسمتهای برنامه مهم هستن
پس
Data Flow
↓
مسیر حرکت داده
Program Slicing
↓
بخش مرتبط با یک داده یا نتیجه خاص
وقتی با یک باینری بزرگ روبهرو شدیم
لازم نیست کل برنامه رو بررسی کنیم
میتونیم یک مقدار مهم مثل
Input
Length
Pointer
Crash Value
رو انتخاب کنیم
بعد مسیر وابستگیهای اون رو پیدا کنیم
در نتیجه حجم زیادی از کد که ارتباطی با مسئله ما نداره کنار گذاشته میشه
و تحلیل خیلی سریعتر میشه
@reverseengine
ReverseEngineering
قسمت سی و دوم بافر اورفلو Program Slicing فقط کدهایی که واقعا به درد تحلیل میخورن یعنی یک داده رو از نقطه ورود تا محل استفاده دنبال کردیم حالا فرض کنید برنامه چند هزار دستور داره قرار نیست همه رو خط به خط بخونیم Program Slicing کمک میکنه فقط قسمت هایی…
Part 32 Buffer Overflow
Program Slicing
Focusing only on the code relevant to the analysis.
It involves tracing a piece of data from its entry point to where it is used.
Imagine a program with thousands of instructions;
we don't need to read through every single line.
Program Slicing
helps isolate only those parts of the program that affect a specific value.
Program Slicing
Suppose we have a value:
user_input
and we want to know which parts of the program affect this value.
For example:
Input
↓
Function A
↓
Function B
↓
Function C
↓
Crash
Instead of examining the entire program,
we examine only this specific path.
Two main types:
Forward Slice
We start with a value and move forward.
The key question:
Where is this value used?
For example:
input
↓
buffer
↓
memcpy
↓
target
Backward Slice
We start from a critical point and trace back.
For example, we have a crash:
Crash
↑
memcpy
↑
buffer
↑
input
The key question:
Where did this value come from?
A simple example:
Suppose the code is:
C
int process(int input)
{
int a = input;
int b = a + 10;
int c = b * 2;
int unrelated = 500;
return c;
}
If our goal is the value of
c,we don't need to examine
unrelated,because it has no effect on
c.The slice
consists roughly of this part:
input
↓
a
↓
b
↓
c
Why is it important in Reverse Engineering? Suppose you have a crash,
and the stack trace points to a large function—
for example:
process_packet()
This function might contain hundreds of lines of code,
yet the crash depends on only one specific value—
for instance:
length
Now, instead of examining the entire function,
we trace the path of
length:length
↓
check
↓
calculation
↓
buffer operation
↓
Crash
Data dependency
For example:
C
int size = input_length;
int copy_size = size + 8;
memcpy(buffer, input, copy_size);
Here,
copy_size depends on size,and
size depends on input_length;so, the data path is:
input_length
↓
size
↓
copy_size
↓
memcpy
This is precisely what we aim to identify during slicing.
Control dependency is also important;
sometimes a value isn't passed directly but influences a program decision—
for example:
C++
if (size > 100)
{
process_large_input(); }
Here, the
size determines which execution path is taken;so, in addition to Data Dependency,
we also have a Control Dependency.
In tools like Ghidra or IDA,
once you identify a significant variable or register,
you can examine its references and
usage paths:
For example:
Variable
↓
References
↓
Functions
↓
Instructions
You then gradually construct the desired slice;
in more advanced tools, this process can be automated.
Difference between Data Flow and Slicing
Simply put:
Data Flow
describes how data moves.
Program Slicing
identifies exactly which parts of the program are relevant
to analyzing a specific value.
So:
Data Flow
↓
Data movement path
Program Slicing
↓
The portion of the code related to a specific piece of data or result
When dealing with a large binary,
there is no need to examine the entire program;
we can select a key value—such as
Input
Length
Pointer
Crash Value
and trace its dependency paths.
Consequently, large sections of code unrelated to our specific problem are excluded,
making the analysis much faster.
@reverseengine
Taint Analysis
روی Memory و Pointer ها
تا اینجا Taint Analysis رو روی متغیرهای ساده انجام دادیم
ولی توی برنامه واقعی داستان یکم پیچیده تر میشه چون داده فقط بین چند تا متغیر جابه جا نمیشه
ممکنه وارد Memory بشه
داخل یه Buffer قرار بگیره
از طریق Pointer جابه جا بشه
و بعد چند تا تابع مختلف از همون Memory استفاده کنن
اینجاست که Taint Analysis یکم سخت تر میشه
فرض کنید اینو داریم
C
char buffer[32];
buffer[0] = input;
اینجا داده ای که از input اومده وارد Memory شده
پس میتونیم بگیم
input
↓
buffer[0]
حالا فرض کنید یه Pointer به این Buffer داریم
C
char *p = buffer;
بعد هم
char x = p[0];
اینجا داده دوباره از Memory خونده شده
پس مسیرمون میشه
input
↓
buffer[0]
↓
p[0]
↓
x
پس Taint فقط بین Register و Variable حرکت نمیکنه
ممکنه مسیرش از چند مرحله مختلف رد بشه
Register
↓
Memory
↓
Pointer
↓
Memory
↓
Register
این موضوع توی Reverse Engineering خیلی مهمه
چون وقتی Assembly رو نگاه میکنید ممکنه اصلا چیزی به اسم input یا buffer نبینید
مثلا ممکنه همچین چیزی ببینید
mov [rbp-40h], eax
lea rcx, [rbp-40h]
movzx eax, byte ptr [rcx]
اینجا باید خودتون رابطه بین این دستورها رو پیدا کنید
یعنی
EAX
↓
[rbp-40h]
↓
RCX
↓
[RCX]
↓
EAX
یعنی داده یه بار وارد Memory شده
بعد یه Pointer به اون قسمت ساخته شده
و دوباره داده از طریق همون Pointer خونده شده
Pointer
خودش Tainted هست یا داده ای که بهش اشاره میکنه
اینجا باید خیلی دقت کنیم
فرض کنید
C
char *p = buffer;
char x = *p;
ممکنه Buffer حاوی داده Tainted باشه
ولی خود Pointer یعنی p لزوما Tainted نیست
مثلا:
Address = Clean
Data at Address = Tainted
این دوتا با هم فرق دارن
پس وقتی Memory رو تحلیل میکنید همیشه دو تا سوال جدا از خودتون بپرسید
این Address از کجا اومده
محتوای این Address از کجا اومده
مثلا ممکنه داشته باشیم
Pointer
0x500000
خود این Address ممکنه کاملا عادی و قابل اعتماد باشه
ولی محتوای
Memory[0x500000]
ممکنه از ورودی کاربر اومده باشه
پس Address میتونه Clean باشه
ولی Data داخل اون Address Tainted باشه
یه مثال ساده تر:
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] + 10;
return x;
}
اگر input رو Tainted کنیم
مسیر داده میشه
input[0]
↓
buffer[0]
↓
x
↓
return
پس حتی اگر داده برای یه مدت داخل Stack قرار گرفته باشه
Taint
خودش از بین نمیره
تا وقتی که داده جدید همچنان به اون مقدار وابسته باشه
حالا همین رو توی Assembly ببینیم
ممکنه چیزی شبیه این ببینیم
movzx eax, byte ptr [rcx]
mov byte ptr [rbp-20h], al
movzx eax, byte ptr [rbp-20h]
add eax, 10
اینجا باید مسیر رو خودمون از روی دستورها بسازیم
[RCX]
↓
AL
↓
[rbp-20h]
↓
EAX
↓
ADD 10
اگر
پس این مسیر هم باید توی تحلیل ما دنبال بشه
چرا Pointer Analysis مهم میشه
چون توی برنامه های بزرگ ممکنه داده مستقیم از یه Variable به Variable بعدی نره
ممکنه مسیر این شکلی باشه
Input
↓
Buffer
↓
Pointer
↓
Function A
↓
Memory
↓
Pointer
↓
Function B
↓
Comparison
اگر فقط Registerها رو نگاه کنی ممکنه وسط مسیر ارتباط داده رو گم کنی
برای همین توی Reverse Engineering خیلی مهم
Data Flow و Memory Analysis
خیلی به هم وابسته هستن
یه نکته مهم
هر Memory Access به معنی انتقال Taint نیست
مثلا:
C++
int x = 100;
int y = x + 20;
اینجا هیچ داده ای از Input نیومده
پس
x = Clean
y = Clean
ولی اگر داشته باشیم
C++
int x = input;
int y = x + 20;
اینجا داریم
x = Tainted
y = Tainted
پس همیشه باید منشا داده رو دنبال کنیم
فقط اینکه یه مقدار وارد Memory شده به تنهایی کافی نیست
باید ببینیم اون مقدار از کجا اومده و بعد کجا استفاده شده
تمرین:
این کد رو بررسی کنید
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] ^ 0x55;
int clean = 500;
clean += 20;
int result = x + 10;
return result;
}
فرض کنید
مسیر رو پیدا کنید
input[0]
↓
?
↓
?
↓
?
↓
return
بعد مشخص کنید
@reverseengine
روی Memory و Pointer ها
تا اینجا Taint Analysis رو روی متغیرهای ساده انجام دادیم
ولی توی برنامه واقعی داستان یکم پیچیده تر میشه چون داده فقط بین چند تا متغیر جابه جا نمیشه
ممکنه وارد Memory بشه
داخل یه Buffer قرار بگیره
از طریق Pointer جابه جا بشه
و بعد چند تا تابع مختلف از همون Memory استفاده کنن
اینجاست که Taint Analysis یکم سخت تر میشه
فرض کنید اینو داریم
C
char buffer[32];
buffer[0] = input;
اینجا داده ای که از input اومده وارد Memory شده
پس میتونیم بگیم
input
↓
buffer[0]
حالا فرض کنید یه Pointer به این Buffer داریم
C
char *p = buffer;
بعد هم
char x = p[0];
اینجا داده دوباره از Memory خونده شده
پس مسیرمون میشه
input
↓
buffer[0]
↓
p[0]
↓
x
پس Taint فقط بین Register و Variable حرکت نمیکنه
ممکنه مسیرش از چند مرحله مختلف رد بشه
Register
↓
Memory
↓
Pointer
↓
Memory
↓
Register
این موضوع توی Reverse Engineering خیلی مهمه
چون وقتی Assembly رو نگاه میکنید ممکنه اصلا چیزی به اسم input یا buffer نبینید
مثلا ممکنه همچین چیزی ببینید
mov [rbp-40h], eax
lea rcx, [rbp-40h]
movzx eax, byte ptr [rcx]
اینجا باید خودتون رابطه بین این دستورها رو پیدا کنید
یعنی
EAX
↓
[rbp-40h]
↓
RCX
↓
[RCX]
↓
EAX
یعنی داده یه بار وارد Memory شده
بعد یه Pointer به اون قسمت ساخته شده
و دوباره داده از طریق همون Pointer خونده شده
Pointer
خودش Tainted هست یا داده ای که بهش اشاره میکنه
اینجا باید خیلی دقت کنیم
فرض کنید
C
char *p = buffer;
char x = *p;
ممکنه Buffer حاوی داده Tainted باشه
ولی خود Pointer یعنی p لزوما Tainted نیست
مثلا:
Address = Clean
Data at Address = Tainted
این دوتا با هم فرق دارن
پس وقتی Memory رو تحلیل میکنید همیشه دو تا سوال جدا از خودتون بپرسید
این Address از کجا اومده
محتوای این Address از کجا اومده
مثلا ممکنه داشته باشیم
Pointer
0x500000
خود این Address ممکنه کاملا عادی و قابل اعتماد باشه
ولی محتوای
Memory[0x500000]
ممکنه از ورودی کاربر اومده باشه
پس Address میتونه Clean باشه
ولی Data داخل اون Address Tainted باشه
یه مثال ساده تر:
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] + 10;
return x;
}
اگر input رو Tainted کنیم
مسیر داده میشه
input[0]
↓
buffer[0]
↓
x
↓
return
پس حتی اگر داده برای یه مدت داخل Stack قرار گرفته باشه
Taint
خودش از بین نمیره
تا وقتی که داده جدید همچنان به اون مقدار وابسته باشه
حالا همین رو توی Assembly ببینیم
ممکنه چیزی شبیه این ببینیم
movzx eax, byte ptr [rcx]
mov byte ptr [rbp-20h], al
movzx eax, byte ptr [rbp-20h]
add eax, 10
اینجا باید مسیر رو خودمون از روی دستورها بسازیم
[RCX]
↓
AL
↓
[rbp-20h]
↓
EAX
↓
ADD 10
اگر
[RCX] حاوی داده Tainted باشهپس این مسیر هم باید توی تحلیل ما دنبال بشه
چرا Pointer Analysis مهم میشه
چون توی برنامه های بزرگ ممکنه داده مستقیم از یه Variable به Variable بعدی نره
ممکنه مسیر این شکلی باشه
Input
↓
Buffer
↓
Pointer
↓
Function A
↓
Memory
↓
Pointer
↓
Function B
↓
Comparison
اگر فقط Registerها رو نگاه کنی ممکنه وسط مسیر ارتباط داده رو گم کنی
برای همین توی Reverse Engineering خیلی مهم
Data Flow و Memory Analysis
خیلی به هم وابسته هستن
یه نکته مهم
هر Memory Access به معنی انتقال Taint نیست
مثلا:
C++
int x = 100;
int y = x + 20;
اینجا هیچ داده ای از Input نیومده
پس
x = Clean
y = Clean
ولی اگر داشته باشیم
C++
int x = input;
int y = x + 20;
اینجا داریم
x = Tainted
y = Tainted
پس همیشه باید منشا داده رو دنبال کنیم
فقط اینکه یه مقدار وارد Memory شده به تنهایی کافی نیست
باید ببینیم اون مقدار از کجا اومده و بعد کجا استفاده شده
تمرین:
این کد رو بررسی کنید
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] ^ 0x55;
int clean = 500;
clean += 20;
int result = x + 10;
return result;
}
فرض کنید
input[0] نقطه شروع Taint باشهمسیر رو پیدا کنید
input[0]
↓
?
↓
?
↓
?
↓
return
بعد مشخص کنید
clean چرا نباید وارد این مسیر بشه@reverseengine
ReverseEngineering
Taint Analysis روی Memory و Pointer ها تا اینجا Taint Analysis رو روی متغیرهای ساده انجام دادیم ولی توی برنامه واقعی داستان یکم پیچیده تر میشه چون داده فقط بین چند تا متغیر جابه جا نمیشه ممکنه وارد Memory بشه داخل یه Buffer قرار بگیره از طریق Pointer جابه…
Taint Analysis On Memory and Pointers
So far we have done Taint Analysis on simple variables
But in a real program the story gets a bit more complicated because data is not just moved between a few variables
It may be entered into Memory
It may be placed into a Buffer
It may be moved through a Pointer
And then several different functions may use the same Memory
This is where Taint Analysis gets a bit more difficult
Suppose we have this
C
char buffer[32];
buffer[0] = input;
Here the data that came from input is entered into Memory
So we can say
input
↓
buffer[0]
Now suppose we have a Pointer to this Buffer
C
char *p = buffer;
And then
char x = p[0];
Here the data is read from Memory again
So our path becomes
input
↓
buffer[0]
↓
p[0]
↓
x
So Taint doesn't just move between Register and Variable
Its path may go through several different stages
Register
↓
Memory
↓
Pointer
↓
Memory
↓
Register
This is very important in Reverse Engineering
Because when you look at the Assembly you may not see anything called input or buffer at all
For example, you may see something like this
mov [rbp-40h], eax
lea rcx, [rbp-40h]
movzx eax, byte ptr [rcx]
Here you have to find the relationship between these instructions yourself
That is
EAX
↓
[rbp-40h]
↓
RCX
↓
[RCX]
↓
EAX
That is, the data is entered into Memory once
Then a Pointer is created to that part
And the data is read again through the same Pointer
Pointer
Itself Is it Tainted or the data it points to
Here we have to be very careful
Suppose
C
char *p = buffer;
char x = *p;
Buffer may contain Tainted data
But Pointer itself, i.e. p, is not necessarily Tainted
For example:
Address = Clean
Data at Address = Tainted
These two are different
So when you analyze Memory, always ask yourself two separate questions
Where did this Address come from
Where did the content of this Address come from
For example, we may have
Pointer
0x500000
This Address itself may be completely normal and reliable
But the content of
Memory[0x500000]
may have come from user input
So Address can be Clean
But Data inside that Address is Tainted
A simpler example:
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] + 10;
return x;
}
If we Tainted the input
the data path will be
input[0]
↓
buffer[0]
↓
x
↓
return
So even if the data is in the Stack for a while
Taint
itself will not go away
as long as the new data is still attached to that value
Now let's see the same in Assembly
We may see something like this
movzx eax, byte ptr [rcx]
mov byte ptr [rbp-20h], al
movzx eax, byte ptr [rbp-20h]
add eax, 10
Here we have to create the path ourselves from the instructions
[RCX]
↓
AL
↓
[rbp-20h]
↓
EAX
↓
ADD 10
If [RCX] contains Tainted data
then this path should also be followed in our analysis
Why Pointer Analysis is important
Because in large programs, data may be passed directly from one Variable to the next Variable No
The path may be like this
Input
↓
Buffer
↓
Pointer
↓
Function A
↓
Memory
↓
Pointer
↓
Function B
↓
Comparison
If you only look at the registers, you may miss the data connection in the middle of the path
That is why it is very important in Reverse Engineering
Data Flow and Memory Analysis
are very interrelated
An important point
Not every Memory Access means transferring Taint
For example:
C++
int x = 100;
int y = x + 20;
Here no data came from Input
So
x = Clean
y = Clean
But if we have
C++
int x = input;
int y = x + 20;
Here we have
x = Tainted
y = Tainted
So we always need to trace the origin of the data
Just because a value is entered into Memory is not enough
We need to see where that value came from and where it was used
Exercise:
Examine this code
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] ^ 0x55;
int clean = 500;
clean += 20;
int result = x + 10;
return result;
}
Assume input[0] is the starting point of Taint
Find the path
input[0]
↓
?
↓
?
↓
?
↓
return
Then specify why clean should not enter this path
@reverseengine
So far we have done Taint Analysis on simple variables
But in a real program the story gets a bit more complicated because data is not just moved between a few variables
It may be entered into Memory
It may be placed into a Buffer
It may be moved through a Pointer
And then several different functions may use the same Memory
This is where Taint Analysis gets a bit more difficult
Suppose we have this
C
char buffer[32];
buffer[0] = input;
Here the data that came from input is entered into Memory
So we can say
input
↓
buffer[0]
Now suppose we have a Pointer to this Buffer
C
char *p = buffer;
And then
char x = p[0];
Here the data is read from Memory again
So our path becomes
input
↓
buffer[0]
↓
p[0]
↓
x
So Taint doesn't just move between Register and Variable
Its path may go through several different stages
Register
↓
Memory
↓
Pointer
↓
Memory
↓
Register
This is very important in Reverse Engineering
Because when you look at the Assembly you may not see anything called input or buffer at all
For example, you may see something like this
mov [rbp-40h], eax
lea rcx, [rbp-40h]
movzx eax, byte ptr [rcx]
Here you have to find the relationship between these instructions yourself
That is
EAX
↓
[rbp-40h]
↓
RCX
↓
[RCX]
↓
EAX
That is, the data is entered into Memory once
Then a Pointer is created to that part
And the data is read again through the same Pointer
Pointer
Itself Is it Tainted or the data it points to
Here we have to be very careful
Suppose
C
char *p = buffer;
char x = *p;
Buffer may contain Tainted data
But Pointer itself, i.e. p, is not necessarily Tainted
For example:
Address = Clean
Data at Address = Tainted
These two are different
So when you analyze Memory, always ask yourself two separate questions
Where did this Address come from
Where did the content of this Address come from
For example, we may have
Pointer
0x500000
This Address itself may be completely normal and reliable
But the content of
Memory[0x500000]
may have come from user input
So Address can be Clean
But Data inside that Address is Tainted
A simpler example:
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] + 10;
return x;
}
If we Tainted the input
the data path will be
input[0]
↓
buffer[0]
↓
x
↓
return
So even if the data is in the Stack for a while
Taint
itself will not go away
as long as the new data is still attached to that value
Now let's see the same in Assembly
We may see something like this
movzx eax, byte ptr [rcx]
mov byte ptr [rbp-20h], al
movzx eax, byte ptr [rbp-20h]
add eax, 10
Here we have to create the path ourselves from the instructions
[RCX]
↓
AL
↓
[rbp-20h]
↓
EAX
↓
ADD 10
If [RCX] contains Tainted data
then this path should also be followed in our analysis
Why Pointer Analysis is important
Because in large programs, data may be passed directly from one Variable to the next Variable No
The path may be like this
Input
↓
Buffer
↓
Pointer
↓
Function A
↓
Memory
↓
Pointer
↓
Function B
↓
Comparison
If you only look at the registers, you may miss the data connection in the middle of the path
That is why it is very important in Reverse Engineering
Data Flow and Memory Analysis
are very interrelated
An important point
Not every Memory Access means transferring Taint
For example:
C++
int x = 100;
int y = x + 20;
Here no data came from Input
So
x = Clean
y = Clean
But if we have
C++
int x = input;
int y = x + 20;
Here we have
x = Tainted
y = Tainted
So we always need to trace the origin of the data
Just because a value is entered into Memory is not enough
We need to see where that value came from and where it was used
Exercise:
Examine this code
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] ^ 0x55;
int clean = 500;
clean += 20;
int result = x + 10;
return result;
}
Assume input[0] is the starting point of Taint
Find the path
input[0]
↓
?
↓
?
↓
?
↓
return
Then specify why clean should not enter this path
@reverseengine