Initial commit

This commit is contained in:
pygrum
2024-06-19 17:05:13 +01:00
commit ad10dd4c1e
10 changed files with 6218 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
hash.py
cmake-build-debug
*.exe
.idea
+11
View File
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.28)
project(Gimmick C)
set(CMAKE_C_STANDARD 23)
add_executable(Gimmick gimmick.c
gimmick.h
example/main.c
)
target_include_directories(Gimmick PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
+11
View File
@@ -0,0 +1,11 @@
.PHONY: build
CC64 = x86_64-w64-mingw32-gcc
SRC = example/main.c gimmick.c
FLAGS = -s -Os
OUT = -o gimmick.exe
build:
$(CC64) $(FLAGS) $(SRC) $(OUT) -DDEBUG
python3 crypt.py -f gimmick.exe -o gimmick.exe -s .vmp0 .rodata
+75
View File
@@ -0,0 +1,75 @@
# Gimmick
A thread-safe, section-based payload obfuscation technique.
## How it works
Each section is treated as a shared resource by the application.
To access a section, it needs to be decrypted.
To decrypt a section, the following conditions are satisfied:
1. There are no other threads currently encrypting or decrypting the section simultaneously
To encrypt a section, the following conditions are satisfied:
1. There are no other threads currently encrypting or decrypting the section simultaneously
2. There are no 'references' to the section
## Extra features
- PIC (Position Independent Code) compatible library, with custom GetModuleHandle and GetProcAddress implementations
## Limitations
- 64-bit only (for now)
- Sections used by stdlib cannot be encrypted. Modify and compile with -nostdlib to have more standard section names available for use
- If the executable is to be loaded by the OS, only sections that are untouched by Windows loader can be used to store data.
This technique is best used with an rDLL or Shellcode.
- All sections are marked as encrypted on initialisation.
Gimmick attempts to encrypt / decrypt any section referenced by the API, as there is no current implementation for
*initial* section encryption state detection. Only functions and variables marked with the `SEC` macro should be called,
provided that the section will be encrypted with `crypt.py`. This shouldn't be an issue **provided that you only target the
sections that you want to encrypt.**
## Run
An example multithreaded application is set up for POC purposes. It is compiled with MinGW gcc.
1. `make build`
2. `./gimmick.exe`
### Output
```
--- Starting threads
[*][.vmp0] attempting to decrypt section
[*][.vmp0] decrypting section
[+][.vmp0] done! releasing mutex and restoring protection.
[+][.vmp0] data is now available for use.
[*][00007ff6963e9000] -- executing callee function
[*][.rodata] attempting to decrypt section
[*][.rodata] decrypting section
[*][.vmp0] attempting to decrypt section
[!][.vmp0] section is already decrypted
[*][00007ff6963e9000] -- executing callee function
[+][.rodata] done! releasing mutex and restoring protection.
[+][.rodata] data is now available for use.
[*][.rodata] attempting to decrypt section
[!][.rodata] section is already decrypted
[*][.rodata] attempting to re-encrypt section
[!][.rodata] section is in use - no re-encryption was performed
[*][00007ff6963e9000] -- exited with code 0xdead
[*][.vmp0] attempting to re-encrypt section
[!][.vmp0] section is in use - no re-encryption was performed
[*][.rodata] attempting to re-encrypt section
[*][.rodata] re-encrypting section
[+][.rodata] successfully re-encrypted section
[*][00007ff6963e9000] -- exited with code 0xdead
[*][.vmp0] attempting to re-encrypt section
[*][.vmp0] re-encrypting section
[+][.vmp0] successfully re-encrypted section
```
## Usage
1. Add `gimmick.c`, `gimmick.h` and `ntdll.h` to your project
2. Assign objects to desired sections with the `SEC` macro, separating different types (e.g. functions and variables)
3. Initialise Gimmick context (`GkInitCtx`)
4. Use `GkGet` (+`GkRelease`), `GkRun`, or `GkRunEx` to run functions or access variables assigned to encrypted sections
5. Compile the file
6. Choose sections that contain data accessed with Gimmick to encrypt (`crypt.py`) and encrypt them with the same key used
for Gimmick's context (edit in script)
7. Run your executable
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import pefile
import argparse
from Crypto.Cipher import ARC4
RC4_KEY = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
if __name__ in '__main__':
try:
parser = argparse.ArgumentParser(description='Encrypts PE Sections.')
parser.add_argument('-f', required=True, help='Path to the source executable', type=str)
parser.add_argument('-o', required=True, help='Path to store the output executable', type=str)
parser.add_argument('-s', nargs='+', required=True, help='Sections to encrypt')
option = parser.parse_args()
exe = pefile.PE(option.f)
for section in exe.sections:
name = section.Name.rstrip(b'\x00').decode('utf-8')
for s in option.s:
if name == s:
print("[*] encrypting", name+"...", end=" ")
section_data = section.get_data()
# need to recreate cipher due to RC4 limitations
cipher = ARC4.new(RC4_KEY)
new_section_data = cipher.encrypt(section_data)
exe.set_data_bytes(section.PointerToRawData, new_section_data)
print("done!")
exe.write(option.o)
except Exception as e:
print('[!] error: {}'.format(e))
+58
View File
@@ -0,0 +1,58 @@
#include "../gimmick.h"
#ifdef DEBUG
#include <stdio.h>
#endif
typedef struct {
PCHAR greeting;
} greet, *pgreet;
// encrypted data. initialise strings with [] to prevent them from being moved
SEC( rodata ) CHAR hello[] = "hello from gimmick";
SEC( rodata ) CHAR goodbye[] = "goodbye from gimmick";
SEC( vmp0 ) DWORD __stdcall Message(PGK_CONTEXT Context, PVOID Args)
{
pgreet greet = Args;
PCHAR greeting = greet->greeting;
// get encrypted data
GkGet( Context, greeting );
MessageBoxA(NULL, greeting, NULL, 0);
// release data
GkRelease( Context, greeting );
return 0xDEAD;
}
int main()
{
GKSTATUS Status = GK_SUCCESS;
GK_CONTEXT Context = {};
DWORD ThreadId = 0;
UCHAR Key[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
if ((Status = GkInitContext(GkGetModuleHandle(0), &Context, Key)) != GK_SUCCESS) {
return Status;
}
greet greetH = { .greeting = hello };
greet greetG = { .greeting = goodbye };
// gkrun arguments
GK_ARGS ArgsHello = { .Context = &Context, .Function = Message, .Args = &greetH };
GK_ARGS ArgsGoodbye = { .Context = &Context, .Function = Message, .Args = &greetG };
#ifdef DEBUG
printf("--- Starting threads\n");
#endif
HANDLE ThreadH = CreateThread(NULL, 0, GkRunEx, &ArgsHello, 0, &ThreadId);
HANDLE ThreadG = CreateThread(NULL, 0, GkRunEx, &ArgsGoodbye, 0, &ThreadId);
WaitForSingleObject(ThreadH, INFINITE);
WaitForSingleObject(ThreadG, INFINITE);
return GkFreeSectionContext(&Context);
}
+328
View File
@@ -0,0 +1,328 @@
#include "gimmick.h"
#ifdef DEBUG
#include <stdio.h>
#endif
GKSTATUS GkInitContext( LPVOID BaseAddress, PGK_CONTEXT Context, PUCHAR Key )
{
PIMAGE_NT_HEADERS NtHeaders = NULL;
PIMAGE_FILE_HEADER FileHeader = NULL;
PGK_SECTION_CONTEXT SectionContext, LastSectionContext = NULL;
LPVOID Section = NULL;
// Initialize encryption key
Context->EncryptionKey.Length = 16;
Context->EncryptionKey.MaximumLength = 16;
Context->EncryptionKey.Buffer = Key;
// load dependencies
Context->Ntdll = GkGetModuleHandle(HASH_NTDLL);
Context->Kernel32 = GkGetModuleHandle(HASH_KERNEL32);
WIN_PROC(Context, Ntdll, LdrGetProcedureAddressForCaller, HASH_LDRGETPROCEDUREADDRESSFORCALLER);
WIN_PROC(Context, Ntdll, RtlAnsiStringToUnicodeString, HASH_RTLANSISTRINGTOUNICODESTRING);
WIN_PROC(Context, Ntdll, LdrLoadDll, HASH_LDRLOADDLL);
WIN_PROC(Context, Kernel32, CreateMutexA, HASH_CREATEMUTEXA);
WIN_PROC(Context, Kernel32, ReleaseMutex, HASH_RELEASEMUTEX);
WIN_PROC(Context, Kernel32, VirtualAlloc, HASH_VIRTUALALLOC);
WIN_PROC(Context, Kernel32, VirtualProtect, HASH_VIRTUALPROTECT);
WIN_PROC(Context, Kernel32, VirtualFree, HASH_VIRTUALFREE);
WIN_PROC(Context, Kernel32, WaitForSingleObject, HASH_WAITFORSINGLEOBJECT);
// TODO: Obfuscate as you please----------
CHAR AdvApi[] = { 'A', 'D', 'V', 'A', 'P', 'I', '3', '2', '\0'};
ANSI_STRING AdvapiAnsi = { .Buffer = AdvApi, .Length = 8, .MaximumLength = 9 };
UNICODE_STRING AdvapiUnicode = {};
if (Context->RtlAnsiStringToUnicodeString(&AdvapiUnicode, &AdvapiAnsi, TRUE) != STATUS_SUCCESS)
return GK_ERROR_USTRING_ALLOC_FAILED;
// ---------------------------------------
// load SystemFunction032
Context->LdrLoadDll(NULL, NULL, &AdvapiUnicode, &Context->Advapi32);
WIN_PROC(Context, Advapi32, SystemFunction032, HASH_SYSTEMFUNCTION032);
NtHeaders = (PIMAGE_NT_HEADERS)((CHAR*)BaseAddress + ((PIMAGE_DOS_HEADER)BaseAddress)->e_lfanew);
if (NtHeaders->Signature != IMAGE_NT_SIGNATURE)
return GK_ERROR_BAD_NT_SIGNATURE;
FileHeader = &NtHeaders->FileHeader;
// get first section
Section = (CHAR*)&NtHeaders->OptionalHeader + FileHeader->SizeOfOptionalHeader;
for (WORD i = 0; i < FileHeader->NumberOfSections; i++)
{
PIMAGE_SECTION_HEADER SectionHeader = Section;
SectionContext = (PGK_SECTION_CONTEXT)Context->VirtualAlloc(
NULL,
sizeof(GK_SECTION_CONTEXT),
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
if (SectionContext == NULL) {
return GK_ERROR_SECTION_CTX_ALLOC_FAILED;
}
SectionContext->Section.Buffer = (PUCHAR)BaseAddress + SectionHeader->VirtualAddress; // assumes sections are loaded correctly in memory
SectionContext->Section.Length = SectionHeader->Misc.VirtualSize;
SectionContext->Section.MaximumLength = SectionContext->Section.Length;
SectionContext->Encrypted = TRUE; // (prod: TRUE) we assume it has been encrypted statically
SectionContext->Mutex = Context->CreateMutexA(NULL, FALSE, NULL);
SectionContext->Name = SectionHeader->Name;
SectionContext->Next = NULL;
// Attach to linked list
if (LastSectionContext)
LastSectionContext->Next = SectionContext;
else // otherwise, set as first section in section context list
Context->SectionContexts = SectionContext;
LastSectionContext = SectionContext;
// set to next section header
Section = (CHAR*)Section + sizeof(IMAGE_SECTION_HEADER);
}
return GK_SUCCESS;
}
GKSTATUS GkFreeSectionContext( PGK_CONTEXT Context )
{
DWORD status = GK_SUCCESS;
PGK_SECTION_CONTEXT SectionContext = Context->SectionContexts;
do
{
PGK_SECTION_CONTEXT Next = SectionContext->Next;
if (!Context->VirtualFree(SectionContext, 0, MEM_RELEASE))
status = GK_ERROR_SECTION_FREE_FAILED;
SectionContext = Next;
} while (SectionContext != NULL);
return status;
}
GKSTATUS GkGet( PGK_CONTEXT Context, PVOID Data)
{
// find section that data lives in
PGK_SECTION_CONTEXT SectionContext = NULL;
for (SectionContext = Context->SectionContexts; SectionContext != NULL; SectionContext = SectionContext->Next) {
PBUFFER Section = &SectionContext->Section;
// if it is within the range of the section
if ((DWORD_PTR)Data >= (DWORD_PTR)Section->Buffer &&
(DWORD_PTR)Data <= (DWORD_PTR)Section->Buffer + Section->Length) {
/*wait for in-progress crypt*/
Context->WaitForSingleObject(SectionContext->Mutex, INFINITE);
/* if encryption just happened, then hold mutex and:
* 1. Make RW
* 2. Decrypt
* 3. Restore protection
* 4. Update encryption status
* 5. Release mutex
*/
#ifdef DEBUG
printf("[*][%s] attempting to decrypt section\n", SectionContext->Name);
#endif
if (SectionContext->Encrypted) {
#ifdef DEBUG
printf("[*][%s] decrypting section\n", SectionContext->Name);
#endif
// rw
Context->VirtualProtect(Section->Buffer, Section->Length, PAGE_READWRITE, &SectionContext->OriginalProtect);
// decrypt
if (Context->SystemFunction032((PBUFFER)SectionContext, &Context->EncryptionKey) != STATUS_SUCCESS) {
return GK_ERROR_CRYPT_FAILED;
}
#ifdef DEBUG
printf("[+][%s] done! releasing mutex and restoring protection.\n", SectionContext->Name);
#endif
// original
DWORD op;
Context->VirtualProtect(Section->Buffer, Section->Length, SectionContext->OriginalProtect, &op);
// notify
SectionContext->Encrypted = FALSE;
#ifdef DEBUG
printf("[+][%s] data is now available for use.\n", SectionContext->Name);
#endif
}
#ifdef DEBUG
else {
printf("[!][%s] section is already decrypted\n", SectionContext->Name);
}
#endif
// update before release so that encryptor knows that there's now an accessor
SectionContext->Accessors += 1;
Context->ReleaseMutex(SectionContext->Mutex);
return GK_SUCCESS;
}
}
return GK_ERROR_ADDRESS_SECTION_NOT_FOUND;
}
GKSTATUS GkRelease( PGK_CONTEXT Context, PVOID Data )
{
// find section that data lives in
PGK_SECTION_CONTEXT SectionContext = NULL;
for (SectionContext = Context->SectionContexts; SectionContext != NULL; SectionContext = SectionContext->Next) {
PBUFFER Section = &SectionContext->Section;
// if it is within the range of the section
if ((DWORD_PTR)Data >= (DWORD_PTR)Section->Buffer &&
(DWORD_PTR)Data <= (DWORD_PTR)Section->Buffer + Section->Length) {
// acquire before checking accessors, effectively wait for an accessor to register themselves
Context->WaitForSingleObject(SectionContext->Mutex, INFINITE);
SectionContext->Accessors -= 1; // decrement before acquiry in case another thread acquires and checks for accessors before us
#ifdef DEBUG
printf("[*][%s] attempting to re-encrypt section\n", SectionContext->Name);
#endif
if (!SectionContext->Accessors) {
/*
* 1. Acquire mutex
* 2. Make RW
* 3. Encrypt
* 4. Restore original protection
* 5. Update encryption status
*/
// rw
#ifdef DEBUG
printf("[*][%s] re-encrypting section\n", SectionContext->Name);
#endif
Context->VirtualProtect(Section->Buffer, Section->Length, PAGE_READWRITE, &SectionContext->OriginalProtect);
// encrypt
if (Context->SystemFunction032((PBUFFER)SectionContext, &Context->EncryptionKey) != STATUS_SUCCESS) {
return GK_ERROR_CRYPT_FAILED;
}
// original
DWORD op; // discard rx
Context->VirtualProtect(Section->Buffer, Section->Length, SectionContext->OriginalProtect, &op);
// notify
SectionContext->Encrypted = TRUE;
#ifdef DEBUG
printf("[+][%s] successfully re-encrypted section\n", SectionContext->Name);
#endif
}
#ifdef DEBUG
else {
printf("[!][%s] section is in use - no re-encryption was performed\n", SectionContext->Name);
}
#endif
// release after notify so that decryptor has the correct encryption bool
Context->ReleaseMutex(SectionContext->Mutex);
return GK_SUCCESS;
}
}
return GK_ERROR_ADDRESS_SECTION_NOT_FOUND;
}
DWORD WINAPI GkRunEx( LPVOID Args )
{
PGK_ARGS GkArgs = Args;
return GkRun(GkArgs->Context, GkArgs->Function, GkArgs->Args, &GkArgs->ReturnValue);
}
GKSTATUS GkRun( PGK_CONTEXT Context, LPGK_ROUTINE Function, LPVOID Args, PDWORD ReturnValue )
{
typedef struct {
PCHAR greeting;
} greet, *pgreet;
GKSTATUS Status = GK_SUCCESS;
if ((Status = GkGet(Context, Function)) != GK_SUCCESS)
return Status;
#ifdef DEBUG
printf("[*][%p] -- executing callee function\n", Function);
#endif
*ReturnValue = Function(Context, Args);
#ifdef DEBUG
printf("[*][%p] -- exited with code 0x%.4x\n", Function, *ReturnValue);
#endif
if ((Status = GkRelease(Context, Function)) != GK_SUCCESS)
return Status;
return Status;
}
SIZE_T StrLenA(PCHAR String )
{
SIZE_T length = 0;
while (*String++) {
length++;
}
return length;
}
HANDLE GkGetModuleHandle( DWORD Hash )
{
PPEB Peb = NtCurrentPeb();
HANDLE hModule = NULL;
LIST_ENTRY ModuleList;
PPEB_LDR_DATA LdrData = Peb->Ldr;
if (LdrData) {
ModuleList = LdrData->InLoadOrderModuleList;
PLDR_DATA_TABLE_ENTRY CurrentModule = *((PLDR_DATA_TABLE_ENTRY*)(&ModuleList));
BOOL First = TRUE;
for (;
CurrentModule != NULL && CurrentModule->DllBase != NULL;
CurrentModule = (PLDR_DATA_TABLE_ENTRY)CurrentModule->InLoadOrderLinks.Flink
) {
if (First && Hash == 0) {
// first module is always the base of current process
hModule = CurrentModule->DllBase;
break;
}
First = FALSE;
if (CurrentModule->BaseDllName.Buffer == NULL)
continue;
if (HASH_CMPW(CurrentModule->BaseDllName, Hash)) {
hModule = CurrentModule->DllBase;
break;
}
}
}
return hModule;
}
FARPROC GkGetProcAddress( PGK_CONTEXT Context, HANDLE hModule, DWORD Hash )
{
if (BAD_MODULE(hModule))
return NULL;
PIMAGE_NT_HEADERS NtHeaders = NT_HEADERS_PTR(hModule);
PIMAGE_DATA_DIRECTORY ExportDirectory = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (ExportDirectory == NULL)
return NULL;
// can simply offset from imagebase since image (including export section) is already loaded properly
PIMAGE_EXPORT_DIRECTORY ExportData = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)hModule + ExportDirectory->VirtualAddress);
if (ExportData) {
// get absolutes from rva
DWORD* NameTable = (DWORD*)((CHAR*)hModule + ExportData->AddressOfNames); // table of RVAs
WORD* OrdinalTable = (WORD*)((CHAR*)hModule + ExportData->AddressOfNameOrdinals);
DWORD* ProcTable = (DWORD*)((CHAR*)hModule + ExportData->AddressOfFunctions); // table of RVAs
for (SIZE_T i = 0; i < ExportData->NumberOfNames; i++) {
LPSTR CurrentName = (LPSTR)((BYTE*)hModule + NameTable[i]); // convert from RVA
WORD Index = OrdinalTable[i];
FARPROC ProcAddress = (FARPROC)((BYTE*)hModule + ProcTable[Index]); // convert from RVA
if (HASH_CMPA(CurrentName, Hash)) {
if (FORWARDER( ExportData , ExportDirectory->Size, ProcAddress )) {
ANSI_STRING String = { .Buffer = CurrentName, .Length = StrLenA(CurrentName) };
String.MaximumLength = String.Length + 1;
if (Context->LdrGetProcedureAddressForCaller) {
PVOID LdrProcAddress, CallbackAddress = NULL;
if ( NT_SUCCESS(Context->LdrGetProcedureAddressForCaller(
hModule,
&String,
0,
&LdrProcAddress,
0,
&CallbackAddress))) {
return LdrProcAddress;
}
return NULL;
}
}
return ProcAddress;
}
}
}
return NULL;
}
+181
View File
@@ -0,0 +1,181 @@
#ifndef GIMMICK_H
#define GIMMICK_H
#include "ntdll.h"
#define FORWARDER( ex, s, p ) (DWORD_PTR)p >= (DWORD_PTR)ex && \
(DWORD_PTR)p < (DWORD_PTR)ex + s
#define NT_HEADERS_PTR( x ) (PIMAGE_NT_HEADERS)((PCHAR)x + ((PIMAGE_DOS_HEADER)x)->e_lfanew)
#define BAD_MODULE( x ) ((PIMAGE_DOS_HEADER)x)->e_magic != IMAGE_DOS_SIGNATURE
#define TO_LOWERCASE(c) (c = (c <= 'Z' && c >= 'A') ? c + ' ': c)
#define WIN_FUNC( x ) __typeof__(x)*x;
#define WIN_PROC( c, m, f, h ) c->f = (__typeof__(f)*)GkGetProcAddress(c, c->m, h)
typedef DWORD GKSTATUS;
#define GK_SUCCESS 0x0
#define GK_ERROR_BAD_NT_SIGNATURE 0x100
#define GK_ERROR_SECTION_CTX_ALLOC_FAILED 0x101
#define GK_ERROR_USTRING_ALLOC_FAILED 0x102
#define GK_ERROR_CRYPT_FAILED 0x103
#define GK_ERROR_ADDRESS_SECTION_NOT_FOUND 0x104
#define GK_ERROR_SECTION_FREE_FAILED 0x105
/* MODULE HASHES -------------------------------------------------*/
#define HASH_KERNEL32 0x3bbc195
#define HASH_NTDLL 0x11c9b04d
/* PROC HASHES ---------------------------------------------------*/
#define HASH_LDRGETPROCEDUREADDRESSFORCALLER 0x2bdda210
#define HASH_RTLANSISTRINGTOUNICODESTRING 0x427c583a
#define HASH_VIRTUALALLOC 0x58dacbd7
#define HASH_VIRTUALPROTECT 0x8b9ebdcd
#define HASH_VIRTUALFREE 0x1238036e
#define HASH_CREATEMUTEXA 0xd8b1f26d
#define HASH_RELEASEMUTEX 0x790e3959
#define HASH_LDRLOADDLL 0x23a21f83
#define HASH_SYSTEMFUNCTION032 0xd3a21dc5
#define HASH_WAITFORSINGLEOBJECT 0xda18e23a
#ifndef HASH
#define HASH 5381
#endif
/*
* djb2 hash for wstring, case insensitive modification: http://www.cse.yorku.ca/~oz/hash.html
*/
static DWORD DJB2W(LPWSTR String, DWORD Length)
{
DWORD Hash = HASH;
for (INT i = 0; i < Length; i++) {
CHAR c = *((CHAR*)String + i);
TO_LOWERCASE(c);
Hash = ((Hash << 5) + Hash) + c;
}
return Hash;
}
/*
* djb2 hash, case insensitive modification: http://www.cse.yorku.ca/~oz/hash.html
*/
static DWORD DJB2A(LPSTR String)
{
CHAR c;
DWORD Hash = HASH;
while ((c = *String++) != 0) {
TO_LOWERCASE(c);
Hash = ((Hash << 5) + Hash) + c;
}
return Hash;
}
// Hash comparison for UNICODE_STRING
#define HASH_CMPW( x, h ) DJB2W((LPWSTR)x.Buffer, x.Length) == h
#define HASH_CMPA( x, h ) DJB2A((unsigned char*)x) == h
// buffer struct used for encryption in SystemFunction032.
typedef struct _BUFFER {
DWORD Length;
DWORD MaximumLength;
PUCHAR Buffer;
} BUFFER, *PBUFFER;
// unexported win32 functions and symbols--------------------------------
NTSTATUS
SystemFunction032
(
PBUFFER data,
PBUFFER key
);
NTSYSAPI
NTSTATUS
NTAPI
LdrGetProcedureAddressForCaller(
IN HMODULE ModuleHandle,
IN PANSI_STRING FunctionName OPTIONAL,
IN WORD Oridinal OPTIONAL,
OUT PVOID *FunctionAddress,
IN BOOL bValue,
IN PVOID *CallbackAddress
);
// ---------------------------------------------------------------------
/*
* stores section context for syncronisation.
* Implements buffer used for RC4 encryption by SystemFunction032.
* 1. Allow access when section is unencrypted
* 2. Do not encrypt if there are other threads accessing the section
* 3. Do not access while a section is being encrypted or decrypted (mutex is used for insurance)
* 4. Decrypt a section when access is required and it is encrypted (mutex is used for insurance)
*/
typedef struct _GK_SECTION_CONTEXT {
BUFFER Section;
DWORD OriginalProtect;
PCHAR Name;
DWORD Accessors;
HANDLE Mutex; // held during encryption and decryption.
BOOL Encrypted; // changed BEFORE crypt is done
struct _GK_SECTION_CONTEXT* Next;
} GK_SECTION_CONTEXT, *PGK_SECTION_CONTEXT;
// Stores information about each section on initialisation, as well as helper functions
typedef struct _GK_CONTEXT {
PGK_SECTION_CONTEXT SectionContexts;
BUFFER EncryptionKey;
HANDLE Ntdll;
HANDLE Kernel32;
HANDLE Advapi32;
WIN_FUNC( LdrGetProcedureAddressForCaller )
WIN_FUNC( CreateMutexA )
WIN_FUNC( ReleaseMutex )
WIN_FUNC( VirtualAlloc )
WIN_FUNC( VirtualProtect )
WIN_FUNC( VirtualFree )
WIN_FUNC( SystemFunction032 )
WIN_FUNC( LdrLoadDll )
WIN_FUNC( RtlAnsiStringToUnicodeString )
WIN_FUNC( WaitForSingleObject )
} GK_CONTEXT, *PGK_CONTEXT;
// Used to pass information to the GkRunner thread.
typedef struct _GK_ARGS {
PGK_CONTEXT Context;
LPVOID Function;
PVOID Args;
DWORD ReturnValue;
} GK_ARGS, *PGK_ARGS;
// Function signature for Gimmick callees. Context is passed in case a callee wants to access encrypted data.
typedef DWORD (__stdcall *LPGK_ROUTINE) (
IN PGK_CONTEXT Context,
IN LPVOID Args
);
// Initialises Gimmick context
GKSTATUS GkInitContext( LPVOID BaseAddress, PGK_CONTEXT Context, PUCHAR Key );
// Frees Gimmick section context that was allocated with GkInitContext
GKSTATUS GkFreeSectionContext( PGK_CONTEXT Context );
// Retrieve handle to a module from PEB loader data
HANDLE GkGetModuleHandle( DWORD Hash );
// Get process address from loaded module data
FARPROC GkGetProcAddress( PGK_CONTEXT Context, HANDLE hModule, DWORD Hash );
// Request data from an encrypted section. Gimmick ensures the section is protected while the data is being used
GKSTATUS GkGet( PGK_CONTEXT Context, PVOID Data );
// Signal that data accessed by GkGet is no longer in use
GKSTATUS GkRelease( PGK_CONTEXT Context, HANDLE Data );
// Runs the function with the provided arguments (e.g. a struct pointer) and returns its return value. decrypting the section
// it exists in if necesssary
GKSTATUS GkRun( PGK_CONTEXT Context, LPGK_ROUTINE Function, PVOID Args, OUT PDWORD ReturnValue );
// Thread routine to run a function asyncronously. Pass a pointer to the GK_ARGS struct
DWORD WINAPI GkRunEx( LPVOID Args );
#define SEC( s ) __attribute__( ( section("." #s ) ) )
#endif //GIMMICK_H
+5513
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
pefile
pycryptodome