en
Feedback

Don't get caught by a cheater! Telemetrio finds and tags such channels 👉 If you want to see the tag, subscribe

ReverseEngineering

ReverseEngineering

Open in Telegram
1 442
Subscribers
+124 hours
+1087 days
+11130 days
Posts Archive
An AI-Assisted Cyber Attack: Inside a Unit 42 Investigation injecting backdoor in to terraform https://unit42.paloaltonetworks.com/ai-assisted-cyber-attack-inside-a-unit-42-investigation @reverseengine

+1
Assembly Language for x86 Processors

Pointer Aliasing So far we have understood that data can go from Register to Memory and back again through Pointer Now we have a more important problem Two different Pointers may point to the same part of Memory For example: int value = 100; int *p = &value; int *q = &value; Here p and q are two different Pointers But both point to the same Memory value | 0x5000 / \ / \ p q So if we do this *p = 200; the value of value will change And when we read this later printf("%d", *q); We see the value 200 Because p and q actually point to the same location We call this situation Pointer Aliasing This is very important for Reverse Engineering Because you might see inside a function mov [rax], 200 And a few lines down mov ecx, [rbx] At first glance you might think these two instructions have nothing to do But if RAX = 0x5000 RBX = 0x5000 Well They are both working with the same memory RAX ─────┐ ↓ 0x5000 ↑ | RBX ─────┘ So a change through RAX can directly affect what is read through RBX Here the Data Flow is not just between Registers You should also follow the relationship between Pointers and Memory Addresses A simpler example int value = 10; int *p = &value; int *q = p; *p = 50; int x = *q; Actual data path value ↓ p ↓ q ↓ Memory ↓ 50 ↓ x But in terms of Address p ──┐ ├──> value q ──┘ So p and q are two different paths to reach the same data In Reverse Engineering When working with Pointers, don't just ask Where is this Pointer? You should ask Where does this Pointer point to? What other Pointers might point to the same Address? Who changed the value of that Memory? Who later read that value? These questions help you find connections that are not obvious from normal Control Flow Exercise: Examine this code int value = 10; int *p = &value; int *q = p; *p = 100; int result = *q; Draw the Data Flow path value ↓ p ↓ q ↓ *p = 100 ↓ *q ↓ result Then determine why result is equal to 100 Next, we will move on to a more important topic Memory Aliasing In different functions There we no longer have two pointers inside a function One function modifies the Memory and another function reads the same Memory through a different Pointer And this is exactly where Data Flow analysis in real binaries starts to cause trouble @reverseengine

Pointer Aliasing تا اینجا فهمیدیم داده میتونه از Register بره داخل Memory و دوباره از طریق Pointer برگرده حالا یک مشکل مهم تر داریم ممکنه دو Pointer مختلف به یک قسمت از Memory اشاره کنن مثلا: int value = 100; int *p = &value; int *q = &value; اینجا p و q دو Pointer مختلف هستن ولی هر دو به یک Memory اشاره میکنن value | 0x5000 / \ / \ p q پس اگر این کار رو انجام بدیم *p = 200; مقدار value تغییر میکنه و وقتی بعدا این رو بخونیم printf("%d", *q); مقدار 200 رو میبینیم چون p و q در واقع به یک محل اشاره میکنن به این وضعیت میگیم Pointer Aliasing این موضوع برای Reverse Engineering خیلی مهمه چون ممکنه داخل یک تابع ببینید mov [rax], 200 و چند خط پایین‌ تر ببینید mov ecx, [rbx] در نگاه اول ممکنه فکر کنید این دو دستور هیچ ارتباطی ندارن ولی اگر RAX = 0x5000 RBX = 0x5000 باشه هر دو دارن با یک Memory مشترک کار میکنن RAX ─────┐ ↓ 0x5000 ↑ | RBX ─────┘ پس تغییر از طریق RAX میتونه مستقیما روی چیزی که از طریق RBX خونده میشه اثر بذاره اینجا Data Flow دیگه فقط بین Register ها نیست باید رابطه Pointer ها و Memory Address ها رو هم دنبال کنید یک مثال ساده‌ تر int value = 10; int *p = &value; int *q = p; *p = 50; int x = *q; مسیر واقعی داده value ↓ p ↓ q ↓ Memory ↓ 50 ↓ x اما از نظر Address p ──┐ ├──> value q ──┘ پس p و q دو مسیر متفاوت برای رسیدن به یک داده هستن در Reverse Engineering وقتی با Pointerها کار میکنید فقط نپرسد این Pointer کجاست؟ باید بپرسید این Pointer به کجا اشاره میکنه؟ چه Pointer های دیگه‌ ای ممکنه به همین Address اشاره کنن؟ چه کسی مقدار اون Memory رو تغییر داده؟ چه کسی بعدا اون مقدار رو خونده؟ این سوال‌ها کمک میکنن ارتباط‌ هایی رو پیدا کنی که از روی Control Flow معمولی مشخص نیستن تمرین: این کد رو بررسی کنید int value = 10; int *p = &value; int *q = p; *p = 100; int result = *q; مسیر Data Flow رو رسم کنید value ↓ p ↓ q ↓ *p = 100 ↓ *q ↓ result بعد مشخص کن چرا result برابر 100 میشه در مرحله بعد میریم سراغ یک موضوع مهم‌تر Memory Aliasing در توابع مختلف اونجا دیگه دو Pointer داخل یک تابع نداریم یک تابع Memory رو تغییر میده و یک تابع دیگه از طریق Pointer متفاوت همون Memory رو میخونه و دقیقا همینجا تحلیل Data Flow در باینری‌ های واقعی شروع میکنه به دردسر درست کردن @reverseengine

Therefore, dynamic analysis cannot provide information about an unexecuted path with the same level of certainty as it does for an executed one. For this reason, these two methods are generally more valuable when used together: Static Analysis + Dynamic Analysis Static Helps us understand what paths and dependencies might exist. Dynamic Helps us understand what actually happened during a specific execution. Tools There are various tools and frameworks available for Dynamic Binary Analysis and Taint Tracking: Intel Pin DynamoRIO Triton angr PANDA However, we shouldn't assume all these tools do exactly the same thing; their instrumentation models and capabilities differ. For example: Triton Offers features like Taint Analysis and Symbolic Execution for Dynamic Binary Analysis. Connection to Fuzzing This is where it gets interesting. Previously, we had: Fuzzer ↓ Interesting Input ↓ Crash Now, we can use Dynamic Taint Analysis to examine the data path. For example: Interesting Input ↓ Tainted Data ↓ Parser ↓ Variable ↓ Memory Operation ↓ Crash So, we don't just say the program crashed; we can also trace the path the input data took during execution and identify where it contributed to the issue. Static + Dynamic A logical analysis workflow might look like this: Fuzzing ↓ Crash ↓ Crash Triage ↓ Static Analysis ↓ Find Source / Sink ↓ Dynamic Analysis ↓ Trace Data ↓ Root Cause Here, we gradually move from a simple crash to a better understanding of the path that caused the bug. Dynamic Taint Analysis Means tracking specific data or data dependencies during the actual execution of the program. Key concepts: Source ↓ Taint ↓ Propagation ↓ Register / Memory ↓ Control Flow ↓ Sink An important point is that static analysis and dynamic analysis are not rivals. Static analysis primarily answers this question: What possible paths might exist? Dynamic analysis primarily answers this question: What path was actually taken during this specific execution? When we combine these two with fuzzing and crash analysis, we can trace a path from Crash to Input ↓ Data Flow ↓ Instruction ↓ Memory Operation ↓ Crash ↓ Root Cause And this is precisely the point where analyzing a crash shifts from observing a simple event to understanding the bug's root cause. @reverseengine

We see the same actual path But here we have a problem Dynamic Analysis does not see everything Suppose the program looks like this Input ↓ if (condition) ↓ Path B If the condition is not met Path B will not be executed at all

Part 34 Buffer Overflow
Dynamic Taint Analysis Tracking Data During Actual Program Execution So far, we have mostly examined the data path based on the assembly and program structure That is, we did not execute the program and tried to understand from the code where the data might come from and where it might end up This is called Static Analysis Now we want to go one step further to actually execute the program and see exactly what paths the data takes in this specific execution Here we come to Dynamic Taint Analysis or DTA Difference between Static and Dynamic In Static Analysis, the program is not executed We examine from the code and assembly Source ↓ Instruction ↓ Function ↓ Sink But in Dynamic Analysis, the program is actually executed Program Running ↓ Input ↓ Execution ↓ Track Data ↓ Sink That is, instead of just saying that this path might exist We see what actually happened in this specific execution A simple example Suppose the program is C #include <stdio.h> #include <string.h> void process(char *input) { char buffer[32]; strcpy(buffer, input); printf("%s\n", buffer); } The program runs and receives this input hello Now our question is What path has this hello taken since entering the program? This is where Taint Analysis comes in Source First we need to specify where the data entered the program from For example: stdin Or for example read() recv() fread() Then we can say stdin ↓ TAINTED DATA That is, the data entered from Source is currently marked as Tainted Taint Propagation Now suppose the program does this C char *input = get_input(); char *data = input; char *ptr = data; From the perspective of Taint Tracking, the data dependency path can be a concept like this input ↓ Tainted ↓ data ↓ Tainted ↓ ptr ↓ Tainted That is, the value in ptr comes from data that was entered from Source Of course, in real tools, the story is a little more complicated Because Taint is not just a simple label on a variable The tool must also track data transfers and changes in Registers and Memory Taint on a part of the data An important point is that not always the entire data is Tainted Suppose the input is ABCDEFGH An operation may depend only on the first four bytes ABCD In this case, we can have a concept like this A → Tainted B → Tainted C → Tainted D → Tainted E → Clean F → Clean G → Clean H → Clean That is, Taint can be tracked at the level of a part of a value This is especially true in the analysis of Parsers and Protocols Important Because we can understand which part of the Input actually affected a particular operation Taint in Registers Now let's move on to what we deal with most in Reverse Engineering Assembly Suppose that when the program is executed, this instruction is executed mov rax,[rbp-40h] and the Memory at the address [rbp-40h] contains Tainted data So after executing the instruction [rbp-40h] ↓ RAX Now the next instruction mov rcx,rax will also follow the Taint dependency Memory ↓ RAX ↓ RCX Then suppose we have mov [rdi],rcx Now the data path is Input ↓ Memory ↓ RAX ↓ RCX ↓ Memory Write Here we are not just guessing from the Assembly We are following the data path in a real execution An important point about Taint and Pointer Here we need to be careful Okay Taint a Pointer does not necessarily mean that the data that the Pointer points to is tainted For example: Pointer ↓ 0x12345678 The value of the Pointer itself may be affected by Input But the content of the address 0x12345678 is not necessarily tainted in the same way This difference is very important in Memory Analysis Because Pointer Taint and Pointee Data Taint are two different concepts What is the advantage of Dynamic Analysis ? Suppose a function has 50 different paths In Static Analysis we may need to examine a large number of these paths But Dynamic Analysis only sees what is actually executed on the current execution For example: Function ├── Path A ├── Path B ├── Path C └── Path D If our Input causes Path C to be executed Input ↓ Path C ↓ Sink

پس Dynamic Analysis نمیتونه درباره مسیری که اجرا نشده با همان قطعیت مسیر اجراشده اطلاعات بده به همین دلیل معمولا این دو روش کنار هم ارزش بیشتری دارن Static Analysis + Dynamic Analysis Static کمک میکنه بفهمیم چه مسیر ها و وابستگی‌ هایی ممکنه وجود داشته باشن Dynamic کمک میکنه بفهمیم در یک اجرای مشخص واقعا چه اتفاقی افتاده ابزارها برای Dynamic Binary Analysis و Taint Tracking ابزارها و Framework های مختلفی وجود دارن Intel Pin DynamoRIO Triton angr PANDA ولی نباید فکر کنیم همه این ابزارها دقیقا یک کار رو انجام میدن مدل Instrumentation و قابلیت‌ های هرکدوم متفاوته مثلا: Triton برای Dynamic Binary Analysis امکاناتی مثل Taint Analysis و Symbolic Execution داره ارتباط با Fuzzing اینجا داستان جالب‌ تر میشه ما قبلا داشتیم Fuzzer ↓ Interesting Input ↓ Crash حالا میتونیم از Dynamic Taint Analysis برای بررسی مسیر داده استفاده کنیم مثلا: Interesting Input ↓ Tainted Data ↓ Parser ↓ Variable ↓ Memory Operation ↓ Crash پس فقط نمیگیم برنامه Crash کرد بلکه میتونیم بررسی کنیم داده‌ای که از Input وارد شده در طول اجرای برنامه چه مسیر هایی رو طی کرده و کجا در ایجاد مشکل نقش داشته Static + Dynamic یک Workflow منطقی برای تحلیل میتونه این شکلی باشه Fuzzing ↓ Crash ↓ Crash Triage ↓ Static Analysis ↓ Find Source / Sink ↓ Dynamic Analysis ↓ Trace Data ↓ Root Cause اینجا کم‌ کم از یک Crash ساده میرسیم به درک بهتر مسیر ایجاد Bug Dynamic Taint Analysis یعنی داده یا وابستگی داده‌ ای مشخص رو هنگام اجرای واقعی برنامه دنبال کنیم مفاهیم اصلی: Source ↓ Taint ↓ Propagation ↓ Register / Memory ↓ Control Flow ↓ Sink و نکته مهم اینه که Static Analysis و Dynamic Analysis رقیب هم نیستن Static بیشتر جواب این سوال رو میده چه مسیر هایی ممکنه وجود داشته باشه؟ Dynamic بیشتر جواب این سوال رو میده در این اجرای مشخص واقعا چه مسیری طی شد؟ وقتی این دو رو کنار Fuzzing و Crash Analysis بذاریم میتونیم از Crash بریم به Input ↓ Data Flow ↓ Instruction ↓ Memory Operation ↓ Crash ↓ Root Cause و این دقیقا همون جاییه که تحلیل یک Crash از یک اتفاق ساده تبدیل میشه به فهمیدن علت واقعی Bug @reverseengine

قسمت سی و چهارم بافر اورفلو
Dynamic Taint Analysis دنبال کردن داده در زمان اجرای واقعی برنامه تا اینجا بیشتر از روی Assembly و ساختار برنامه مسیر داده رو بررسی کردیم یعنی برنامه رو اجرا نکردیم و سعی کردیم از روی کد بفهمیم داده ممکنه از کجا وارد بشه و به کجا برسه این میشه Static Analysis حالا میخوایم یک قدم جلوتر بریم برنامه رو واقعا اجرا کنیم و ببینیم داده در همین اجرای مشخص دقیقا از چه مسیرهایی عبور میکنه اینجا می‌رسیم به Dynamic Taint Analysis یا DTA فرق Static و Dynamic در Static Analysis برنامه اجرا نمیشه ما از روی کد و Assembly بررسی میکنیم Source ↓ Instruction ↓ Function ↓ Sink ولی در Dynamic Analysis برنامه واقعا اجرا میشه Program Running ↓ Input ↓ Execution ↓ Track Data ↓ Sink یعنی به جای اینکه فقط بگیم این مسیر ممکنه وجود داشته باشه میبینیم در این اجرای مشخص واقعا چه اتفاقی افتاده یک مثال ساده فرض کنید برنامه اینه C #include <stdio.h> #include <string.h> void process(char *input) { char buffer[32]; strcpy(buffer, input); printf("%s\n", buffer); } برنامه اجرا میشه و این ورودی رو دریافت میکنه hello حالا سوال ما اینه این hello از لحظه ورود به برنامه چه مسیری رو طی کرده؟ اینجاست که Taint Analysis وارد میشه Source اول باید مشخص کنیم داده از کجا وارد برنامه شده مثلا: stdin یا مثلا read() recv() fread() پس میتونیم بگیم stdin ↓ TAINTED DATA یعنی داده‌ای که از Source وارد شده فعلا به عنوان Tainted علامت‌گذاری شده Taint Propagation حالا فرض کنید برنامه این کار رو انجام بده C char *input = get_input(); char *data = input; char *ptr = data; از دید Taint Tracking مسیر وابستگی داده میتونه مفهومی شبیه این باشه input ↓ Tainted ↓ data ↓ Tainted ↓ ptr ↓ Tainted یعنی مقدار موجود در ptr از داده‌ای میاد که از Source وارد شده البته در ابزارهای واقعی داستان کمی پیچیده‌ تره چون Taint فقط یک برچسب ساده روی متغیر نیست ابزار باید انتقال و تغییر داده رو در Registerها و Memory هم دنبال کنه Taint روی بخشی از داده یک نکته مهم اینه که همیشه کل داده Tainted نیست فرض کنید ورودی اینه ABCDEFGH ممکنه یک عملیات فقط به چهار بایت اول وابسته باشه ABCD در این حالت میتونیم مفهومی شبیه این داشته باشیم A → Tainted B → Tainted C → Tainted D → Tainted E → Clean F → Clean G → Clean H → Clean یعنی Taint میتونه در سطح بخشی از یک مقدار هم دنبال بشه این موضوع مخصوصا در تحلیل Parser ها و Protocol ها مهمه چون میتونیم بفهمیم کدوم قسمت از Input واقعا روی یک عملیات خاص تأثیر گذاشته Taint در Register ها حالا بریم سراغ چیزی که در Reverse Engineering بیشتر باهاش سر و کار داریم Assembly فرض کنید هنگام اجرای برنامه این دستور اجرا میشه mov rax,[rbp-40h] و Memory در آدرس [rbp-40h] حاوی داده Tainted باشه پس بعد از اجرای دستور [rbp-40h] ↓ RAX حالا دستور بعدی mov rcx,rax باعث میشه وابستگی Taint هم دنبال بشه Memory ↓ RAX ↓ RCX بعد فرض کنید داریم mov [rdi],rcx حالا مسیر داده میشه Input ↓ Memory ↓ RAX ↓ RCX ↓ Memory Write اینجا دیگه فقط از روی Assembly حدس نزدیم داریم مسیر داده رو در یک اجرای واقعی دنبال میکنیم یک نکته مهم درباره Taint و Pointer اینجا باید حواسمون باشه Taint کردن یک Pointer حتما به معنی Taint شدن داده‌ای که Pointer بهش اشاره می‌کنه نیست مثلا: Pointer ↓ 0x12345678 ممکنه خود مقدار Pointer تحت تاثیر Input باشه ولی محتوای آدرس 0x12345678 حتما به همون شکل Tainted نیست این تفاوت در تحلیل Memory خیلی مهمه چون Pointer Taint و Pointee Data Taint دو مفهوم متفاوت هستن Dynamic Analysis چه مزیتی داره؟ فرض کنید یک تابع 50 مسیر مختلف داره در Static Analysis ممکنه لازم باشه تعداد زیادی از این مسیرها رو بررسی کنیم ولی Dynamic Analysis روی اجرای فعلی فقط چیزی رو میبینه که واقعا اجرا شده مثلا: Function ├── Path A ├── Path B ├── Path C └── Path D اگر Input ما باعث اجرای Path C بشه Input ↓ Path C ↓ Sink همین مسیر واقعی رو مشاهده میکنیم ولی اینجا یک مشکل داریم Dynamic Analysis همه چیز رو نمیبینه فرض کنید برنامه این شکلیه Input ↓ if (condition) ↓ Path B اگر شرط برقرار نشه Path B اصلا اجرا نمیشه

Image-backed Memory ↓ Memory associated with an image mapping Private Memory ↓ Memory not dependent on a standard image mapping VAD ↓ Kernel structures describing virtual address space ranges And most importantly: Private ≠ Malicious Executable ≠ Malicious RW → RX ≠ Malicious What matters is the combination of evidence and context. @reverseengine

Region B It could be perfectly normal, as Private RW memory is very common within processes. However, Region C: Private RX warrants further investigation. Let's gather more information: Region C ↓ Private ↓ RX ↓ Thread Start Address → Inside the region ↓ Call Stack → Unusual At this point, Region C becomes an investigation target. But we don't immediately conclude: Malware Found because there might be a legitimate explanation. For instance, runtimes, JIT compilation, instrumentation, or certain software techniques can also create executable memory. So: Suspicious ≠ Malicious Permissions are just one piece of the puzzle. We’ve discussed permissions quite a bit so far— such as: RW RX RWX —but a proper analysis looks beyond just permissions. Compare these two regions, for example: Region A Image RX Known Module Normal Thread Activity And: Region B Private RX Unknown Mapping Unusual Thread Start Unexpected Call Stack Both might be executable, but they exist in completely different contexts. So, a better model considers: Memory Type + Protection + Content + Thread + Call Stack + Module Relationship + Timeline The more the evidence points to a consistent narrative, the higher the confidence in the analysis. Why is the timeline important? Suppose a memory region currently looks like this: Private RX Just by looking at the current state, we don't know what happened previously. It might have initially been: RW ...and its permissions were subsequently changed, or it might have been created as: RX from the start, or created by a legitimate runtime process. So, if we have sufficient telemetry, the timeline can be crucial: Allocation ↓ Memory Write ↓ Protection Change ↓ Thread Creation ↓ Execution This chain provides far more information than a simple snapshot. From a detection perspective: We can now piece together several pieces of evidence: Process │ ├── Memory Region │ ├── Private │ └── Executable │ ├── Thread │ └── Start Address → Region │ └── Call Stack └── Unexpected Frames None of these individually necessarily indicates an attack, but the relationship between them can be vital for detection. This is precisely one of the core concepts of behavioral detection: A single event ↓ might be benign Multiple related events ↓ Context ↓ Behavior From a Red Teamer's perspective— and from an attacker's perspective, too—there is an important lesson here: Memory also generates telemetry. This means the notion that: No File = No Detection is incorrect; there might not be an obvious file on disk, yet the state of the process and memory can still generate evidence. A more comprehensive model: Disk + Process + Memory + Thread + Call Stack + Timeline ↓ Behavioral Evidence So, understanding memory isn't just important for those writing detection rules; it is also crucial for Red Teamers to understand what visible traces code and data leave behind after entering a process. An important point regarding "Private" and "Image" types: Let's not oversimplify these two categories. In Windows memory management, the type of a region... It isn't determined solely by a statement like: Private = Something created by the program itself Image = Something read directly from disk Details regarding the Memory Manager, Section Objects, mappings, and allocation types are crucial. So, when you see something like Type = Private in forensics tools or debuggers, you need to know exactly what information the tool used to make that classification. Our goal in Reverse Engineering (RE) and memory forensics isn't just to memorize a label; The goal is to understand the memory behavior underlying that label. API / Syscall ↓ Memory Allocation ↓ Memory Protection ↓ Memory Type ↓ Thread ↓ Call Stack ↓ VAD ↓ Behavioral Analysis Keep these three concepts firmly in mind for now:

Private Memory and Image-backed Memory and VAD So far we have talked about RW → RX What does this Memory even belong to? When you examine a process, not all the memory you see is the same To analyze memory-based behavior, we need to know how each region is built and what its relationship is to the file on disk Image-backed Memory When a PE such as: notepad.exe ntdll.dll kernel32.dll is placed inside the process, parts of the Virtual Address Space are linked to an Image File on disk Conceptually: PE File │ ▼ Image Mapping │ ▼ Process Virtual Address Space For example, an Executable may have sections like this: notepad.exe ├── .text ├── .rdata ├── .data └── ... In memory, these sections are usually linked to the Image related to the same Module, so when a Region is of the Image type, one of the important questions is: Which Module on disk does this Region relate to? Private Memory Another type of memory is created as Private This memory is not directly mapped to an image from the PE file. For example, a process may use Private Memory for various purposes: Process │ ├── Image-backed Memory │ └── Private Memory ├── Heap ├── Runtime Data ├── Temporary Allocations └── Other Private Allocations Important note: Private Memory ≠ Malware Completely legitimate programs use Private Memory all the time So just seeing Type = Private doesn't prove anything So why is Private Memory important for security analysis? Because we need to examine several features together For example: Private Memory + Executable Permission + Unusual Thread + Unexpected Call Stack Each one can be completely normal on its own But when several Evidences are put together, the Investigation value increases For example, a Region: Private RX` Alone does not say: Malicious But it can be a good point for further investigation The analyst should examine the Context VAD Now we come to VAD Virtual Address Descriptor VAD is one of the internal structures Windows to describe the limits of a Virtual Address Space Process Conceptually: Process Virtual Address Space 0x0000 │ ├── Region A ├── Region B ├── Region C ├── Region D └── ... 0xFFFF VADs help the Kernel manage and describe information regarding the Virtual Memory limits of a Process For example, that a Where is the region and what are its properties Memory Region Not just an Address Suppose you see something like this in Debugger or Memory Analysis: Address: 0x7... Size: 0x... Protection: RX Type: Private If you only see the Address, you will miss a lot of information You should ask a few questions: Where is this Region? ↓ How big is it? ↓ What is its Protection? ↓ Private Is it an Octal or an Image? ↓ What Mapping or Allocation does it belong to? ↓ What Threads are accessing or executing it? ↓ What happened to it over time? Here Memory Analysis goes from looking at a few Hex Values ​​to Behavioral Analysis VAD in Memory Forensics In Memory Forensics we can examine the Virtual Memory structures related to the Process Conceptually: Process │ ├── Image │ ├── .text │ └── .data │ ├── DLLs │ ├── Heap │ └── Private Regions Now suppose we find a Region that needs further investigation We can ask: What is the Protection? ↓ Is it Private or Image? ↓ Is it associated with a known Module? ↓ What does its content look like? ↓ Is there a Thread associated with it ↓ What does the Call Stack show? ↓ Has the Region's state changed over time? The important point is that VAD alone does not answer all these questions VAD is one of the sources of evidence in memory analysis Analysis scenario Suppose we have this situation in a memory dump: Process: example.exe Region A Image RX Region B Private RW Region C Private RX Region A looks very normal Because it is related to an image and its permission is expected for executing code

Region B هم میتونه کاملا طبیعی باشه چون Private RW داخل Process ها بسیار رایجه اما Region C: Private RX ارزش بررسی بیشتری پیدا میکنه حالا اطلاعات بیشتری به دست میاریم: Region C ↓ Private ↓ RX ↓ Thread Start Address → داخل Region ↓ Call Stack → غیرمعمول اینجا Region C تبدیل میشه به یک Investigation Target ولی هنوز نمیگیم: Malware Found چون ممکنه توضیح Legitimate دیگه ای وجود داشته باشه مثلا Runtimeها JIT Compilatio، Instrumentation یا بعضی تکنیک‌ های نرم‌افزاری هم میتونن Memory Executable ایجاد کنن پس: Suspicious ≠ Malicious Permission فقط یک تکیه از پازله تا اینجا درباره Permission زیاد صحبت کردیم مثلا: RW RX RWX ولی یک تحلیل درست فقط Permission رو نگاه نمیکنه مثلا این دو Region رو مقایسه کنید: Region A Image RX Known Module Normal Thread Activity و: Region B Private RX Unknown Mapping Unusual Thread Start Unexpected Call Stack هر دو ممکنه Executable باشن ولی Context کاملا متفاوتی دارن پس یک مدل بهتر اینه: Memory Type + Protection + Content + Thread + Call Stack + Module Relationship + Timeline هرچه Evidence ها به یک داستان مشترک اشاره کنن Confidence تحلیل بالاتر میره چرا Timeline مهمه؟ فرض کنید یک Region الان این شکلیه: Private RX فقط با دیدن وضعیت فعلی نمیدونیم قبلش چه اتفاقی افتاده ممکنه اول: RW بوده و بعد Permission اون تغییر کرده باشه یا از اول: RX ایجاد شده باشه یا یک Runtime legitimate اون رو ایجاد کرده باشه پس اگر Telemetry کافی داشته باشیم Timeline میتونه خیلی مهم باشه: Allocation ↓ Memory Write ↓ Protection Change ↓ Thread Creation ↓ Execution این زنجیره از یک Snapshot ساده اطلاعات بیشتری میده از دید Detection حالا میتونیم چند Evidence رو کنار هم بذاریم: Process │ ├── Memory Region │ ├── Private │ └── Executable │ ├── Thread │ └── Start Address → Region │ └── Call Stack └── Unexpected Frames هیچکدوم به تنهایی لزوما نشونه حمله نیست ولی ارتباط بین اونها میتونه برای Detection مهم باشه این دقیقا یکی از ایده‌های اصلی Behavioral Detection هست: یک Event ↓ ممکنه عادی باشه چند Event مرتبط ↓ Context ↓ Behavior از دید ردتیمرا از دید مهاجم هم این قسمت یک درس مهم داره Memory هم Telemetry ایجاد میکنه یعنی این تصور: No File = No Detection اشتباهه چون ممکنه فایل واضحی روی Disk وجود نداشته باشه ولی وضعیت Process و Memory همچنان Evidence ایجاد کنه مدل کامل‌ تر: Disk + Process + Memory + Thread + Call Stack + Timeline ↓ Behavioral Evidence پس درک Memory فقط برای کسی که Detection مینویسه مهم نیست برای Red Team هم مهمه که بدونه کد و Data بعد از ورود به Process چه ردپای قابل مشاهده‌ ای ایجاد میکنن یک نکته مهم درباره Private و Image این دو دسته رو بیش از حد ساده نکنیم در Windows Memory Management نوع یک Region فقط با این جمله تعیین نمیشه که: Private = چیزی که برنامه خودش ساخته Image = چیزی که حتما مستقیم از Disk خونده شده جزئیات Memory Manager Section Objectها Mappingها و نوع Allocation مهم هستن پس وقتی در ابزارهای Forensics یا Debugger چیزی مثل: ` Type = Private میبینید باید دقیقا بدونید اون ابزار بر اساس چه اطلاعاتی این Classification رو انجام داده هدف ما در RE و Memory Forensics این نیست که یک Label رو حفظ کنیم هدف اینه که بدونیم پشت اون Label چه Memory Behavior ای وجود داره API / Syscall ↓ Memory Allocation ↓ Memory Protection ↓ Memory Type ↓ Thread ↓ Call Stack ↓ VAD ↓ Behavioral Analysis سه مفهوم رو فعلا خوب نگه دارید: Image-backed Memory ↓ Memory مرتبط با Image Mapping Private Memory ↓ Memory که به یک Image Mapping معمولی وابسته نیست VAD ↓ ساختارهای Kernel برای توصیف محدوده‌ های Virtual Address Space و مهم‌تر از همه: Private ≠ Malicious Executable ≠ Malicious RW → RX ≠ Malicious چیزی که مهمه ترکیب Evidence ها و Context هست @reverseengine

Private Memory و Image-backed Memory و VAD تا اینجا درباره RW → RX صحبت کردیم این Memory اصلا متعلق به چیه؟ وقتی یک Process رو بررسی میکنید همه Memory هایی که میبینید یک جنس نیستن برای تحلیل Memory-based Behavior باید بدونیم هر Region چطور ساخته شده و چه رابطه‌ای با فایل روی دیسک داره Image-backed Memory وقتی یک PE مثل: notepad.exe ntdll.dll kernel32.dll داخل Process قرار میگیره بخش‌ هایی از Virtual Address Space به یک Image File روی دیسک مرتبط میشن به‌صورت مفهومی: PE File │ ▼ Image Mapping │ ▼ Process Virtual Address Space مثلا یک Executable ممکنه Section هایی مثل این داشته باشه: notepad.exe ├── .text ├── .rdata ├── .data └── ... در Memory این بخش‌ها معمولا به Image مربوط به همان Module مرتبط هستن پس وقتی یک Region از نوع Image باشه یکی از سوال‌های مهم اینه: این Region به کدوم Module روی دیسک مربوطه؟ Private Memory نوع دیگری از Memory به شکل Private ایجاد میشه این Memory مستقیما یک Image Mapping از فایل PE مشخص نیست مثلا Process ممکنه برای موارد مختلف از Private Memory استفاده کنه: Process │ ├── Image-backed Memory │ └── Private Memory ├── Heap ├── Runtime Data ├── Temporary Allocations └── Other Private Allocations نکته مهم: Private Memory ≠ Malware برنامه‌ های کاملا Legitimate هم دائما از Private Memory استفاده میکنن پس صرفا دیدن Type = Private هیچ چیزی رو اثبات نمیکنه پس چرا Private Memory برای تحلیل امنیتی مهمه؟ چون باید چند ویژگی رو کنار هم بررسی کنیم مثلا: Private Memory + Executable Permission + Unusual Thread + Unexpected Call Stack هر کدوم به تنهایی میتونه کاملا طبیعی باشه ولی وقتی چند Evidence کنار هم قرار میگیرن ارزش Investigation بیشتر میشه مثلا یک Region: Private RX` به‌ تنهایی نمیگه: Malicious اما میتونه یک نقطه مناسب برای بررسی بیشتر باشه تحلیلگر باید Context رو بررسی کنه VAD حالا می‌رسیم به VAD Virtual Address Descriptor VAD یکی از ساختارهای داخلی Windows برای توصیف محدوده‌های یک Virtual Address Space Process هست به‌ صورت مفهومی: Process Virtual Address Space 0x0000 │ ├── Region A ├── Region B ├── Region C ├── Region D └── ... 0xFFFF VAD ها به Kernel کمک میکنن اطلاعات مربوط به محدوده‌ های Virtual Memory یک Process رو مدیریت و توصیف کنه مثلا اینکه یک محدوده کجاست و چه ویژگی‌ هایی داره Memory Region فقط یک Address نیست فرض کنید در Debugger یا Memory Analysis چنین چیزی ببینید: Address: 0x7... Size: 0x... Protection: RX Type: Private اگر فقط Address رو ببینید اطلاعات زیادی از دست میدید باید چند تا سوال بپرسید: این Region کجاست؟ ↓ چه اندازه‌ای داره؟ ↓ Protection اون چیه؟ ↓ Private هشت یا Image؟ ↓ به چه Mapping یا Allocation ای مربوطه؟ ↓ چه Thread هایی به اون دسترسی یا اجرای مرتبط دارن؟ ↓ در طول زمان چه اتفاقی برای اون افتاده؟ اینجا Memory Analysis از نگاه کردن به چند Hex Value تبدیل میشه به تحلیل رفتار VAD در Memory Forensics در Memory Forensics میتونیم ساختار های Virtual Memory مربوط به Process رو بررسی کنیم به‌صورت مفهومی: Process │ ├── Image │ ├── .text │ └── .data │ ├── DLLs │ ├── Heap │ └── Private Regions حالا فرض کنید یک Region پیدا کردیم که نیاز به بررسی بیشتری داره میتونیم بپرسیم: Protection چیه؟ ↓ Private هست یا Image? ↓ به Module شناخته‌شده‌ ای مرتبطه؟ ↓ محتواش چه شکلیه؟ ↓ Thread ی با اون ارتباط داره ↓ Call Stack چه چیزی نشون میده؟ ↓ وضعیت Region در طول زمان تغییر کرده؟ نکته مهم اینه که VAD به‌تنهایی جواب همه این سؤال‌ها رو نمیده VAD یکی از منابع Evidence در تحلیل Memory هست یک سناریوی تحلیلی فرض کنید داخل یک Memory Dump این وضعیت رو داریم: Process: example.exe Region A Image RX Region B Private RW Region C Private RX Region A خیلی عادی به نظر میرسه چون به یک Image مربوطه و Permission اون هم برای اجرای Code قابل انتظاره

This is one of the things that can help us find the relationship between classes @reverseengine

Reconstructing Inheritance and Multiple Inheritance in Assembly So far we have learned how to reconstruct an unknown Struct from Memory Accesses and Offsets But when we deal with C++ programs, the matter becomes a bit more complicated Because an Object may not have just a few simple fields We may have: Class ↓ Inheritance ↓ Virtual Functions ↓ vptr ↓ vtable This is where we need to understand from the Assembly: What Class is this Object? What Class does it inherit from? Where are the Virtual Functions And which part of the Object do the different Offsets belong to? Understand Virtual Functions Suppose the Source Code is like this: class Animal { public: virtual void speak(); }; And: class Dog : public Animal { public: void speak() override; }; Because "speak" is a Virtual Function, the Compiler usually considers a Pointer to the Virtual Function Table for this Object We usually call this Pointer: vptr and the table: vtable Conceptually: Object │ ├── vptr │ │ │ ▼ │ vtable │ │ │ ├── speak() │ ├── ... │ └── Other Fields That is, the Object usually has a Pointer that points to the Virtual Function information related to the Dynamic Object type Of course, the exact details of the Layout depend on the Compiler and ABI. What should we look for inside the Assembly? Suppose we see something like this inside the Constructor: mov qword ptr [rcx], offset vtable_Dog Here we have a very important clue That is, the initial value of Object is set: Object + 0x00 ↓ vptr ↓ vtable_Dog So we can say for now: Dog Object +0x00 vptr +0x08 unknown +0x10 unknown We still don't know what the next fields are But at least we know one important thing: +0x00 is probably vptr What exactly is vtable? Very simply, if we want to look at it: vptr │ ▼ vtable │ ├── Function Pointer ├── Function Pointer ├── Function Pointer └── Each entry in the vtable is usually a pointer to a function or part of the Virtual Dispatch mechanism For example, suppose the assembly looks like this:
mov rax, [rcx] mov rax, [rax+0x10] call rax
Let's look at it step by step First: mov rax, [rcx ] That is: RCX ↓ Object ↓ +0x00 ↓ vptr Then:
mov rax, [rax+0x10 ]
That is, an entry at a specific offset is read from the vptr table Then: call rax That is, that value is used for Call So we have a conceptual path: Object ↓ +0x00 ↓ vptr ↓ vtable ↓ Function Pointer ↓ call This is one of the most important patterns in Reverse Engineering C++ programs. How do we find the Virtual Function from this pattern? Suppose we have this Assembly:
mov rax, [rcx] mov rax, [rax+0x18] call rax
We can create this model for now: Object | +0x00 → vptr | ↓ vtable | +0x18 → function pointer But one important point: We should not say quickly: +0x18 = Function X Because we still do not know exactly what Virtual Function Entry is related to We should check the vtable itself and its References Now Inheritance comes in Suppose we have: class Animal { public: virtual void speak(); }; class Dog : public Animal { public: void speak() override; }; Conceptually: Animal ↑ | Dog Dog inherits parts of Animal The Object might look something like this: Dog Object +0x00 vptr +0x08 Animal-related field +0x0C Dog-related field This Layout is not exact and is just a conceptual model Because the actual Layout depends on the Compiler, ABI, Alignment and Inheritance type But the important point is that: Base Class + Derived Class can be present in a single Object We can find the relationship from the Constructor Suppose we have a Constructor: mov qword ptr [rcx], offset vtable_Animal And then in the Constructor we see another class: call Animal::Animal mov qword ptr [rcx], offset vtable_Dog Here we have a very important clue That is, the Constructor related to Dog first initializes the part related to the Base Class and then the Virtual Dispatch state related to Sets the Derived Class Conceptually: Dog Constructor | ├── Animal Constructor | └── Dog-specific initialization

باز سازی Inheritance و Multiple Inheritance در Assembly تا اینجا یاد گرفتیم چطور از روی Memory Access ها و Offset ها یک Struct ناشناخته رو بازسازی کنیم ولی وقتی با برنامه‌ های + +C طرف میشیم قضیه یک مقدار پیچیده‌ تر میشه چون ممکنه یک Object فقط چند Field ساده نداشته باشه ممکنه: Class ↓ Inheritance ↓ Virtual Functions ↓ vptr ↓ vtable داشته باشیم اینجاست که باید از روی Assembly بفهمیم: این Object چه Class ای هست؟ از چه Class ای ارثبری کرده؟ Virtual Function ها کجا قرار دارن؟ و Offset های مختلف مربوط به کدوم بخش Object هستن؟ Virtual Function رو بفهمیم فرض کنید Source Code این شکلی باشه:
class Animal { public: virtual void speak(); }; و: class Dog : public Animal { public: void speak() override; };
چون "speak" یک Virtual Function هست Compiler معمولا برای این Object یک Pointer به جدول Virtual Function ها در نظر میگیره به این Pointer معمولا میگیم: vptr و جدول: vtable به شکل مفهومی: Object │ ├── vptr │ │ │ ▼ │ vtable │ │ │ ├── speak() │ ├── ... │ └── Other Fields یعنی Object معمولا یک Pointer داره که به اطلاعات Virtual Functionهای مربوط به نوع Dynamic Object اشاره میکنه البته جزئیات دقیق Layout به Compiler و ABI وابسته است. داخل Assembly دنبال چه چیزی بگردیم؟ فرض کنید داخل Constructor چنین چیزی ببینیم: mov qword ptr [rcx], offset vtable_Dog اینجا یک سرنخ خیلی مهم داریم یعنی مقدار ابتدای Object تنظیم شده: Object + 0x00 ↓ vptr ↓ vtable_Dog پس میتونیم فعلا بگیم: Dog Object +0x00 vptr +0x08 unknown +0x10 unknown هنوز نمیدونیم Field های بعدی چی هستن ولی حداقل یک چیز مهم فهمیدیم: +0x00 احتمالا vptr است vtable دقیقا چیه؟ خیلی ساده اگر بخوایم نگاهش کنیم: vptr │ ▼ vtable │ ├── Function Pointer ├── Function Pointer ├── Function Pointer └── هر Entry در vtable معمولا یک Pointer به یک Function یا بخشی از مکانیزم Virtual Dispatch هست مثلا فرض کنید Assembly این شکلی باشه:
mov rax, [rcx] mov rax, [rax+0x10] call rax
بیایم مرحله‌ به‌ مرحله نگاه کنیم اول:
mov rax, [rcx
] یعنی: RCX ↓ Object ↓ +0x00 ↓ vptr بعد:
mov rax, [rax+0x10
] یعنی از جدول مربوط به vptr یک Entry در Offset مشخص خونده شده بعد: call rax یعنی اون مقدار برای Call استفاده شده پس یک مسیر مفهومی داریم: Object ↓ +0x00 ↓ vptr ↓ vtable ↓ Function Pointer ↓ call این یکی از الگوهای خیلی مهم در Reverse Engineering برنامه‌های C++ هست. از روی این الگو چطور Virtual Function رو پیدا کنیم؟ فرض کنید این Assembly رو داریم:
mov rax, [rcx] mov rax, [rax+0x18] call rax
میتونیم فعلا این مدل رو بسازیم: Object | +0x00 → vptr | ↓ vtable | +0x18 → function pointer اما یک نکته مهم: نباید سریع بگیم: +0x18 = Function X چون هنوز نمی‌دونیم Entry دقیقا مربوط به چه Virtual Function ای هست باید خود vtable و Reference های مربوط به اون رو بررسی کنیم حالا Inheritance وارد میشه فرض کنید داریم:
class Animal { public: virtual void speak(); }; class Dog : public Animal { public: void speak() override; };
از نظر مفهومی: Animal ↑ | Dog Dog بخش‌هایی از Animal رو به ارث میبره ممکنه Object چیزی شبیه این داشته باشه: Dog Object +0x00 vptr +0x08 Animal-related field +0x0C Dog-related field این Layout دقیق نیست و صرفا یک مدل مفهومیه چون Layout واقعی به Compiler، ABI، Alignment و نوع Inheritance بستگی داره ولی نکته مهم اینه که: Base Class + Derived Class میتونن در یک Object واحد حضور داشته باشن از روی Constructor می‌تونیم رابطه رو پیدا کنیم فرض کن یک Constructor داریم:
mov qword ptr [rcx], offset vtable_Animal
و بعد در Constructor کلاس دیگه ای میبینیم: call Animal::Animal mov qword ptr [rcx], offset vtable_Dog اینجا یک سرنخ خیلی مهم داریم یعنی Constructor مربوط به Dog ابتدا بخش مربوط به Base Class رو مقدار دهی میکنه و بعد وضعیت Virtual Dispatch مربوط به Derived Class رو تنظیم میکنه به شکل مفهومی: Dog Constructor | ├── Animal Constructor | └── Dog-specific initialization این یکی از چیزهایی هست که میتونه به ما کمک کنه رابطه بین Class ها رو پیدا کنیم @reverseengine

ClickLock Stealer: Paste Once, Lose Everything https://www.group-ib.com/blog/clicklock-stealer-macos-malware