improve this code
/* == SIMPLE ASM-x32 HOOKING ENGINE v1.2 ==
DESC:
    - automatic nops insertion for original instruction size alignment
    - automatic hook jump addresses calculation and non-relative code relocation
    - multiple serial hooks installation, already patched code detection
    - simple automatic memory management and pages allocation
    - user-defined size hook memory and code sections
    - hook user-defined sections initialization (zero data & place nops in code sections)
    - hook user-program installation
    - page protection saving before installation process and restoring after
    - checks if memory page at the modifiable regions is accessible
    - returns original trampoline content if return pointer is not NULL (v1.2)
I/O (I=input, O=output):
(I) install_offset - relative starting address (to process base) for hook installation
(I) trampoline_sz - size in bytes of original code to swap (replace with jump instr, fill with nops leftovers and place original instr at the start of hook code section)
(I) code_sz - size of user code section
(I) *p_asm_prog - pointer to user asm-prog byte array, if NULL - no prog installation
(I) data_sz - size of user data section
(I/O) *p_data - if not NULL then returns pointer to first (lower addr) user data byte (void* for type support)
(I/O) *p_orig_trampoline - if not NULL then fills pointed buffer with original trampoline content (new v1.2)
(O) - returns pointer to first user code byte
[!] WARNING: only 32-bit processes and only instruction swapping without relative addresses supported at the moment
*/
BYTE *aip_mbapi_asm_install_hook(DWORD install_offset, BYTE trampoline_sz, WORD code_sz, BYTE *p_asm_prog, WORD data_sz, void *p_data, BYTE *p_orig_trampoline)
{
    static DWORD mem_page_pos = 0, mem_page_start = 0;
    static WORD hook_no = 0;
    const BYTE jump_instr_sz = 1 + sizeof(DWORD);
    BYTE p_jump_instr[jump_instr_sz];
    p_jump_instr[0] = 0xE9; //jump instr
    ++hook_no;

    AIP_CORE_PRINT(AIP_CORE_STRM_DEBUG, "New hook params: offset=0x%08lX, code_sz=%u, data_sz=%u", install_offset, code_sz, data_sz);

    BYTE *p_install_addr = (BYTE *)(aip_mbapi.base_address + install_offset);
    aip_mbapi_asm_check_mem_is_accessible(p_install_addr, trampoline_sz); //target mem is ok?
    DWORD oldProtect, newProtect;
    AIP_CORE_CHECK_ERROR(!VirtualProtect(p_install_addr, trampoline_sz, PAGE_EXECUTE_READWRITE, &oldProtect), AIP_EC_WINAPI_PAGE_PROTECTION_CHANGE_FAIL);
    if (oldProtect == PAGE_EXECUTE) //we need read access after hook installation
        oldProtect = PAGE_EXECUTE_READ;

    if (p_orig_trampoline != NULL) // new v1.2 - return original trampoline data
        memcpy(p_orig_trampoline, p_install_addr, trampoline_sz);

    BYTE *p_custom_code_addr = NULL, *p_custom_data_addr = NULL;
    if (p_install_addr[0] == p_jump_instr[0]) { //already patched?
        DWORD *rel_addr_to_old_hook = (DWORD *)(p_install_addr + 1);
        DWORD old_hook_addr = ((DWORD)(*rel_addr_to_old_hook)) + aip_mbapi.base_address + install_offset + jump_instr_sz; //give me old hook address!
        p_custom_data_addr = (BYTE *)(old_hook_addr - data_sz);
        if (p_data != NULL)
            memcpy(p_data, &p_custom_data_addr, sizeof(void *));
        p_custom_code_addr = (BYTE *)old_hook_addr;
        AIP_CORE_PRINT(AIP_CORE_STRM_DEBUG, "Detected installed hook#%u at address: 0x%08lX (p_data:0x%p, p_code:0x%p)", hook_no, old_hook_addr, p_custom_data_addr, p_custom_code_addr);
        VirtualProtect(p_install_addr, trampoline_sz, oldProtect, &newProtect);
        return p_custom_code_addr; //return pointer to first nop
    }  //NOTE: new hooks is better to place on the new page even if there is some free space on detected hook page...

    BYTE *p_hook_addr = NULL;
    WORD hook_sz_full = data_sz + code_sz + trampoline_sz + jump_instr_sz;

    //new v1.3 - analyze trampoline and exclude it from hook if all opcodes are nops
    BYTE trampoline_sz_optimized = trampoline_sz; BOOL is_nops = TRUE;
    for (BYTE i = 0; i < trampoline_sz; ++i) {
        if (((BYTE)*(p_install_addr + i)) != 0x90) {is_nops = FALSE; break;}
    }
    if (is_nops) {
        AIP_CORE_PRINT(AIP_CORE_STRM_DEBUG, "Detected empty trampoline, optimizing hook size...");
        hook_sz_full -= trampoline_sz; trampoline_sz_optimized = 0;
    }

    if ( (mem_page_pos != 0) && ((mem_page_pos + hook_sz_full) < (mem_page_start + aip_mbapi.page_size)) ) { //check if we can use already allocated page
        AIP_CORE_PRINT(AIP_CORE_STRM_DEBUG, "Installing hook in already allocated page...");
        p_hook_addr = (BYTE *)mem_page_pos;
        *(p_hook_addr - 1) = 0x90; //fix - replace 00 with nop to fix disasm view
    } else { //no allocated page or not enough bytes on current page
        p_hook_addr = VirtualAlloc(NULL, hook_sz_full, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        AIP_CORE_PRINT(AIP_CORE_STRM_DEBUG, "New page for hook allocated at address 0x%p", p_hook_addr);
        mem_page_start = (DWORD)p_hook_addr;
    }
    AIP_CORE_CHECK_ERROR((p_hook_addr == NULL), AIP_EC_WINAPI_MEM_ALLOC_FAIL); //WHAT?!
    aip_mbapi_asm_check_mem_is_accessible(p_hook_addr, hook_sz_full); //hook mem is ok?

    p_custom_data_addr = (BYTE *)p_hook_addr;
    if (p_data != NULL) //if data pointer is valid
        memcpy(p_data, &p_custom_data_addr, sizeof(void *)); //then save first data byte address into it
    memset(p_hook_addr, 0x00, data_sz); //make sure that data section contains only zeros
    p_hook_addr += data_sz; //shift from data section
    hook_sz_full -= data_sz; //reduce size due to shift

    memset(p_hook_addr, 0x90, hook_sz_full); //fill code section with nops
    BYTE *p_hook_trampoline_addr = (BYTE *)(p_hook_addr + code_sz); //address where trampoline first byte is located inside hook
    if (trampoline_sz_optimized != 0)
        AIP_CORE_CALL_IF_BUILD_ALIVE(memcpy, p_hook_trampoline_addr, p_install_addr, trampoline_sz_optimized); //! memcpy(p_hook_trampoline_addr, p_install_addr, trampoline_sz_optimized); //copy stolen bytes at the end of hook

    BYTE *p_return_from_hook_addr = (BYTE *)(p_hook_addr + hook_sz_full - jump_instr_sz); //create custom hook asm code before patch
    DWORD rel_addr_from_hook = (DWORD)(p_install_addr - p_return_from_hook_addr - jump_instr_sz + trampoline_sz); //jump AFTER trampoline
    memcpy((BYTE *)(p_jump_instr + 1), &rel_addr_from_hook, sizeof(DWORD)); //assign jump address
    memcpy(p_return_from_hook_addr, p_jump_instr, jump_instr_sz); //append jump from hook instr

    DWORD rel_addr_to_hook = (DWORD)(p_hook_addr - p_install_addr - jump_instr_sz); //mb code patching - redirect code flow to custom section
    memcpy((BYTE *)(p_jump_instr + 1), &rel_addr_to_hook, sizeof(DWORD)); //assign jump address
    BYTE p_install_addr_patch[trampoline_sz];
    memset(p_install_addr_patch, 0x90, trampoline_sz); //don't show me garbage in debugger!
    memcpy(p_install_addr_patch, p_jump_instr, jump_instr_sz); //full patch with jump and nops
    memcpy(p_install_addr, p_install_addr_patch, trampoline_sz); //append this shit
    VirtualProtect(p_install_addr, trampoline_sz, oldProtect, &newProtect); //restore page protection

    p_custom_code_addr = p_hook_addr; //NEW - place custom code at the start of hook code section
    AIP_CORE_PRINT(AIP_CORE_STRM_DEBUG, "Hook#%u installed at address: 0x%p (p_data:0x%p, p_code:0x%p)", hook_no, p_hook_addr, p_custom_data_addr, p_custom_code_addr);

    if (p_asm_prog != NULL)
        aip_mbapi_asm_install_prog(p_custom_code_addr, p_asm_prog, code_sz);

    mem_page_pos = (DWORD)(p_hook_addr + hook_sz_full + 1); //set page pos to end of current hook + 1 nop
    AIP_CORE_PRINT(AIP_CORE_STRM_DEBUG, "Page address for next hook: 0x%08lX", mem_page_pos);

    return p_custom_code_addr; //return pointer to first NOP instr in hook
}