• XSS.stack #1 – первый литературный журнал от юзеров форума

Хэширование строк в MASM64 + C

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
В продолжение темы atavism. Сугубо для первертов. С помощью ml.64 и его CTL (Compile Time Language) реализовал PoC для этого алгоритма хэширования строк с доступом к хешам из С или С++ модулей. Реализовать PRNG для констант const1, hash в масмовском CTL не удалось. Не нашел какой-либо информации по поддержке @Date, или каких-нибудь других подходящих функций. Может быть есть сведущие люди, кто знает как решить проблему. Компилировалось все это дело в Visual Studio.

Код:
comment "types & libs, all in one place"
include \masm32\include64\masm64rt.inc
comment "constant definitions"
comment "_HASH_API is a main struct which stores hash values"

_HASH_API         struct
    self  qword        ?   ;stores address of the struct
    hash1 dword        ?
    hash2 dword        ?
    hash3 dword        ?
    hash4 dword        ?
_HASH_API        ends

PHASH_API typedef PTR _HASH_API
HASH_API  typedef      _HASH_API

.const
;nothing here yet

;data section starts here
.data

comment "TODO: Create a PRNG macro for hash value if it's possible"
comment "This macro produce hash from API name"
HashAPIName macro apiStr

const1 = 0AB10F29Fh         ;change this value if you want
                            ;to generate new hashes

hash = 35h                  ;same as previous
    ;hash loop, obviously
    forc char, apiStr
        hash = hash + ((hash * const1 + '&char') and 0FFFFFFh)
    endm
    exitm <hash>
endm

comment "Apply macro to the needed API calls"
dw_CreateThreadHash        dword HashAPIName(<CreateThread>)
dw_CreateProcessHash       dword HashAPIName(<CreateProcess>)
dw_Privet                  dword HashAPIName(<Privet>)
dw_MessageBoxHash          dword HashAPIName(<MessageBox>)
;add more if you need

;initialize struct fields
p_HashApiTable _HASH_API {0,0,0,0,0}

;code section starts here
.code
comment "This function fills the table with our hashes, which we will reference later from c/cpp code"
public FillAPIHashTable
FillAPIHashTable proc
STACKFRAME
    lea        rdx, [p_HashApiTable]
    mov        p_HashApiTable.self, rdx
    mov        edx, dw_CreateThreadHash
    mov        p_HashApiTable.hash1, edx
    mov        edx, dw_CreateProcessHash
    mov        p_HashApiTable.hash2, edx
    mov        edx, dw_Privet
    mov        p_HashApiTable.hash3, edx
    mov        edx, dw_MessageBoxHash
    mov        p_HashApiTable.hash4, edx
    mov        rax, p_HashApiTable.self
    ret
FillAPIHashTable endp


C:
#include <Windows.h>
#include <stdio.h>
#include <winnt.h>
#include <WinDef.h>

//prototypes
PDWORD GetFunctionAddressByHash(char* library, DWORD hash);
extern PHASH_API FillAPIHashTable();

//i don't understand how to get access to the same struct from asm file
//so I just dublicate it ;D
typedef struct _HASH_API
{
    UINT64 self;
    UINT32 dw_CreateThreadHash;
    UINT32 dw_CreateProcessHash;
    UINT32 dw_Privet;
    UINT32 dw_MessageBoxHash;
} HASH_API, *PHASH_API;

typedef HANDLE (__stdcall* CUSTOMCREATETHREAD)(
    LPSECURITY_ATTRIBUTES   lpThreadAttributes,
    SIZE_T                  dwStackSize,
    LPTHREAD_START_ROUTINE  lpStartAddress,
    __drv_aliasesMem LPVOID lpParameter,
    DWORD                   dwCreationFlags,
    LPDWORD                 lpThreadId
    );
}

//credits: https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware
void main()
{
    //i changed original code here and add stack strings to avoid strings search
    char k32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2','\0'};
    char u32[] = { 'u', 's', 'e', 'r', '3', '2', '\0' };
    PHASH_API hapi;
    hapi = FillAPIHashTable();
    PDWORD functionAddress = GetFunctionAddressByHash(k32, hapi->dw_CreateThreadHash);
    CUSTOMCREATETHREAD CreateThread = (CUSTOMCREATETHREAD)functionAddress;
    DWORD tid = 0;

    //call CreateThread
    HANDLE th = CreateThread(NULL, NULL, NULL, NULL, NULL, &tid);
    return 1;
}

DWORD GetHashFromString(char* string)
{
    size_t stringLength = strnlen_s(string, 50);
    DWORD hash = 0x35;

    for (size_t i = 0; i < stringLength; i++)
    {
        hash += (hash * 0xab10f29f + string[i]) & 0xffffff;
    }
    //printf("%s: 0x00%x\n", string, hash);

    return hash;
}

PDWORD GetFunctionAddressByHash(char* library, DWORD hash)
{
    PDWORD functionAddress = (PDWORD)0;

    //get base address of the module in which our exported function of interest resides (kernel32 in the case of CreateThread)
    HMODULE hmlibraryBase = LoadLibraryA(library);
    
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hmlibraryBase;
    PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)hmlibraryBase + dosHeader->e_lfanew);

    DWORD_PTR exportDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

    PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)hmlibraryBase + exportDirectoryRVA);

    //get RVAs to exported function related information
    PDWORD addresOfFunctionsRVA = (PDWORD)((DWORD_PTR)hmlibraryBase + imageExportDirectory->AddressOfFunctions);
    PDWORD addressOfNamesRVA = (PDWORD)((DWORD_PTR)hmlibraryBase + imageExportDirectory->AddressOfNames);
    PWORD addressOfNameOrdinalsRVA = (PWORD)((DWORD_PTR)hmlibraryBase + imageExportDirectory->AddressOfNameOrdinals);

    //iterate through exported functions, calculate their hashes and check if any of them match our hash of 0x00544e304 (CreateThread)
    //if yes, get its virtual memory address (this is where CreateThread function resides in memory of our process)
    for (DWORD i = 0; i < imageExportDirectory->NumberOfFunctions; i++)
    {
        DWORD functionNameRVA = addressOfNamesRVA[i];
        DWORD_PTR functionNameVA = (DWORD_PTR)hmlibraryBase + functionNameRVA;
        char* functionName = (char*)functionNameVA;
        DWORD_PTR functionAddressRVA = 0;

        //calculate hash for this exported function
        DWORD functionNameHash = GetHashFromString(functionName);

        //if hash for CreateThread is found, resolve the function address
        if (functionNameHash == hash)
        {
            functionAddressRVA = addresOfFunctionsRVA[addressOfNameOrdinalsRVA[i]];
            functionAddress = (PDWORD)((DWORD_PTR)hmlibraryBase + functionAddressRVA);
            printf("%s : 0x%x : %p\n", functionName, functionNameHash, functionAddress);
            return functionAddress;
        }
    }
}
 
Пожалуйста, обратите внимание, что пользователь заблокирован
В fasm есть директива file, можно сгенерировать файл случайных данных и читать из него.
Вот пример для Linux, где есть системный ГСЧ. Сам fasm напрямую из "/dev/random" не читает из-за lseek, потому создаётся промежуточный файл.
Код:
format ELF64 executable 3

segment readable executable

file_offset = 0;

macro write_random_byte {

    mov    eax, 0a0000h

    ; генерируем инструкцию mov al, imm8
    db    0b0h
    file     'rnd.dat':file_offset,1

   ; конвертируем байт в символьное представление и выводим
    mov    ah, al
    shr    al, 4
    and    eax, 0ff0f0fh
    add    al, '0'
    cmp    al, '9'
    jbe    @f
    add    al, 'A'-('9'+ 1)
@@:    add    ah, '0'
    cmp    ah, '9'
    jbe    @f
    add    ah, 'A'-('9'+1)
@@:
    push    rax
    lea    rsi, [rsp]
    mov    rdx, 3
    mov    edi,1        ; STDOUT
    mov    eax,1        ; sys_write
    syscall
    pop    rax

    file_offset = file_offset + 1
}

entry $
    write_random_byte
    write_random_byte
    write_random_byte
    write_random_byte

    xor    edi,edi     ; exit code 0
    mov    eax,60        ; sys_exit
    syscall

$ dd bs=1 count=4 if=/dev/random of=rnd.dat && fasm rnd.asm && ./rnd
4+0 записей получено
4+0 записей отправлено
4 байта скопировано, 0,000300623 s, 13,3 kB/s
flat assembler version 1.73.12 (16384 kilobytes memory)
2 passes, 373 bytes.
B8
0E
1B
53

$ dd bs=1 count=4 if=/dev/random of=rnd.dat && fasm rnd.asm && ./rnd
4+0 записей получено
4+0 записей отправлено
4 байта скопировано, 0,000317365 s, 12,6 kB/s
flat assembler version 1.73.12 (16384 kilobytes memory)
2 passes, 373 bytes.
EF
EB
9A
B9
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
В fasm есть директива file, можно сгенерировать файл случайных данных и читать из него.
Эмм, зачем? В фасмовских макросах есть изменяемые глобальные переменные? Просто сделай любой линейный конгруэнтный ГПСЧ, проинициализируй его текущим временем и генерируй сколько угодно псевдо-случайных чисел, изменяя его состояние (глобальную переменную). https://ru.m.wikipedia.org/wiki/Лин...стандартные библиотеки различных компиляторов.
 
Эмм, зачем? В фасмовских макросах есть изменяемые глобальные переменные? Просто сделай любой линейный конгруэнтный ГПСЧ, проинициализируй его текущим временем и генерируй сколько угодно псевдо-случайных чисел, изменяя его состояние (глобальную переменную).
Переменные есть, а про текущее время не помню, опять из файла читать? :) Я и так перетрудился, пока писал конверсию, которая нужна только для демонстрации, ещё и генератор сочинять. Тем более, что /dev/random это ГСЧ, а ГПСЧ /dev/urandom.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
а про текущее время не помню, опять из файла читать?
Там было что-то типа %T - время в виде юникс-таймстемпа, или что-то такое.

ещё и генератор сочинять
Его не нужно сочинять, просто берешь параметры A и С, какие тебе понравились, в вики есть таблица, какие из них где используются, каждое следующее псевдослучайное число равно предыдущиму умноженному на A и сложенному с C.

Тем более, что /dev/random это ГСЧ, а ГПСЧ /dev/urandom.
Это всё ГПСЧ, в современных компах нет ГСЧ. Их преимущество в том, что они зерно для генерации получают из всякого ядерного бреда (зерно образно случайное, но далее последовательность уже псевдослучайная), поэтому их в принципе можно использовать для генерации ключей в криптографии. И потом эти файлы могут еще быть недоступны на старте системы, когда ты будешь пытаться их читать, тк не успели собрать достаточно энтропии для инициализации последовательности.
 
Там было что-то типа %T - время в виде юникс-таймстемпа, или что-то такое.
Точно, нашёл %t.

Его не нужно сочинять, просто берешь параметры A и С, какие тебе понравились, в вики есть таблица, какие из них где используются, каждое следующее псевдослучайное число равно предыдущиму умноженному на A и сложенному с C.
Просто было, когда я взял готовый /dev/random, который посмотрели тысячиглаз, и лишь некоторые построили конспирологию про происки АНБ. Кто-то может сгенерировать "одноразовый блокнот" любимым Пихоном, или найти программку, которая собирает энтропию с мышки. А смотреть Википедию, выбирать таблицу, это, наверное, познавательно, но не так просто.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Кстати, недавно ковырялся в x86 ISA и наткнулся на инструкции RDSEED и RDRAND, теоретически это аппаратный ГПСЧ. Не уверен, что это применимо в "боевых" условиях, однако все же можно на них поглядеть.
Это рантайм, к слову в ядре используется в функции ExGenRandom. Зерно надо генерировать на этапе компиляции. В x86 масме были переменные @Date, , которые использовали системные дату и время, но ml64 с ними уже почему-то не работает.
 
Можно передавать макросы через параметр /D.
Например
Код:
$cval=Get-Random | % ToString X8
$hval=Get-Random | % ToString X8
Start-Process -Wait -FilePath 'ml64.exe' -ArgumentList ('/c /D "CONST1={0}h" /D "HASHVAL={1}h" /Ta"ap.asm" /Fo"ap.obj"' -f $cval, $hval)
Start-Process -Wait -FilePath 'cl.exe' -ArgumentList ('/c /D "CONST1=0x{0}" /D "HASHVAL=0x{1}" /Tc"main.c" /Fo"main.obj"' -f $cval, $hval)
Start-Process -Wait -FilePath 'link.exe' -ArgumentList ('/nologo /OUT:"api-hashing.exe" /DYNAMICBASE "kernel32.lib" /MACHINE:X64 /SUBSYSTEM:CONSOLE "ap.obj" "main.obj"')
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Можно передавать макросы через параметр /D.
Например
Код:
$cval=Get-Random | % ToString X8
$hval=Get-Random | % ToString X8
Start-Process -Wait -FilePath 'ml64.exe' -ArgumentList ('/c /D "CONST1={0}h" /D "HASHVAL={1}h" /Ta"ap.asm" /Fo"ap.obj"' -f $cval, $hval)
Start-Process -Wait -FilePath 'cl.exe' -ArgumentList ('/c /D "CONST1=0x{0}" /D "HASHVAL=0x{1}" /Tc"main.c" /Fo"main.obj"' -f $cval, $hval)
Start-Process -Wait -FilePath 'link.exe' -ArgumentList ('/nologo /OUT:"api-hashing.exe" /DYNAMICBASE "kernel32.lib" /MACHINE:X64 /SUBSYSTEM:CONSOLE "ap.obj" "main.obj"')

Дошли руки, чтобы посмотреть. Однозначно лайк.
Ниже листинги с символьной инфой под идой.

Код:
Первый .obj

.data:00000000000001D9     dw_CreateThreadHash dd 84D8191h        
.data:00000000000001D9                                            
.data:00000000000001DD     dw_CreateProcessHash dd 6EECB18h      
.data:00000000000001DD                                            
.data:00000000000001E1     dw_Privet  dd 5C49335h            
.data:00000000000001E1                                            
.data:00000000000001E5     dw_MessageBoxHash dd 6018093h          
.data:00000000000001E9     p_HashApiTable  dq 0                  


Второй .obj

.data:00000000000001D9     dw_CreateThreadHash dd 6C85159h        
.data:00000000000001D9                                            
.data:00000000000001DD     dw_CreateProcessHash dd 7B47820h      
.data:00000000000001DD                                            
.data:00000000000001E1     dw_Privet  dd 719964Dh            
.data:00000000000001E1                                            
.data:00000000000001E5     dw_MessageBoxHash dd 4FD9E3Bh          
.data:00000000000001E9     p_HashApiTable  dq 0

Случайно не знаете, существует ли современная документация по масму аналогичная той, что была для x86 "MASM61PROGUIDE", а то msdn не богат на примеры, а прогайд в некоторых аспектах для x64 не актуален.
 
Последнее редактирование:
Наклепал простой compile-time рандомизатор.
Код:
__SEED = 0
CTRND MACRO
    LOCAL _M, _H, _S
    IF __SEED EQ 0
        _M SUBSTR @Time, 1, 2
        _H SUBSTR @Time, 4, 2
        _S SUBSTR @Time, 7, 2
        __SEED = 0FFh - _M - _H - _S
    ENDIF
    __SEED = (__SEED * 41A7h + 9C682h) MOD 0FFFFFFFFh
    EXITM <__SEED>
ENDM
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх