from __future__ import annotations

from typing import TYPE_CHECKING, List
import unicorn
import ida_dbg

from capstone.arm64 import *
from qiling.os.mapper import QlFsMappedObject    
from unicorn.unicorn_const import UC_MEM_READ

if TYPE_CHECKING:
    from qiling import Qiling
    from qiling.core_hooks_types import HookRet

first_casa = True

def my_syscall_writev(ql: Qiling, fd: int, vec: int, vlen: int):
    regreturn = 0
    size_t_len = ql.arch.pointersize
    iov = ql.mem.read(vec, vlen * size_t_len * 2)
    ql.log.debug('writev() CONTENT:')

    for i in range(vlen):
        addr = ql.unpack(iov[i * size_t_len * 2 : i * size_t_len * 2 + size_t_len])
        l = ql.unpack(iov[i * size_t_len * 2 + size_t_len : i * size_t_len * 2 + size_t_len * 2])
        regreturn += l

        buf = ql.mem.read(addr, l)
        ql.log.debug(f'{bytes(buf)}')

        if hasattr(ql.os.fd[fd], 'write'):
            ql.os.fd[fd].write(buf.decode('utf-8'))

    return regreturn

def my_syscall_write(ql: Qiling, fd: int, buf: int, count: int):
    ql.log.info('my_syscall_write called')
    try:
        # read data from emulated memory
        data = ql.mem.read(buf, count)

        # select the emulated file object that corresponds to the requested
        # file descriptor
        fobj = ql.os.fd[fd]
        if fobj == None:
            ql.log.ingo('none file descriptor')
        # write the data into the file object, if it supports write operations
        elif hasattr(fobj, 'write'):
            fobj.write(data.decode('utf-8'))
    except:
        ret = -1
    else:
        ret = count

    ql.log.info(f'my_syscall_write({fd}, {buf:#x}, {count}) = {ret}')

    return ret


tpidr_el0_base = 0x1000
tpidr_el0_size = 0x1000
def tpidr_el0_init(ql: Qiling):
    ql.log.info('Allocating a memory region at 0x1000 with 0x1000 bytes')
    ql.arch.regs.write("tpidr_el0", tpidr_el0_base)
    ql.mem.map(tpidr_el0_base, tpidr_el0_size, unicorn.UC_PROT_READ | unicorn.UC_PROT_WRITE, "tpidr_el0")
    # do not unmap

def to_bstring(addr):
    return addr.to_bytes(8, byteorder='little')

class FakeUrandom(QlFsMappedObject):
    def read(self, size: int) -> bytes:
        if size == 1:
         return b"\x42" # v5的长度为1
        # return a constant value upon reading
        else:
         return b"\x41"
 
    def fstat(self) -> int:
        # return -1 to let syscall fstat ignore it
        return -1
 
    def close(self) -> int:
        return 0

update_index_table_flag = 0
log_flag = True
class QILING_IDA:
    def _show_context(self, ql: Qiling):
        registers = tuple(ql.arch.regs.register_mapping.keys())
        grouping = 4

        for idx in range(0, 34, grouping):
            ql.log.info('\t'.join(f'{r:5s}: {ql.arch.regs.read(r):016x}' for r in registers[idx:idx + grouping]))

    def custom_prepare(self, ql: Qiling) -> None:
        ql.log.info('Context before starting emulation:')
        ql.os.set_syscall('write', my_syscall_write)
        ql.log.info('my_syscall_write registered')
        ql.os.set_syscall('writev', my_syscall_writev)
        ql.log.info('my_syscall_writev registered')
        self._show_context(ql)

        ql.add_fs_mapper(r'/dev/urandom', FakeUrandom())

    def custom_continue(self, ql: Qiling) -> List[HookRet]:
        ql.log.info('custom_continue hook')
        self._show_context(ql)

        def step_hook(ql: Qiling, addr: int, size: int, md: Cs) -> None:
            self._show_context(ql)

        def show_context(ql: Qiling, addr: int, size: int) -> None:
            ql.log.info(f"address: {addr:#x}")
            # self._show_context(ql)

        def addr_abd0_hook(ql: Qiling) -> None:
            ql.arch.regs.W28 = 3
            ql.arch.regs.PC += 4

        def addr_6170_hook(ql: Qiling) -> None:
            ql.arch.regs.X4 = 1
            ql.arch.regs.PC += 4

        def addr_27E18_hook(ql: Qiling) -> None:
            bin_path = b"E:\\gitRepo\\qiling\\examples\\rootfs\\arm64_linux\\bin\\libtps.so"
            ql.mem.write(ql.arch.regs.X1, bin_path)
            ql.arch.regs.X0 = len(bin_path)
            ql.arch.regs.PC += 4

        def addr_27DA8_hook(ql: Qiling) -> None:
            ql.arch.regs.W0 = 0
            ql.arch.regs.PC += 4

        def addr_27FB4_hook(ql: Qiling) -> None:
            if ql.arch.regs.X11 == 0 and ql.arch.regs.W12 == 1:
                phdr_vir_addr = ql.arch.regs.X8
                poffset_addr = ql.arch.regs.x10
                write_base = ql.arch.regs.X19
                ql.log.info(f'phdr_vir_addr: {phdr_vir_addr:#x}')
                ql.log.info(f'poffset_addr: {poffset_addr:#x}')
                sub1 = to_bstring(phdr_vir_addr-0x40)
                ql.mem.write(write_base+0x100, sub1)
                ql.log.info(b'sub1: ' + sub1)

                image_vir_addr = phdr_vir_addr-0x40
                ql.mem.write(write_base+0x10, to_bstring(0x40))
                ql.log.info(f'sub2: {image_vir_addr:#x}')

                ql.arch.regs.X8 = image_vir_addr
                ql.arch.regs.PC = 0x28070 + 0x7ffff7dd5000

        def addr_27FDC_hook(ql: Qiling) -> None:
            ql.log.info(f'image.e_type: {ql.arch.regs.W8:#x}')

        # def addr_283E4_hook(ql: Qiling) -> None:
        #     p_dynamic = ql.arch.regs.X19+0x20
        #     dynamic_addr = int.from_bytes(ql.mem.read(p_dynamic, 8), byteorder='little')
        #     ql.log.info(f'soinfo_addr: {ql.arch.regs.X19:#x}')
        #     ql.log.info(f'p_dynamic: {p_dynamic:#x}')
        #     ql.log.info(f'dynamic_addr: {dynamic_addr:#x}')
        #     if dynamic_addr == 0x321fc8:
        #         new_dynamic_addr = dynamic_addr + 0x00555555554000
        #         ql.log.info(f'new dynamic addr: {new_dynamic_addr:#x}')
        #         ql.mem.write(p_dynamic, new_dynamic_addr)

        # def addr_2796C_hook(ql: Qiling) -> None:
        #     p_linktime = ql.arch.regs.X10
        #     linktime_addr = ql.arch.regs.X11
        #     v9 = ql.arch.regs.X19

        #     ql.log.info(f'p_linktime: {p_linktime:#x}')
        #     ql.log.info(f'linktime_addr: {linktime_addr:#x}')
        #     ql.log.info(f'v9: {v9:#x}')

        # return [ql.hook_code(step_hook, user_data=ql.arch.disassembler)]
        # return [ql.hook_address(addr_abd0_hook, 0x00007ffff7ddfbd0), 
        #         ql.hook_address(addr_6170_hook, 0x00007ffff7ddb170)]
        # return [ql.hook_address(addr_27E18_hook, 0x7ffff7dfce18), 
        #         ql.hook_address(addr_27DA8_hook, 0x7ffff7dfcda8), 
        #         ql.hook_code(show_context)]

        # return [ql.hook_address(addr_27E18_hook, 0x7ffff7dfce18), 
        #         ql.hook_address(addr_27DA8_hook, 0x7ffff7dfcda8),
        #         ql.hook_address(addr_27FB4_hook, 0x7ffff7dfcfb4)
        #         ]
        return [ql.hook_code(show_context)]

    def custom_run_to_here(self, ql: Qiling) -> List[HookRet]:
        ql.log.info('custom_run_to_here hook')
        self._show_context(ql)

        def show_context(ql: Qiling, addr: int, size: int) -> None:
            ql.log.info(f"address: {addr:#x}")
            # self._show_context(ql)

        def addr_27E18_hook(ql: Qiling) -> None:
            bin_path = b"E:\\gitRepo\\qiling\\examples\\rootfs\\arm64_linux\\bin\\libtps.so"
            ql.mem.write(ql.arch.regs.X1, bin_path)
            ql.arch.regs.X0 = len(bin_path)
            ql.arch.regs.PC += 4

        def addr_27DA8_hook(ql: Qiling) -> None:
            ql.arch.regs.W0 = 0
            ql.arch.regs.PC += 4

        def addr_27FB4_hook(ql: Qiling) -> None:
            if ql.arch.regs.X11 == 0 and ql.arch.regs.W12 == 1:
                phdr_vir_addr = ql.arch.regs.X8
                poffset_addr = ql.arch.regs.x10
                write_base = ql.arch.regs.X19
                ql.log.info(f'phdr_vir_addr: {phdr_vir_addr:#x}')
                ql.log.info(f'poffset_addr: {poffset_addr:#x}')
                sub1 = to_bstring(phdr_vir_addr-0x40)
                ql.mem.write(write_base+0x100, sub1)
                ql.log.info(b'sub1: ' + sub1)

                image_vir_addr = phdr_vir_addr-0x40
                ql.mem.write(write_base+0x10, to_bstring(0x40))
                ql.log.info(f'sub2: {image_vir_addr:#x}')

                ql.arch.regs.X8 = image_vir_addr
                ql.arch.regs.PC = 0x28070 + 0x7ffff7dd5000

        def addr_tps_ADAB8_hook(ql: Qiling) -> None:
            ql.arch.regs.PC += 4
            ql.arch.regs.X0 = 0


        def calc_W11(ql: Qiling):
            base = ql.arch.regs.W12
            first = (base & 0x10000000) >> 0x1A
            assert first == (first & 0xFC)
            W11 = first | (base >> 0x1A) & 3 | ((base & 0x20000000) >> 0x1A) | ((base & 0x40000000) >> 0x1A)
            assert W11 == ((base >> 0x1A) & 0x1F)
            return W11

        def calc_off(ql: Qiling):
            base = ql.arch.regs.W12
            off = base & 0xF000 | ((base >> 6) & 0x3F) | ((base >> 0x14) & 0xFC0)
            return off & 0x0000FFFF

        def read_mem_qword(ql: Qiling, addr):
            buf = ql.mem.read(addr, 8)
            return int.from_bytes(buf, byteorder='little')

        def read_mem_word(ql: Qiling, addr):
            buf = ql.mem.read(addr, 4)
            return int.from_bytes(buf, byteorder='little')

        def read_mem_short(ql: Qiling, addr):
            buf = ql.mem.read(addr, 2)
            return int.from_bytes(buf, byteorder='little')

        def read_mem_byte(ql: Qiling, addr):
            buf = ql.mem.read(addr, 1)
            return int.from_bytes(buf, byteorder='little')

        def get_base_W8(ql: Qiling):
            addr = ql.arch.regs.X25 + 8 + ql.arch.regs.W8 * 8
            return read_mem_qword(ql, addr)

        def get_base_W8_byte(ql: Qiling):
            addr = ql.arch.regs.X25 + 8 + ql.arch.regs.W8 * 8
            return read_mem_byte(ql, addr)

        def get_base_W8_word(ql: Qiling):
            addr = ql.arch.regs.X25 + 8 + ql.arch.regs.W8 * 8
            return read_mem_word(ql, addr)

        def get_base_W9(ql: Qiling):
            addr = ql.arch.regs.X25 + 8 + ql.arch.regs.W9 * 8
            return read_mem_qword(ql, addr)

        def get_base_W9_word(ql: Qiling):
            addr = ql.arch.regs.X25 + 8 + ql.arch.regs.W9 * 8
            return read_mem_word(ql, addr)

        def get_base(ql: Qiling, addr):
            func_data_addr = ql.arch.regs.X23
            distance = func_data_addr - addr
            if distance < 0x10000 and distance > -0x10000:
                return f'func_data-{func_data_addr - addr:#x}'
            else:
                return f'{addr:#x}'

        def map_func_name(addr):
            func_name_map = {0x55555564b338: "memset" }
            for key, value in func_name_map.items():
                if addr == key:
                    return value
            return f'{addr:#x}'

        def next_pc(ql: Qiling, index = ""):
            next_pc = ql.arch.regs.X1 - 0x555555554000
            index = ql.arch.regs.X12 & 0x3F
            rs = f'next unknown index: {index:#x} -> '
            ql.log.info(rs + f'next pc: {next_pc:#x}')
            ql.os.stop()
            global log_flag
            log_flag = False

        def next_pc_c(ql: Qiling):
            next_pc = ql.arch.regs.X1 - 0x555555554000
            index = ql.arch.regs.X12 & 0xFFF
            rs = f'next unknown index: {index:#x} -> '
            ql.log.info(rs + f'next pc: {next_pc:#x}')
            ql.os.stop()
            global log_flag
            log_flag = False

        def addr_tps_F7E20_hook(ql: Qiling) -> None:
            # ql.log.info(f'index_base: {ql.arch.regs.X19:#x}')
            index = ql.arch.regs.X12 & 0x3F
            # ql.log.info(f'index_value: {index:#x}')
            # ql.log.info(f'off1: {ql.arch.regs.W8:#x}')
            # ql.log.info(f'off2: {ql.arch.regs.W9:#x}')

            indent = f'\n\t\t\t\t\t\t'
            off = calc_off(ql)
            W11 = calc_W11(ql)
            rs = f'index: {index:#x} -> '
            base_W8 = f'[func_data-{0x130-ql.arch.regs.W8*8:#x}]'
            base_W9 = f'[func_data-{0x130-ql.arch.regs.W9*8:#x}]'
            base_W10 = f'[func_data-{0x130-ql.arch.regs.W10*8:#x}]'

            global update_index_table_flag
            if update_index_table_flag == 2:
                update_index_table_flag += 1

            if index == 0x9 or index == 0x1C:
                base_W9_value = get_base_W9(ql)
                rs += f'{base_W8} = {base_W9} + {off:#x}; {get_base(ql, base_W9_value)}, rs: {get_base(ql, base_W9_value+off)}'
            elif index in [0, 0x34]:
                flag_20 = ql.arch.regs.X0
                if flag_20 != 0:
                    rs += "ignore!!"
                v78 = f'(int)({off:#x} << 0x10) >> 0xE'
                v77 = get_base_W8(ql)
                rs += f'{base_W8}; {v77:#x}'
                v79 = get_base_W9(ql)
                rs += f'{indent}{base_W9}; {v79:#x}'

                str1 = f'{indent}[func_data-0x18] = index_table_pointer+8(2 elements); {ql.arch.regs.X19:#x}, rs: {ql.arch.regs.X19+8:#x}'
                str2 = f'{indent}[func_data-0x18] = index_table_pointer+4+({v78}); {ql.arch.regs.X19:#x}'
                if index == 0:
                    if v77 != v79:
                        rs += str1
                    else:
                        rs += str2

                if index == 0x34:
                    if v77 == v79:
                        rs += str1
                    else:
                        rs += str2

                rs += f'{indent}[func_data-0x20] = 2'
                update_index_table_flag = 2

            elif index == 0x7:
                index = ql.arch.regs.W12 & 0xFFF
                if index == 0x447:
                    if (W11 + ql.arch.regs.W10) <= 0x3F:
                        v38 = f''
                        if ql.arch.regs.W10 != 0x3F:
                            v38 = f'((1(LL) << (W10+1)) - 1) & ({base_W9} >> W11); {ql.arch.regs.W10:#x}, {get_base_W9_word(ql):#x}, {ql.arch.regs.W11:#x}, rs: {((1 << (ql.arch.regs.W10+1)) - 1) & (get_base_W9_word(ql) >> ql.arch.regs.W11):#x}'
                        else:
                            v38 = f'{base_W9}; {get_base(ql, get_base_W9(ql))}'
                        rs += f'{base_W8} = {v38}'
                else:
                    next_pc_c(ql)
            elif index == 0x2D:
                rs += f'{base_W8} = d{base_W9} + (int64)(int){off:#x}; {get_base_W9_word(ql):#x}, rs: {get_base_W9_word(ql)+off:#x}'
            elif index == 0x29:
                rs += f'[{base_W9}+{off:#x}] = {base_W8}; {get_base(ql, get_base_W9(ql)+off)}, {get_base(ql, get_base_W8(ql))}'
            elif index == 0x38:
                rs += f'{base_W8} = {base_W9} < {off:#x}; {get_base_W9(ql):#x}, rs: {get_base_W9(ql)<off:#x}'
            elif index in [3, 0xD, 0x16, 0x17, 0x19, 0x1E, 0x26, 0x27, 0x30, 0x32, 0x35, 0x3C, 0x3E]:
                v74 = f'{base_W9} + {off:#x}'
                sign = f''
                if index == 0x3C:
                    v38 = f'[{v74}]'
                    rs += f'{base_W8} = {v38}; {get_base(ql, get_base_W9(ql))} -> {get_base(ql, get_base_W9(ql)+off)}, {read_mem_qword(ql, get_base_W9(ql)+off):#x}'
                elif index in [0x19, 0x30]:#0x30 ->unsigned
                    if index == 0x19:
                        sign += f'(signed )'
                    v38 = f'b[{v74}]'
                    rs += f'{base_W8} = {sign}{v38}; {get_base(ql, get_base_W9(ql))}, {read_mem_byte(ql, get_base_W9(ql)+off):#x}'
                elif index in [0x17, 0x35]:
                    if index == 0x35:
                        sign += f'(signed )'
                    v38 = f'd[{v74}]'
                    rs += f'{base_W8} = {sign}{v38}; {get_base(ql, get_base_W9(ql))}, {read_mem_word(ql, get_base_W9(ql)+off):#x}'
                elif index in [0xD, 0x26]:
                    if index == 0x26:
                        sign += f'(signed )'
                    v38 = f's[{v74}]'
                    rs += f'{base_W8} = {sign}{v38}; {get_base(ql, get_base_W9(ql))}, {read_mem_short(ql, get_base_W9(ql)+off):#x}'
                else:
                    next_pc(ql)
            elif index in [5, 0xE, 0x10, 0x12, 0x29, 0x2A, 0x33, 0x3B]:
                v82 = f'{base_W9} + {off:#x}'
                if index == 0x2A:
                    rs += f'b[{v82}] = b{base_W8}; {get_base(ql, get_base_W9(ql)+off)}, {get_base_W8_byte(ql):#x}'
                else:
                    next_pc(ql)
            elif index == 0x1F:
                v38 = f'int({off:#x} << 0x10)'
                rs += f'{base_W8} = {v38}'
            elif index in [0x1D, 0x22, 0x37]:
                if index == 0x1D:
                    rs += f'{base_W8} = {base_W9} | {off:#x}; {get_base_W9(ql):#x}, rs:{get_base_W9(ql) | off:#x}'
                elif index == 0x22:
                    rs += f'{base_W8} = {base_W9} ^ {off:#x}; {get_base_W9(ql):#x}, rs:{get_base_W9(ql) ^ off:#x}'
                elif index == 0x37:
                    rs += f'{base_W8} = {base_W9} & {off:#x}; {get_base_W9(ql):#x}, rs:{get_base_W9(ql) & off:#x}'
            elif index == 0x2B:
                index = ql.arch.regs.W12 & 0xFFF
                rs = f'index: {index:#x} -> '
                sel = (index - 0x2B) >> 6
                if sel in [0xB, 0xE, 0x13, 0x1B, 0x1D, 0x2C, 0x30, 0x32]:
                    if index == 0xC2B:
                        rs += f'{base_W10} = {base_W8} + {base_W9}; {get_base_W8(ql):#x}, {get_base(ql, get_base_W9(ql))}, rs: {get_base(ql, (get_base_W8(ql)+get_base_W9(ql))&0xFFFFFFFFFFFFFFFF)}'
                    elif index == 0xCAB:
                        rs += f'{base_W10} = d{base_W8} + d{base_W9}; {get_base_W8_word(ql):#x}, {get_base_W9_word(ql):#x}, rs: {get_base_W8_word(ql)+get_base_W9_word(ql):#x}'
                    elif index == 0xB2B:
                        rs += f'{base_W10} = d{base_W9} - d{base_W8}; {get_base_W9_word(ql):#x}, {get_base_W8_word(ql):#x}, rs: {get_base_W9_word(ql)-get_base_W8_word(ql):#x}'
                    else:
                        next_pc_c(ql)
                elif sel == 0x7:
                    # index == 0x1EB
                    base_W8_value = get_base_W8(ql)
                    base_W9_value = get_base_W9(ql)
                    if base_W9_value >= base_W8_value:
                        rs += "base_W9_value >= base_W8_value: "
                        rs += f'{base_W10} = 0; w8={base_W8}={get_base_W8(ql)}, w9={base_W9}={get_base_W9(ql)}'
                    else:
                        rs += "base_W9_value < base_W8_value: "
                        rs += f'{base_W10} = 1; w8={base_W8}={get_base_W8(ql)}, w9={base_W9}={get_base_W9(ql)}'
                elif sel in [0xD, 0x1F, 0x24, 0x27]:
                    if index == 0x92B:
                        rs += f'{base_W10} = {base_W8} | {base_W9}; {get_base_W8(ql):#x}, {get_base_W9(ql):#x}, rs: {get_base(ql, get_base_W8(ql) | get_base_W9(ql))}'
                    elif index == 0x9EB:
                        rs += f'{base_W10} = {base_W8} & {base_W9}; {get_base_W8(ql):#x}, {get_base_W9(ql):#x}, rs: {get_base(ql, get_base_W8(ql) & get_base_W9(ql))}'
                    elif index == 0x36B:
                        rs += f'{base_W10} = {base_W8} ^ {base_W9}; {get_base_W8(ql):#x}, {get_base_W9(ql):#x}, rs: {get_base(ql, get_base_W8(ql) ^ get_base_W9(ql))}'
                    elif index == 0x7EB:
                        rs += f'{base_W10} = ~{base_W8} | {base_W9}; {get_base_W8(ql):#x}, {get_base_W9(ql):#x}, rs: {get_base(ql, ~get_base_W8(ql) | get_base_W9(ql))}'
                    else:
                        next_pc_c(ql)
                elif sel == 0x37:
                    if index == 0xDEB:
                        rs += f'[func_data-0x18] = {base_W9}; {get_base_W9(ql):#x}'
                        rs += f'{indent}[func_data-0x20] = 2'
                        update_index_table_flag = 2
                        if ql.arch.regs.W10 >= 1:
                            v64 = f'index_table_pointer+8; {ql.arch.regs.X19:#x}, rs: {ql.arch.regs.X19+8:#x}'
                            rs += f'{indent}{base_W10} = {v64}'
                            rs += f'{indent}[func_data-0x10] = {v64}'
                        else:
                            pass
                    else:
                        next_pc_c(ql)
                elif sel in [0x12, 0x1C]:
                    if index == 0x72B:
                        rs += f'{base_W10} = (int)d{base_W8} >> {W11:#x}; {get_base_W8(ql):#x}'
                    elif index == 0x4AB:
                        rs += f'{base_W10} = d{base_W8} << {W11:#x}; {get_base_W8(ql):#x}'
                    else:
                        next_pc_c(ql)
                elif sel == 0x29:
                    v34 = f'archor & 0x200FFF'
                    v34_value = ql.arch.regs.X12 & 0x200FFF
                    if v34_value == 0x200A6B:
                        index = v34_value
                    if index == 0xA6B:
                        v21 = f'd{base_W8} >> {W11:#x}; {get_base_W8(ql):#x}, rs: {get_base_W8(ql) >> W11:#x}'
                        rs += f'{base_W10} = {v21}'
                    else:
                        next_pc_c(ql)
                else:
                    if index == 0xEEB and ql.arch.regs.X0 == 0:
                        rs += f'index_table_pointer+4'
                        rs += f'{indent}[func_data-0x20] = 2'
                        rs += f'{indent}[func_data-0x18] = {base_W9}; {get_base(ql, get_base_W9(ql))}'
                        rs += f'{indent}[beg] = index_table_pointer'
                    else:
                        next_pc_c(ql)
            else:
                next_pc(ql)

            new_index_table_pointer = 0
            if update_index_table_flag == 3:
                new_index_table_pointer = read_mem_qword(ql, ql.arch.regs.X23-0x18)
                rs += f'{indent}index_table_pointer = [func_data-0x18]; {new_index_table_pointer:#x}'
                rs += f'{indent}[func_data-0x20] = 0'
                update_index_table_flag = 0
                rs += f'{indent}[beg] = index_table_pointer'
                if new_index_table_pointer == ql.arch.regs.X24:
                    rs += f'{indent}call func:({new_index_table_pointer:#x})([func_data-0x110], [func_data-0x108]); {map_func_name(read_mem_qword(ql, ql.arch.regs.X3))}'
                    rs += f'{indent}index_table_pointer = [func_data-0x10]; {read_mem_qword(ql, ql.arch.regs.X23-0x10):#x}'
                    rs += f'{indent}[beg] = index_table_pointer'

            if new_index_table_pointer == 0x5555557e5c88:
                rs += f'\n\n\t\tshuffle_factor table content=======================\n\t\t\t'
                shuffle_factor_table = ql.mem.read(0x55555587c038, 0x20)
                for i in range(0, 0x20):
                    rs += f'{shuffle_factor_table[i]:#04x}, '
                    if i == 0x10:
                        rs += f"\n\t\t\t"
                rs += f'\n\n\t\tshuffle_table content=======================\n\t\t\t'
                shuffle_table_addr = ql.arch.regs.X23-0x2a8
                shuffle_table = ql.mem.read(shuffle_table_addr, 0x100)
                for i in range(0, 0x100):
                    if (i % 16) == 15:
                        rs += f'{shuffle_table[i]:#04x}, '
                        rs += f'\n\t\t\t'
                    else:
                        rs += f'{shuffle_table[i]:#04x}, '
            elif new_index_table_pointer == 0x5555557e5d2c:
                data_len = read_mem_word(ql, ql.arch.regs.X23-0xb0)
                assert data_len < 0x100000
                data_addr = read_mem_qword(ql, ql.arch.regs.X23-0xa8)
                data = ql.mem.read(data_addr, data_len)
                rs += f'\n\n\t\tencrypted content with addr: {data_addr:#x}, len: {data_len:#x}=======================\n\t\t\t'
                for i in range(0, data_len):
                    if (i % 16) == 15:
                        rs += f'{data[i]:#04x}, '
                        rs += f'\n\t\t\t'
                    else:
                        rs += f'{data[i]:#04x}, '

            if log_flag:
                ql.log.info(rs)

        return [ql.hook_address(addr_27E18_hook, 0x7ffff7dfce18), 
                ql.hook_address(addr_27DA8_hook, 0x7ffff7dfcda8),
                ql.hook_address(addr_27FB4_hook, 0x7ffff7dfcfb4),
                ql.hook_address(addr_tps_ADAB8_hook, 0x555555601ab8),
                ql.hook_address(addr_tps_F7E20_hook, 0x55555564be20)]

    def custom_step(self, ql: Qiling) -> List[HookRet]:
        def step_hook(ql: Qiling, addr: int, size: int):
            ql.log.info(f'Executing: {addr:#x}')
            self._show_context(ql)

        ql.log.info('custom_step hook')

        # return [ql.hook_code(step_hook)]
        return []

    def custom_execute_selection(self, ql: Qiling) -> List[HookRet]:
        ql.log.info('custom_execute_selection hook')

        return []
