Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ring0-c0d3-br34k3r/inline_hooking
Let's dive deeper into some key aspects of Inline Hooking in C++
https://github.com/ring0-c0d3-br34k3r/inline_hooking
api-hooking hooking inline-hooking
Last synced: 2 months ago
JSON representation
Let's dive deeper into some key aspects of Inline Hooking in C++
- Host: GitHub
- URL: https://github.com/ring0-c0d3-br34k3r/inline_hooking
- Owner: ring0-c0d3-br34k3r
- Created: 2024-01-23T01:37:05.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2024-01-23T05:18:50.000Z (12 months ago)
- Last Synced: 2024-09-13T21:16:21.520Z (4 months ago)
- Topics: api-hooking, hooking, inline-hooking
- Homepage:
- Size: 353 KB
- Stars: 5
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Inline_Hooking
![iInline Hooking](https://github.com/0xp17j8/Inline_Hooking/assets/111459558/ea776262-095a-437e-bc0f-74986a8ef591)
________________________________________________________________________________________________________________________________________________
# Let's dive deeper into some key aspects of Inline Hooking in C++
- Function Prologue and Epilogue :
Prologue: This is the part of the function that sets up the stack frame. Understand how the stack is initialized and local variables are allocated.
Epilogue: Deals with cleaning up the stack frame. Recognize how the function prepares for its return.- Memory Permissions :
Inline Hooking often involves modifying memory permissions to make the target function writable. You'll typically use functions like VirtualProtect on
Windows to achieve this. Be cautious not to violate memory protection and handle permissions responsibly.- Detouring Techniques :
Explore different methods for redirecting the control flow to your hook. Common techniques include using a relative or absolute jump instruction, or even a
trampoline function.- Function Parameters :
If the hooked function takes parameters, understand how they are passed and accessed. Your hook function should handle these parameters correctly to avoid
unexpected behavior.- Thread Safety :
Consider the thread safety of your hook. If the target function is called by multiple threads simultaneously, ensure your hook can handle this scenario
without causing race conditions.- Persistence :
Depending on your use case, you might want the hook to persist across sessions or restarts. Explore techniques for making your changes endure beyond a
single execution of the target application.- Anti-Debugging Measures :
Some applications implement anti-debugging measures. Be aware of this and implement countermeasures if necessary to ensure the stability of your hook in a
debugging environment.- Dynamic Address Resolution :
Consider how you'll dynamically resolve addresses of functions you want to hook, especially if they reside in external libraries. Techniques like manual DLL
loading or using function pointers can be employed.- Compatibility :
Ensure your inline hook is compatible with different operating systems and compiler versions. Factors like calling conventions and memory layout may vary.- Documentation and Resources :
Refer to documentation for the compiler and platform you're working with. Additionally, explore online resources, forums, and community discussions related
to inline hooking for valuable insights.________________________________________________________________________________________________________________________________________________
# Inline Hooking in C++!
_ Inline Hooking is a technique used for intercepting function calls dynamically. Here's a Step By Step guide to get you going :- Understand the Basics : Inline hooking involves redirecting the flow of a program by replacing the beginning of a function with your own code. Get
comfortable with function calls, memory management, and assembly language basics.- Choose a Target : Decide which function you want to hook. It's often helpful to choose a function that's part of a library or system DLL.
- Learn Assembly : Inline Hooking often involves working at the assembly level. Familiarize yourself with the assembly language, especially related to your
target platform (x86 or x64).- Study the Target Function: Analyze the assembly code of the function you want to hook. Understand its prologue and epilogue, and identify the bytes you'll
be modifying.- Write Your Hook : Create a function that will replace the original function. Ensure it does what you need and doesn't break the existing flow.
- Memory Permissions : Understand and modify the memory permissions to make the target function writable.
- Replace the Function Start : Overwrite the initial bytes of the target function with a jump to your hook function. Make sure to save the overwritten bytes
and restore them when needed.- Call the Original Function : Within your hook function, call the original function to maintain the expected behavior. You can do this by jumping back to the
original code after executing your hook.- Error Handling : Implement error handling to gracefully handle unexpected situations.
- Test Thoroughly: Test your inline hook in a controlled environment. Ensure it behaves as expected and doesn't cause unintended consequences
________________________________________________________________________________________________________________________________________________
# Let's consider a simple scenario where we want to hook the function in a Windows application.
_ This example assumes a basic understanding of C++ and Windows API.MessageBoxA :- Choose a Target :
Target Function : (A for ANSI version)MessageBoxA- Study the Target Function:
Check the function signature and its assembly code.
Identify the prologue and epilogue. Here's a simplified version: ```assembly MessageBoxA_Prologue: ; Prologue setup hereMessageBoxA : ; Function code here
MessageBoxA_Epilogue : ; Epilogue cleanup here ```
- Write Your Hook :
Create a function that will replace . Let's call it :
```cpp int HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
// Your hook logic here // ...MessageBoxAHookedMessageBoxACall the original function to maintain expected behavior
int result = MessageBoxA(hWnd, lpText, lpCaption, uType);Additional logic after the original function call
...return result;
} ```- Memory Permissions :
Ensure the target function's memory is writable. Use :
VirtualProtectcpp DWORD oldProtect; VirtualProtect(MessageBoxA, hookSize, PAGE_EXECUTE_READWRITE, &oldProtect);- Replace the Function Start :
Overwrite the beginning of with a jump to your hook :
```cpp const int hookSize = 5; // Size of the jump instruction DWORD oldProtect;
VirtualProtect(MessageBoxA, hookSize, PAGE_EXECUTE_READWRITE, &oldProtect);MessageBoxAWrite the jump instruction (E9 is the opcode for a relative jump) :
(BYTE)MessageBoxA = 0xE9;Calculate the relative address :
DWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)MessageBoxA - 5;Write the relative address :
(DWORD)((DWORD)MessageBoxA + 1) = relativeAddress;VirtualProtect(MessageBoxA, hookSize, oldProtect, &oldProtect); ```
- Call the Original Function :
Inside, call the original function :
HookedMessageBoxAcpp int result = MessageBoxA(hWnd, lpText, lpCaption, uType);- Test Thoroughly :
Compile and run your application. Trigger a call to and observe the behavior.MessageBoxA
This is a basic example, and in real-world scenarios, you might encounter additional challenges. Always be mindful of the application's architecture, handle
different cases, and test extensively.________________________________________________________________________________________________________________________________________________
# let's go through a more detailed example with full source code for inline hooking.
_ In this example, we'll hook the MessageBoxA function in a Windows application using a detouring technique.#include
- Define the original MessageBoxA function signature
using MessageBoxAFunc = int(WINAPI*)(HWND, LPCSTR, LPCSTR, UINT);
- Function pointer to store the address of the original MessageBoxA
MessageBoxAFunc originalMessageBoxA = nullptr;
- Our hook function
int HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{- Your hook logic here
std::cout << "MessageBoxA Hooked!\n";
std::cout << "Text: " << lpText << "\n";
std::cout << "Caption: " << lpCaption << "\n";
- Call the original function to maintain expected behavior
int result = originalMessageBoxA(hWnd, lpText, lpCaption, uType);- Additional logic after the original function call
std::cout << "MessageBoxA Returned: " << result << "\n";return result;
}- Function to perform the inline hook
void InstallHook() {- Get the address of the original MessageBoxA
originalMessageBoxA = (MessageBoxAFunc)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");- Ensure the target function's memory is writable
DWORD oldProtect;
VirtualProtect(originalMessageBoxA, sizeof(int), PAGE_EXECUTE_READWRITE, &oldProtect);- Create the detour (jump) to our hook function
DWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)originalMessageBoxA - 5;
*(BYTE*)originalMessageBoxA = 0xE9; // Jump opcode
*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;- Restore original memory protection
VirtualProtect(originalMessageBoxA, sizeof(int), oldProtect, &oldProtect);
}- Entry point of the program
int main() {- Install the hook
InstallHook();- Trigger a call to MessageBoxA
MessageBoxA(nullptr, "Hello, Inline Hooking!", "Hook Example", MB_OK);- Unhook (optional, depending on your use case)
- ... (restore the original bytes and cleanup)
return 0;
}# This example involves :
- Installing the Hook: The InstallHook function gets the address of the original MessageBoxA, makes the memory writable, and creates a detour to our HookedMessageBoxA function.
- Hook Function: HookedMessageBoxA is our custom function that gets called instead of the original MessageBoxA. It prints information and then calls the original function to maintain expected behavior.
- Testing: In the main function, we trigger a call to MessageBoxA to see our hook in action.
=
________________________________________________________________________________________________________________________________________________# let's break down the provided C++ code for inline hooking step by step :
#include
- Define the original MessageBoxA function signature
using MessageBoxAFunc = int(WINAPI*)(HWND, LPCSTR, LPCSTR, UINT);
- Function pointer to store the address of the original MessageBoxA
MessageBoxAFunc originalMessageBoxA = nullptr;
- Include Headers
#include : This header includes declarations for Windows API functions and types. It's necessary for interacting with the Windows operating system.
#include : This header is for input and output operations and is used here for printing messages to the console.
_ Note : i find problem in github; the headers is hiden, so dont forget him "windows.h and iostream"- Function Signature and Pointer
This line "using MessageBoxAFunc = int(WINAPI*)(HWND, LPCSTR, LPCSTR, UINT);" defines a type alias MessageBoxAFunc for a function pointer type. It represents the signature of the original MessageBoxA function.
This Line "MessageBoxAFunc originalMessageBoxA = nullptr;" Declares a function pointer variable originalMessageBoxA to store the address of the original MessageBoxA function.- Our hook function
int HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
- Your hook logic here
std::cout << "MessageBoxA Hooked!\n";- Hook Function
This "int HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)" is your custom hook function that will be called instead of the original MessageBoxA.
Inside this function, you can implement your own logic. In this example, it simply prints a message indicating that MessageBoxA has been hooked.- Call the original function to maintain expected behavior
int result = originalMessageBoxA(hWnd, lpText, lpCaption, uType);- Additional logic after the original function call
std::cout << "MessageBoxA Returned: " << result << "\n";
return result;
}- Calling the Original Function
This Line "int result = originalMessageBoxA(hWnd, lpText, lpCaption, uType);" Calls the original MessageBoxA function to maintain the expected behavior.
After the original function call, additional logic can be added. In this case, it prints the return value of the original MessageBoxA to the console.- Function to perform the inline hook
void InstallHook() {- Get the address of the original MessageBoxA
originalMessageBoxA = (MessageBoxAFunc)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");- Ensure the target function's memory is writable
DWORD oldProtect;
VirtualProtect(originalMessageBoxA, sizeof(int), PAGE_EXECUTE_READWRITE, &oldProtect);- Save the original bytes
memcpy(originalBytes, originalMessageBoxA, sizeof(int));- Create the detour (jump) to our hook function
DWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)originalMessageBoxA - 5;- Jump opcode
*(BYTE*)originalMessageBoxA = 0xE9;
*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;- Restore original memory protection
VirtualProtect(originalMessageBoxA, sizeof(int), oldProtect, &oldProtect);
}- Installing the Hook :
This function "void InstallHook()" is responsible for installing the hook.
this "originalMessageBoxA = (MessageBoxAFunc)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");" Gets the address of the original MessageBoxA function
from the "user32.dll" module.
This "VirtualProtect(originalMessageBoxA, sizeof(int), PAGE_EXECUTE_READWRITE, &oldProtect);" Makes the memory of the target function writable.This "memcpy(originalBytes, originalMessageBoxA, sizeof(int));" Original Byte Storing him from "originalMessageBoxA" to "originalBytes" for return in unhooking function
This " DWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)originalMessageBoxA - 5;" Calculates the relative address for the jump instruction. up thats
functions We Will take 5 bit for put the JMP
- See : https://www.malwaretech.com/wp-content/uploads/2015/01/CodeFlow.png
This "*(BYTE*)originalMessageBoxA = 0xE9;" Writes the jump opcode to the beginning of the original functionThis "*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;" Writes the relative address for the jump instruction.
This Again "VirtualProtect(originalMessageBoxA, sizeof(int), oldProtect, &oldProtect);" For Restores the original memory protection.
- Function to uninstall the hook and restore the original function
BYTE originalBytes[sizeof(int)];
void UninstallHook() {- Ensure the target function's memory is writable
DWORD oldProtect;
VirtualProtect(originalMessageBoxA, sizeof(int), PAGE_EXECUTE_READWRITE, &oldProtect);- Restore the original bytes of the target function
memcpy(originalMessageBoxA, &originalBytes, sizeof(int));- Restore original memory protection
VirtualProtect(originalMessageBoxA, sizeof(int), oldProtect, &oldProtect);
}- Uninstall the Hook Function :
This function "void UninstallHook()" is responsible for uninstalling the hook and restoring the original functionality.
This "DWORD oldProtect;" Declares a variable to store the original memory protection of the target function.
This "VirtualProtect(originalMessageBoxA, sizeof(int), PAGE_EXECUTE_READWRITE, &oldProtect);" Makes the memory of the target function writable.
This "memcpy(originalMessageBoxA, &originalBytes, sizeof(int));" Restores the original bytes of the target function using the saved bytes (originalBytes).
This "VirtualProtect(originalMessageBoxA, sizeof(int), oldProtect, &oldProtect);" Restores the original memory protection.
- Saving Original Bytes :
Before installing the hook, you need to save the original bytes of the target function. Add the following line to the InstallHook function :
Save the original bytes of the target function :memcpy(&originalBytes, originalMessageBoxA, sizeof(int));
This line "memcpy(&originalBytes, originalMessageBoxA, sizeof(int));" should be placed after obtaining the address of the original MessageBoxA and before modifying
the memory.
- Entry point of the program
int main() {
// Install the hook
InstallHook();// Trigger a call to MessageBoxA
MessageBoxA(nullptr, "Hello, Inline Hooking!", "Hook Example", MB_OK);// Unhook (optional, depending on your use case)
UninstallHook();return 0;
}- Main Function :
The main function is the entry point of the program.
InstallHook();: Calls the InstallHook function to install the hook.
MessageBoxA(nullptr, "Hello, Inline Hooking!", "Hook Example", MB_OK);: Triggers a call to the hooked MessageBoxA.
The program concludes with the return 0;.
This example demonstrates the basic process of hooking a function using a detouring technique. It's important to note that inline hooking involves low-level
manipulation and should be done responsibly and ethically. If you have specific questions about parts of the code or if there's a particular aspect you'd like more
details on, feel free to ask!- Unhooking in the Main Function :
In the main function, after triggering the call to MessageBoxA, the UninstallHook function is called to remove the hook and restore the original functionality.
Keep in mind that unhooking is optional and depends on your specific requirements. In some cases, hooks may need to be kept in place for the duration of the
program's execution. In other cases, unhooking may be necessary to ensure that the program's behavior returns to normal.________________________________________________________________________________________________________________________________________________
- These Are Just appetizers to Know how Inline Hooking is Doing..
- We Will explain each line here in depth just relax Honey
________________________________________________________________________________________________________________________________________________# Lets Dive Deeper into each Part Of the InstallHook Function, using Examples To illustrate The concepts :
- Store original bytes for uninstallation
BYTE originalBytes[sizeof(int)];
- Function to perform the inline hook
void InstallHook() {
- Get the address of the original MessageBoxA
originalMessageBoxA = (MessageBoxAFunc)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");- Get the Address of the Original Function :
In this part, the GetProcAddress function is used to obtain the address of the original MessageBoxA function from the "user32.dll" module.
originalMessageBoxA = (MessageBoxAFunc)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
For example, let's say the address of the original MessageBoxA is 0x7FFE1234. Now originalMessageBoxA points to this address.- Ensure the target function's memory is writable
DWORD oldProtect;
VirtualProtect(originalMessageBoxA, sizeof(int), PAGE_EXECUTE_READWRITE, &oldProtect);- Ensure Writability of the Target Function :
Here, the VirtualProtect function is used to modify the memory protection of the original MessageBoxA function. This is necessary to make the memory writable so
that the hook can be installed.
VirtualProtect(originalMessageBoxA, sizeof(int), PAGE_EXECUTE_READWRITE, &oldProtect);
Assume that originalMessageBoxA now points to the address 0x7FFE1234. This function ensures that the memory at 0x7FFE1234 is marked as writable.- Save the original bytes
memcpy(originalBytes, originalMessageBoxA, sizeof(int));- Save the Original Bytes :
The memcpy function is used to copy the original bytes of the target function (in this case, MessageBoxA) to a buffer (originalBytes). This step is crucial for
later uninstalling the hook.
memcpy(originalBytes, originalMessageBoxA, sizeof(int));
Suppose the first few bytes of MessageBoxA at 0x7FFE1234 are 0x55 0x8B 0xEC 0x83 0xEC. The originalBytes buffer now contains these bytes.
- Create the detour (jump) to our hook functionDWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)originalMessageBoxA - 5;
*(BYTE*)originalMessageBoxA = 0xE9; // Jump opcode
*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;- Create a Detour (Jump) to the Hook Function :
This part involves creating a jump instruction (detour) at the beginning of the original MessageBoxA function. The jump redirects the execution flow to the custom
hook function (HookedMessageBoxA).
DWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)originalMessageBoxA - 5;
*(BYTE*)originalMessageBoxA = 0xE9; // Jump opcode
*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;
The relativeAddress is calculated by finding the offset between HookedMessageBoxA and originalMessageBoxA and subtracting 5 (size of the jump instruction). This
offset is then written into the original function, effectively redirecting it.- let's combine This Steps with an Example :
- Assume The Following :
originalMessageBoxA initially points to 0x7FFE1234.
The first few bytes of MessageBoxA at this address are 0x55 0x8B 0xEC 0x83 0xEC.
- After Executing The InstallHook Function :
The memory protection at 0x7FFE1234 is changed to writable.
The original bytes (0x55 0x8B 0xEC 0x83 0xEC) are saved in the originalBytes buffer.
A jump instruction (0xE9) is written at 0x7FFE1234, redirecting the execution to the HookedMessageBoxA function.- ||
- ||
- ||- __
# Let me Explain to u The Part about Creation a Detour (Jump) to the Hook Function :
- Create the detour (jump) to our hook functionDWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)originalMessageBoxA - 5;
*(BYTE*)originalMessageBoxA = 0xE9; // Jump opcode
*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;- Calculate Relative Address :
This line "DWORD relativeAddress = (DWORD)HookedMessageBoxA - (DWORD)originalMessageBoxA - 5;" calculates the relative address that represents the offset between
the address of the original MessageBoxA function (originalMessageBoxA) and the address of the custom hook function (HookedMessageBoxA). The -5 is subtracted to
account for the size of the jump instruction (5 bytes).- For Example :
if HookedMessageBoxA is at address 0x7FFF5678 and originalMessageBoxA is at address 0x7FFE1234, the relative address would be 0x7FFF5678 - 0x7FFE1234 - 5 =
0x1440F39.- Write the Jump Opcode :
This line "*(BYTE*)originalMessageBoxA = 0xE9;" writes the jump opcode (0xE9) to the beginning of the original MessageBoxA function. The jump opcode instructs the
processor to transfer control to a different location.In binary, 0xE9 represents the opcode for an unconditional jump.
See : https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm- Write the Relative Address into the Jump Instruction :
This line "*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;" writes the calculated relative address into the jump instruction. The +1 is used to skip
the jump opcode and write the address in the correct position.Continuing with the example, if the relative address is 0x1440F39, this line sets the bytes at 0x7FFE1235, 0x7FFE1236, 0x7FFE1237, and 0x7FFE1238 to the
corresponding bytes of the relative address.# Summary :
these three lines of code effectively replace the initial few bytes of the original MessageBoxA function with a jump instruction that redirects the
program's flow to the custom hook function (HookedMessageBoxA). This is a fundamental step in the process of inline hooking, where the original function is
diverted to a custom function while preserving the ability to later restore the original functionality.________________________________________________________________________________________________________________________________________________
# let's explained this part of code deeply "*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;"
# And break Down its functionality with an example :*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;
- Understanding the Cast and Pointer Dereferencing :
(DWORD)originalMessageBoxA : This part casts originalMessageBoxA to a DWORD (Double Word), essentially treating the address as an unsigned 32-bit integer.
*(DWORD*) : This indicates that we are working with a pointer to a DWORD. The * symbol denotes pointer dereferencing.
- Adjusting the Position :
(DWORD)originalMessageBoxA + 1 : This adds 1 to the casted address. In pointer arithmetic, adding 1 doesn't mean moving 1 byte; instead, it moves to the next 32
bits (4 bytes).The addition of 1 is crucial here because we want to skip the first byte of the jump instruction (0xE9). We are aiming to write the address in the correct position
following the jump opcode.- Setting the Value :
= relativeAddress : This assigns the calculated relative address to the memory location specified by the adjusted pointer.
Let's illustrate this with an example:- Assume :
originalMessageBoxA initially points to the address 0x7FFE1234.
The calculated relative address is 0x1440F39.- break down the process :
(DWORD)originalMessageBoxA + 1 :
This becomes 0x7FFE1234 + 1, resulting in 0x7FFE1235.
*(DWORD*)((DWORD)originalMessageBoxA + 1) = relativeAddress;:The value 0x1440F39 is Written To The Memory location 0x7FFE1235, 0x7FFE1236, 0x7FFE1237, and 0x7FFE1238.
- In terms of memory layout : Address | Data -------------|------------- 0x7FFE1234 | 0xE9 ; Original jump opcode 0x7FFE1235 | 0x1440F39 ; Relative address
0x7FFE1239 | ... ; Rest of the original bytesThis setup ensures that the jump instruction now correctly points to the custom hook function, and the address is placed in the right position, considering the
opcode at the beginning.___________________________________________________________________________________________________________________
# Conclusions :
We Have Explained Each piece of Code Simply. if you are having trouble understanding.
See MinHook and Detours Hook techniques
https://github.com/TsudaKageyu/minhook
https://github.com/microsoft/Detours
https://github.com/0xp17j8/Hooking-Detours# Some References :
https://blog.securehat.co.uk/process-injection/manually-implementing-inline-function-hooking
https://unprotect.it/technique/inline-hooking/
https://cplusplus.com/forum/windows/139414/
https://www.ired.team/offensive-security/code-injection-process-injection/how-to-hook-windows-api-using-c++
https://www.malwaretech.com/2015/01/inline-hooking-for-programmers-part-2.html
https://blog.nettitude.com/uk/windows-inline-function-hooking
___________________________________________________________________________________________________________________
- These Are The Most Important points you should understand. And I think u got it xaxaxaxa.
- Happy Hacking.
___________________________________________________________________________________________________________________