├── Debugger files ├── __init__.py ├── utils │ ├── __init__.py │ ├── pretty_prints.py │ └── drawings.py ├── global_vars.py ├── requirements.txt ├── realmode_debugger.py ├── main.py └── .gdbinit ├── README.md ├── QEMU files ├── boot1.asm ├── kernel.asm ├── Makefile └── boot2.asm ├── LICENSE └── .gitignore /Debugger files/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Debugger files/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Debugger files/global_vars.py: -------------------------------------------------------------------------------- 1 | # global var 2 | STACK_DEPTH = 0 3 | 4 | -------------------------------------------------------------------------------- /Debugger files/requirements.txt: -------------------------------------------------------------------------------- 1 | dearpygui==0.6.415 2 | pygdbmi==0.10.0.1 3 | PyAutoGUI==0.9.52 4 | -------------------------------------------------------------------------------- /Debugger files/utils/pretty_prints.py: -------------------------------------------------------------------------------- 1 | def pretty_print_array(array): 2 | for i in array: 3 | print(i) 4 | 5 | 6 | def pretty_string_from_array(array): 7 | string = "" 8 | for i in array: 9 | i = i.replace(":\\t", " ") 10 | string = string + str(i) + "\n" 11 | return string 12 | 13 | # # input loop 14 | # p = input() 15 | # while p!="exit": 16 | # if p=="stack": 17 | # pretty_print_array(array_stack) 18 | # elif p=="reg": 19 | # pretty_print_array(array_regs_and_flags) 20 | # elif p=="code": 21 | # pretty_print_array(array_code) 22 | # print('') 23 | # p = input() 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RealMode-x86-asm-QEMU-Debugger-frontend 2 | A debugger frontend for assembly x86 code runing in Real Mode (tested inside QEMU) (based on gdb). 3 | 4 | ## How to use 5 | 1. (install dependencies) --> run "pip install -r requirements.txt" inside the 'Debugger files' folder 6 | 2. (run QEMU emulation) --> run "make" inside the 'QEMU files' folder 7 | 3. (run the debugger) --> run "python3 main.py" inside the 'Debugger files' folder 8 | 4. press ENTER or click the 'Step' button at the bottom of the screen to step to the next instruction. 9 | or write gdb commands in the input area, click to send it to gdb and get the output on the rightmost bottom window 10 | 6. have fun :) 11 | 12 | ![output](https://user-images.githubusercontent.com/43099047/121617839-2571de00-ca3c-11eb-9dc9-2770249d78ef.gif) 13 | 14 | -------------------------------------------------------------------------------- /QEMU files/boot1.asm: -------------------------------------------------------------------------------- 1 | org 0x7c00 2 | jmp 0x0000:start 3 | 4 | start: 5 | xor ax, ax 6 | mov ds, ax 7 | mov es, ax 8 | 9 | mov ax, 0x50 ;0x50<<1 = 0x500 (início de boot2.asm) 10 | mov es, ax 11 | xor bx, bx ;posição = es<<1+bx 12 | 13 | jmp reset 14 | 15 | reset: 16 | mov ah, 00h ;reseta o controlador de disco 17 | mov dl, 0 ;floppy disk 18 | int 13h 19 | 20 | jc reset ;se o acesso falhar, tenta novamente 21 | 22 | jmp load 23 | 24 | load: 25 | mov ah, 02h ;lê um setor do disco 26 | mov al, 1 ;quantidade de setores ocupados pelo boot2 27 | mov ch, 0 ;track 0 28 | mov cl, 2 ;sector 2 29 | mov dh, 0 ;head 0 30 | mov dl, 0 ;drive 0 31 | int 13h 32 | 33 | jc load ;se o acesso falhar, tenta novamente 34 | 35 | jmp 0x500 ;pula para o setor de endereco 0x500 (start do boot2) 36 | 37 | times 510-($-$$) db 0 ;512 bytes 38 | dw 0xaa55 ;assinatura -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mateus Ferreira Borges Soares 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QEMU files/kernel.asm: -------------------------------------------------------------------------------- 1 | org 0x7e00 2 | jmp 0x0000:start 3 | 4 | print_hi: 5 | mov ah, 0eh 6 | mov al, 'H' 7 | int 10h 8 | mov al, 'i' 9 | int 10h 10 | mov al, ' ' 11 | int 10h 12 | 13 | 14 | ret 15 | 16 | start: 17 | ; 8 bit registers + print characters 18 | xor ax, ax 19 | 20 | mov ah, 0eh 21 | 22 | mov al, 'c' 23 | int 10h 24 | 25 | mov al, 'a' 26 | int 10h 27 | 28 | mov al, 's' 29 | int 10h 30 | 31 | mov al, 'a' 32 | int 10h 33 | 34 | mov al, ' ' 35 | int 10h 36 | 37 | 38 | ; push and pop 39 | push 'A' 40 | push 'B' 41 | push 'C' 42 | push 'D' 43 | pop ax 44 | pop bx 45 | pop cx 46 | pop dx 47 | 48 | ; push and add 49 | push 1 50 | push 2 51 | push 3 52 | add sp, 6 53 | 54 | 55 | ; call function 56 | call print_hi 57 | 58 | ; input 59 | mov ah, 00h ; (argumento do int 16h) 60 | int 16h ; http://www.ctyme.com/intr/rb-1754.htm --> guarda o caractere ASCII em al 61 | mov ah, 0eh ; 0eh = 14 em hexadecimal --> modo de impressão do int 10h 62 | int 10h 63 | 64 | ; char to int 65 | sub ax, 48 66 | 67 | ; ??? 68 | mov ax, 'A' 69 | mov bx, 10 70 | mov bx, 10 71 | 72 | jmp end 73 | 74 | 75 | end: 76 | jmp $ 77 | 78 | 79 | -------------------------------------------------------------------------------- /Debugger files/utils/drawings.py: -------------------------------------------------------------------------------- 1 | from dearpygui.core import * 2 | from dearpygui.simple import * 3 | 4 | 5 | def update_drawing(): 6 | y = get_value("movingArrowY") 7 | text_y = get_value("movingTextY") 8 | 9 | y2 = get_value("movingArrow2Y") 10 | text2_y = get_value("movingText2Y") 11 | # TODO: remove hardcoded magic numbers to a proper way of doing things. 12 | modify_draw_command("drawing##widget","movingArrow", p1=[200,y], p2=[100, y]) 13 | modify_draw_command("drawing##widget","movingText", pos=[70,text_y]) 14 | 15 | modify_draw_command("drawing##widget","movingArrow2", p1=[200,y2], p2=[100, y2]) 16 | modify_draw_command("drawing##widget","movingText2", pos=[70,text2_y]) 17 | 18 | def arrowUP(): 19 | # set_value("movingArrowY",(get_value("movingArrowY"))-13) 20 | # set_value("movingTextY",(get_value("movingTextY"))-13) 21 | update_drawing() 22 | 23 | def arrowDOWN(): 24 | # set_value("movingArrowY",(get_value("movingArrowY"))+13) 25 | # set_value("movingTextY",(get_value("movingTextY"))+13) 26 | update_drawing() 27 | 28 | def arrow2UP(): 29 | set_value("movingArrow2Y",(get_value("movingArrow2Y"))-13) 30 | set_value("movingText2Y",(get_value("movingText2Y"))-13) 31 | update_drawing() 32 | 33 | def arrow2DOWN(): 34 | set_value("movingArrow2Y",(get_value("movingArrow2Y"))+13) 35 | set_value("movingText2Y",(get_value("movingText2Y"))+13) 36 | update_drawing() 37 | 38 | -------------------------------------------------------------------------------- /QEMU files/Makefile: -------------------------------------------------------------------------------- 1 | #primeiro estágio 2 | boot1_file = boot1 3 | 4 | #segundo estágio 5 | boot2_file = boot2 6 | boot2_pos = 1 7 | boot2_size = 1 8 | 9 | #kernel 10 | kernel_file = kernel 11 | kernel_pos = 2 12 | kernel_size = 20 13 | 14 | boot_disk = disk.img 15 | block_size = 512 16 | disk_size = 100 17 | 18 | nasm_flags = -f bin 19 | # qemu_flags = -fda 20 | qemu_flags = -S -s -fda 21 | 22 | all: create_disk boot1_only boot2_only kernel_only write_boot1 write_boot2 write_kernel launch_qemu clean 23 | # all: boot1_only boot2_only kernel_only write_boot1 write_boot2 write_kernel launch_qemu clean 24 | 25 | create_disk: 26 | @dd if=/dev/zero of=$(boot_disk) bs=$(block_size) count=$(disk_size) status=noxfer 27 | 28 | boot1_only: 29 | @nasm $(nasm_flags) $(boot1_file).asm -o $(boot1_file).bin 30 | 31 | boot2_only: 32 | @nasm $(nasm_flags) $(boot2_file).asm -o $(boot2_file).bin 33 | 34 | kernel_only: 35 | @nasm $(nasm_flags) $(kernel_file).asm -o $(kernel_file).bin 36 | 37 | write_boot1: 38 | @dd if=$(boot1_file).bin of=$(boot_disk) bs=$(block_size) count=1 conv=notrunc status=noxfer 39 | 40 | write_boot2: 41 | @dd if=$(boot2_file).bin of=$(boot_disk) bs=$(block_size) seek=$(boot2_pos) count=$(boot2_size) conv=notrunc status=noxfer 42 | 43 | write_kernel: 44 | @dd if=$(kernel_file).bin of=$(boot_disk) bs=$(block_size) seek=$(kernel_pos) count=$(kernel_size) conv=notrunc 45 | 46 | launch_qemu: 47 | clear 48 | @qemu-system-i386 $(qemu_flags) $(boot_disk) 49 | 50 | clean: 51 | @rm -f *.bin $(boot_disk) *~ 52 | clear 53 | -------------------------------------------------------------------------------- /QEMU files/boot2.asm: -------------------------------------------------------------------------------- 1 | org 0x500 2 | jmp 0x0000:start 3 | 4 | ;como o endereço dado para o kernel é 0x7e00, devemos 5 | ;utilizar o método de shift left (hexadecimal) 6 | ;e somar o offset no adress base, para rodarmos o kernel. 7 | 8 | runningKernel db 'Rodando Kernel...', 0 9 | 10 | 11 | print_string: 12 | lodsb 13 | cmp al,0 14 | je end 15 | 16 | mov ah, 0eh 17 | mov bl, 15 18 | int 10h 19 | 20 | mov dx, 0 21 | .delay_print: 22 | inc dx 23 | mov cx, 0 24 | .time: 25 | inc cx 26 | cmp cx, 10000 27 | jne .time 28 | 29 | cmp dx, 1000 30 | jne .delay_print 31 | 32 | jmp print_string 33 | 34 | end: 35 | mov ah, 0eh 36 | mov al, 0xd 37 | int 10h 38 | mov al, 0xa 39 | int 10h 40 | ret 41 | 42 | start: 43 | xor ax, ax 44 | mov ds, ax 45 | mov es, ax 46 | 47 | 48 | ;parte pra printar as mensagens que quisermos 49 | 50 | 51 | mov si, runningKernel 52 | call print_string 53 | 54 | 55 | reset: 56 | mov ah, 00h ;reseta o controlador de disco 57 | mov dl, 0 ;floppy disk 58 | int 13h 59 | 60 | jc reset ;se o acesso falhar, tenta novamente 61 | 62 | jmp load_kernel 63 | 64 | load_kernel: 65 | ;Setando a posição do disco onde kernel.asm foi armazenado(ES:BX = [0x7E00:0x0]) 66 | mov ax,0x7E0 ;0x7E0<<1 + 0 = 0x7E00 67 | mov es,ax 68 | xor bx,bx ;Zerando o offset 69 | 70 | mov ah, 0x02 ;le o setor do disco 71 | mov al, 20 ;porção de setores ocupados pelo kernel.asm 72 | mov ch, 0 ;track 0 73 | mov cl, 3 ;setor 3 74 | mov dh, 0 ;head 0 75 | mov dl, 0 ;drive 0 76 | int 13h 77 | 78 | jc load_kernel ;se o acesso falhar, tenta novamente 79 | 80 | jmp 0x7e00 ;pula para o setor de endereco 0x7e00, que é o kernel 81 | 82 | 83 | 84 | 85 | times 510-($-$$) db 0 ;512 bytes 86 | dw 0xaa55 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /Debugger files/realmode_debugger.py: -------------------------------------------------------------------------------- 1 | from pygdbmi.gdbcontroller import GdbController 2 | 3 | 4 | def trim_by_part_and_type(response, type, part): 5 | """ get specific data """ 6 | array = [] 7 | for item in response: 8 | if item['type'] == type: 9 | array.append(item[part]) 10 | return array 11 | 12 | 13 | def initialize_session(): 14 | # Start gdb process 15 | gdbmi = GdbController(['gdb', '-x', '.gdbinit', '--interpreter=mi3']) 16 | 17 | # Initial commands 18 | gdbmi.write('target remote localhost:1234') 19 | gdbmi.write('break *0x7e00') 20 | gdbmi.write('continue') 21 | return gdbmi 22 | 23 | 24 | def get_context(gdbmi, is_first_call=False): 25 | response = gdbmi.write('context') 26 | # Get array with console output 27 | output_array = trim_by_part_and_type(response, 'console', 'payload') 28 | 29 | # cuts out first (repeated) half if its the first call 30 | if is_first_call is True: 31 | half_lenght = len(output_array) // 2 32 | output_array = output_array[:half_lenght] 33 | 34 | # Divides data in diferent arrays 35 | array_stack = [] 36 | array_regs_and_flags = [] 37 | array_code = [] 38 | i = 0 39 | while output_array[i] != "REGISTERS": 40 | array_stack.append(output_array[i]) 41 | i += 1 42 | i += 1 43 | while output_array[i] != "CODE": 44 | array_regs_and_flags.append(output_array[i]) 45 | i += 1 46 | i += 1 47 | 48 | 49 | # get code string 50 | # array_code = output_array[i:] # we are not getting the code from the context user-defined command of the gdbinit. below is how we are getting it now. 51 | array_code = [] 52 | full_code = gdbmi.write("x/40i $eip") 53 | for i in range(1,len(full_code)-1): 54 | array_code.append(full_code[i]["payload"].replace("\\n", "")) 55 | 56 | # array_code = array_code_aux 57 | 58 | # customize registers string 59 | # this is kinda repetitive. TODO: improve this block of code. make it more consise 60 | array_regs = [] 61 | # remove spaces and \n's 62 | for i in range(7): 63 | string = array_regs_and_flags[i] 64 | string_len_half = int(len(string) / 2) 65 | array_regs.append(string[:string_len_half].replace(' ', '').replace('\n', '')) 66 | array_regs.append(string[string_len_half:].replace(' ', '').replace('\n', '')) 67 | for i in range(7, len(array_regs_and_flags)): 68 | array_regs.append(array_regs_and_flags[i].replace(' ', '').replace('\n', '')) 69 | # do more slices 70 | array_regs = [] 71 | for i in range(7): 72 | array_regs.append(array_regs_and_flags[i][:9]) 73 | array_regs.append(array_regs_and_flags[i][9:]) 74 | for i in range(7, len(array_regs_and_flags)): 75 | array_regs.append(array_regs_and_flags[i]) 76 | array_regs_and_flags = array_regs 77 | # the block of code below makes the program incredibly slow, thus, I disabled it. TODO: optimize and enable it 78 | # add vaue for pointer registers 79 | # for i in range(len(array_regs_and_flags)): 80 | # if "SI" in array_regs_and_flags[i]: 81 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 82 | # array_regs_and_flags[i] += f" --> {value}" 83 | # elif "DI" in array_regs_and_flags[i]: 84 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 85 | # array_regs_and_flags[i] += f" --> {value}" 86 | # elif "SP" in array_regs_and_flags[i]: 87 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 88 | # array_regs_and_flags[i] += f" --> {value}" 89 | # elif "BP" in array_regs_and_flags[i]: 90 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 91 | # array_regs_and_flags[i] += f" --> {value}" 92 | # elif "CS" in array_regs_and_flags[i]: 93 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 94 | # array_regs_and_flags[i] += f" --> {value}" 95 | # elif "DS" in array_regs_and_flags[i]: 96 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 97 | # array_regs_and_flags[i] += f" --> {value}" 98 | # elif "ES" in array_regs_and_flags[i]: 99 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 100 | # array_regs_and_flags[i] += f" --> {value}" 101 | # elif "SS" in array_regs_and_flags[i]: 102 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 103 | # array_regs_and_flags[i] += f" --> {value}" 104 | # elif "IP" in array_regs_and_flags[i]: 105 | # value = gdbmi.write(f"x {array_regs_and_flags[i][4:9]}")[1]["payload"] 106 | # array_regs_and_flags[i] += f" --> {value}" 107 | 108 | # customize stack string 109 | space_str = " " 110 | array_stack.append("") 111 | array_stack.reverse() 112 | array_stack.pop() 113 | for i in range(2, len(array_stack)): 114 | ascii = f"{str(bytes.fromhex(array_stack[i]))}" 115 | ascii_len = len(ascii) 116 | integer = int(array_stack[i],16) 117 | array_stack[i] += f" {ascii} {space_str * (18-ascii_len)} {integer} " 118 | # array_stack[i] += '\n' 119 | 120 | array_stack.reverse() 121 | array_stack.insert(0, "HEXA ASCII INTEGER - lower memory adresses\n") 122 | array_stack[-1] += f"{space_str*36}+ higher memory adresses" 123 | return array_stack, array_regs, array_code 124 | -------------------------------------------------------------------------------- /Debugger files/main.py: -------------------------------------------------------------------------------- 1 | # TODO: make the code more modular --> create separated .py files for each functionality and improt here 2 | 3 | from dearpygui.core import * 4 | from dearpygui.simple import * 5 | import pyautogui 6 | import realmode_debugger 7 | from utils import pretty_prints 8 | import time 9 | from utils import drawings 10 | 11 | # The way I'm using global vars is horrible. TODO: fix this as soon as possible! 12 | import global_vars 13 | 14 | width, height = pyautogui.size() 15 | 16 | # constants 17 | MAIN_WINDOW_WIDTH = width 18 | MAIN_WINDOW_HEIGHT = height 19 | MAIN_WINDOW_TITLE = "RealMode Debugger" 20 | WRAP_SIZE = 430 21 | BOTTOM = 150 22 | 23 | # window object settings 24 | set_main_window_title(MAIN_WINDOW_TITLE) 25 | set_main_window_size(MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT) 26 | set_main_window_pos(0, 0) 27 | # set_global_font_scale(1.25) 28 | set_theme("Dark") 29 | 30 | # window names 31 | code_window_name = "CODE" 32 | registers_window_name = "REGISTERS" 33 | stack_window_name = "STACK" 34 | drawings_window_name = "" 35 | 36 | 37 | # Function definitions # 38 | def sendInputToGDB(): 39 | input_str =get_value("gdbInput") 40 | output_str = "" 41 | response = gdbmi.write(input_str) 42 | for i in range(1,len(response)-1): 43 | output_str += str(response[i]["payload"]) + '\n' 44 | set_value("gdbOutput", output_str) 45 | 46 | def update_context(sender, data, is_end_of_program=False, is_first_call=False, update_text=True): 47 | current_instruction = '' 48 | 49 | array_stack, array_regs_and_flags, array_code = realmode_debugger.get_context(data, is_first_call) 50 | 51 | array_code = pretty_prints.pretty_string_from_array(array_code) 52 | array_regs_and_flags = pretty_prints.pretty_string_from_array(array_regs_and_flags) 53 | array_stack = pretty_prints.pretty_string_from_array(array_stack) 54 | 55 | code_text = str(array_code) 56 | regs_text = str(array_regs_and_flags) 57 | stack_text = str(array_stack) 58 | 59 | # if update_text is false, it means we want to use this function to get the current instrucion 60 | if update_text is False: 61 | first_code_text_line = "" 62 | current_char = '' 63 | i = 0 64 | while current_char!='\n': 65 | current_char = code_text[i] 66 | first_code_text_line += current_char 67 | i += 1 68 | # catch interrupts 69 | if " int "in first_code_text_line: 70 | current_instruction = "int" 71 | # catch end of program 72 | # if "0x7e17" in first_code_text_line: 73 | # current_instruction = "end_of_program" 74 | if " push " in first_code_text_line: 75 | if global_vars.STACK_DEPTH == 0: 76 | global_vars.STACK_DEPTH += 1 77 | drawings.arrowDOWN() 78 | drawings.arrow2DOWN() 79 | else: 80 | global_vars.STACK_DEPTH += 1 81 | drawings.arrow2DOWN() 82 | 83 | if " pop " in first_code_text_line: 84 | if global_vars.STACK_DEPTH == 1: 85 | global_vars.STACK_DEPTH -= 1 86 | drawings.arrow2UP() 87 | drawings.arrowUP() 88 | else: 89 | global_vars.STACK_DEPTH -= 1 90 | drawings.arrow2UP() 91 | if "add " in first_code_text_line and "sp" in first_code_text_line: 92 | hex_number = first_code_text_line.split(',')[1] 93 | number = int(hex_number, 16) 94 | number = int(number/2) 95 | for i in range(number): 96 | if global_vars.STACK_DEPTH == 1: 97 | global_vars.STACK_DEPTH -= 1 98 | drawings.arrow2UP() 99 | drawings.arrowUP() 100 | else: 101 | global_vars.STACK_DEPTH -= 1 102 | drawings.arrow2UP() 103 | 104 | if "sub " in first_code_text_line and "sp" in first_code_text_line: 105 | hex_number = first_code_text_line.split(',')[1] 106 | number = int(hex_number, 16) 107 | number = int(number / 2) 108 | for i in range(number): 109 | if global_vars.STACK_DEPTH == 0: 110 | global_vars.STACK_DEPTH += 1 111 | drawings.arrowDOWN() 112 | drawings.arrow2DOWN() 113 | else: 114 | global_vars.STACK_DEPTH += 1 115 | drawings.arrow2DOWN() 116 | 117 | 118 | return current_instruction 119 | 120 | if update_text is True: 121 | if is_first_call is True: 122 | add_text(code_text, parent=code_window_name, wrap=WRAP_SIZE) 123 | add_text(stack_text, parent=stack_window_name, wrap=WRAP_SIZE) 124 | add_text(regs_text, parent=registers_window_name, wrap=WRAP_SIZE) 125 | 126 | else: 127 | # This is probably a bad way of doing thigs, but I don't have time now to properly do it. 128 | # TODO: write a better way of updating text other than deleting the current and instaciating a new one. 129 | code_children = get_item_children(code_window_name) 130 | for i in code_children: 131 | delete_item(i) 132 | 133 | regs_children = get_item_children(registers_window_name) 134 | for i in regs_children: 135 | delete_item(i) 136 | 137 | stack_children = get_item_children(stack_window_name) 138 | for i in stack_children: 139 | delete_item(i) 140 | 141 | add_text(code_text, parent=code_window_name, wrap=WRAP_SIZE) 142 | add_text(regs_text, parent=registers_window_name, wrap=WRAP_SIZE) 143 | add_text(stack_text, parent=stack_window_name, wrap=WRAP_SIZE) 144 | 145 | 146 | def single_step(sender, data): 147 | current_instruction = '' 148 | current_instruction = update_context(sender=sender, data=data, is_first_call=False, update_text=False) 149 | if current_instruction=="end_of_program": 150 | # this code is repeated from below. TODO: make a separate function "clear_texts" to improve reusability. got no time for that now ;) 151 | code_children = get_item_children(code_window_name) 152 | for i in code_children: 153 | delete_item(i) 154 | # TODO: improve reusability here too. same as the TODO above. 155 | add_text("\n\n############# Reached End Of Program #############", parent=code_window_name, wrap=WRAP_SIZE) 156 | return 1 157 | 158 | elif current_instruction=="int": 159 | gdbmi.write("stepo") 160 | update_context(sender=sender, data=data, is_first_call=False) 161 | else: 162 | 163 | gdbmi.write("ni") 164 | update_context(sender=sender, data=data, is_first_call=False) 165 | 166 | 167 | gdbmi = realmode_debugger.initialize_session() 168 | 169 | # Program # 170 | with window(code_window_name, width=int(MAIN_WINDOW_WIDTH / 3), height=MAIN_WINDOW_HEIGHT - BOTTOM, no_close=True, no_bring_to_front_on_focus=True, no_move=True, no_resize=True): 171 | set_window_pos(code_window_name, 0, 0) 172 | 173 | with window(registers_window_name, width=int((MAIN_WINDOW_WIDTH / 3)/2), height=MAIN_WINDOW_HEIGHT - BOTTOM, no_close=True, no_bring_to_front_on_focus=True, no_move=True, no_resize=True): 174 | set_window_pos(registers_window_name, int(MAIN_WINDOW_WIDTH / 3), 0) 175 | 176 | with window(drawings_window_name, width=int((MAIN_WINDOW_WIDTH / 3)/2) + 3, height=MAIN_WINDOW_HEIGHT - BOTTOM, no_scrollbar=True, no_close=True, no_bring_to_front_on_focus=True, no_move=True, no_resize=True): 177 | set_window_pos(drawings_window_name, int(MAIN_WINDOW_WIDTH / 3) + int((MAIN_WINDOW_WIDTH / 3)/2), 0) 178 | add_drawing(".", width=int((MAIN_WINDOW_WIDTH / 3)/2) + 3, height=20) 179 | add_drawing("drawing##widget", width=int((MAIN_WINDOW_WIDTH / 3)/2) + 3, height=int(MAIN_WINDOW_HEIGHT - BOTTOM)) 180 | 181 | add_value("movingArrowY",8) 182 | add_value("movingTextY", 0) 183 | 184 | add_value("movingArrow2Y", -5) 185 | add_value("movingText2Y", 0) 186 | 187 | # TODO: de-hardcode all the magic values and make this code better. 188 | draw_arrow("drawing##widget",[200, 8], [100, 8], [0, 255, 0], 1, 10, tag="movingArrow") 189 | draw_text("drawing##widget",text="SP", pos=[70, 0], size=18, tag="movingText") 190 | 191 | draw_arrow("drawing##widget",[200, -5], [100, -5], [255, 0, 0], 1, 10, tag="movingArrow2") 192 | draw_text("drawing##widget",text="End of\nStack", pos=[70, 7], size=18, tag="movingText2") 193 | 194 | with window(stack_window_name, width=int(MAIN_WINDOW_WIDTH / 3), height=MAIN_WINDOW_HEIGHT - BOTTOM, no_close=True, no_bring_to_front_on_focus=True, no_move=True, no_resize=True): 195 | set_window_pos(stack_window_name, int(MAIN_WINDOW_WIDTH / 3) * 2, 0) 196 | 197 | with window("Interactive Window", width=int(MAIN_WINDOW_WIDTH/3), height=int(BOTTOM * (6 / 10)), no_title_bar=True, no_close=True, no_bring_to_front_on_focus=True, no_move=True, no_resize=True): 198 | set_window_pos("Interactive Window", 0, MAIN_WINDOW_HEIGHT - BOTTOM) 199 | add_button("Step", callback=single_step, callback_data=gdbmi) 200 | 201 | with window("Interactive Window 2", width=int(MAIN_WINDOW_WIDTH/3), height=int(BOTTOM * (6 / 10)), no_title_bar=True, no_close=True, no_bring_to_front_on_focus=True, no_move=True, no_resize=True): 202 | set_window_pos("Interactive Window 2", int(MAIN_WINDOW_WIDTH/3), MAIN_WINDOW_HEIGHT - BOTTOM) 203 | add_input_text("gdbInput",hint="write to gdb", width=100) 204 | add_button("sendInput", callback=sendInputToGDB) 205 | with window("GDB output", width=int(MAIN_WINDOW_WIDTH/3), height=int(BOTTOM * (6 / 10)), no_title_bar=False, no_close=True, no_bring_to_front_on_focus=True, no_move=True, no_resize=True): 206 | set_window_pos("GDB output", int(MAIN_WINDOW_WIDTH/3 * 2), MAIN_WINDOW_HEIGHT - BOTTOM) 207 | add_text(name="gdbOutput") 208 | 209 | 210 | update_context(None, data=gdbmi, is_first_call=True) 211 | 212 | 213 | def main_callback(sender, data): 214 | if is_key_pressed(mvKey_Return): 215 | single_step(sender, gdbmi) 216 | 217 | 218 | 219 | set_render_callback(main_callback) 220 | 221 | start_dearpygui() 222 | -------------------------------------------------------------------------------- /Debugger files/.gdbinit: -------------------------------------------------------------------------------- 1 | set confirm off 2 | set verbose off 3 | set prompt \033[31mreal-mode-gdb$ \033[0m 4 | 5 | set output-radix 0d10 6 | set input-radix 0d10 7 | 8 | # These make gdb never pause in its output 9 | set height 0 10 | set width 0 11 | 12 | # Intel syntax 13 | set disassembly-flavor intel 14 | # Real mode 15 | set architecture i8086 16 | 17 | set $SHOW_CONTEXT = 1 18 | 19 | set $REAL_MODE = 1 20 | 21 | # By default A20 is present 22 | set $ADDRESS_MASK = 0x1FFFFF 23 | 24 | # nb of instructions to display 25 | set $CODE_SIZE = 10 26 | 27 | define enable-a20 28 | set $ADDRESS_MASK = 0x1FFFFF 29 | end 30 | define disable-a20 31 | set $ADDRESS_MASK = 0x0FFFFF 32 | end 33 | 34 | # convert segment:offset address to physical address 35 | define r2p 36 | if $argc < 2 37 | printf "Arguments: segment offset\n" 38 | else 39 | set $ADDR = (((unsigned long)$arg0 & 0xFFFF) << 4) + (((unsigned long)$arg1 & 0xFFFF) & $ADDRESS_MASK) 40 | printf "0x%05X\n", $ADDR 41 | end 42 | end 43 | document r2p 44 | Convert segment:offset address to physical address 45 | Set the global variable $ADDR to the computed one 46 | end 47 | 48 | # get address of Interruption 49 | define int_addr 50 | if $argc < 1 51 | printf "Argument: interruption_number\n" 52 | else 53 | set $offset = (unsigned short)*($arg0 * 4) 54 | set $segment = (unsigned short)*($arg0 * 4 + 2) 55 | r2p $segment $offset 56 | printf "%04X:%04X\n", $segment, $offset 57 | end 58 | end 59 | document int_addr 60 | Get address of interruption 61 | end 62 | 63 | define compute_regs 64 | set $rax = ((unsigned long)$eax & 0xFFFF) 65 | set $rbx = ((unsigned long)$ebx & 0xFFFF) 66 | set $rcx = ((unsigned long)$ecx & 0xFFFF) 67 | set $rdx = ((unsigned long)$edx & 0xFFFF) 68 | set $rsi = ((unsigned long)$esi & 0xFFFF) 69 | set $rdi = ((unsigned long)$edi & 0xFFFF) 70 | set $rbp = ((unsigned long)$ebp & 0xFFFF) 71 | set $rsp = ((unsigned long)$esp & 0xFFFF) 72 | set $rcs = ((unsigned long)$cs & 0xFFFF) 73 | set $rds = ((unsigned long)$ds & 0xFFFF) 74 | set $res = ((unsigned long)$es & 0xFFFF) 75 | set $rss = ((unsigned long)$ss & 0xFFFF) 76 | set $rip = ((((unsigned long)$cs & 0xFFFF) << 4) + ((unsigned long)$eip & 0xFFFF)) & $ADDRESS_MASK 77 | set $r_ss_sp = ((((unsigned long)$ss & 0xFFFF) << 4) + ((unsigned long)$esp & 0xFFFF)) & $ADDRESS_MASK 78 | set $r_ss_bp = ((((unsigned long)$ss & 0xFFFF) << 4) + ((unsigned long)$ebp & 0xFFFF)) & $ADDRESS_MASK 79 | end 80 | 81 | define print_regs 82 | printf "AX: %04X BX: %04X ", $rax, $rbx 83 | #printf "CX: %04X DX: %04X\n", $rcx, $rdx 84 | printf "CX: %04X DX: %04X", $rcx, $rdx 85 | printf "SI: %04X DI: %04X ", $rsi, $rdi 86 | #printf "SP: %04X BP: %04X\n", $rsp, $rbp 87 | printf "SP: %04X BP: %04X", $rsp, $rbp 88 | printf "CS: %04X DS: %04X ", $rcs, $rds 89 | #printf "ES: %04X SS: %04X\n", $res, $rss 90 | printf "ES: %04X SS: %04X", $res, $rss 91 | #printf "\n" 92 | #printf "IP: %04X EIP:%08X\n", ((unsigned short)$eip & 0xFFFF), $eip 93 | printf "IP: %04X EIP:%08X", ((unsigned short)$eip & 0xFFFF), $eip 94 | #printf "CS:IP: %04X:%04X (0x%05X)\n", $rcs, ((unsigned short)$eip & 0xFFFF), $rip 95 | printf "CS:IP: %04X:%04X (0x%05X)", $rcs, ((unsigned short)$eip & 0xFFFF), $rip 96 | #printf "SS:SP: %04X:%04X (0x%05X)\n", $rss, $rsp, $r_ss_sp 97 | printf "SS:SP: %04X:%04X (0x%05X)", $rss, $rsp, $r_ss_sp 98 | #printf "SS:BP: %04X:%04X (0x%05X)\n", $rss, $rbp, $r_ss_bp 99 | printf "SS:BP: %04X:%04X (0x%05X)", $rss, $rbp, $r_ss_bp 100 | end 101 | document print_regs 102 | Print CPU registers 103 | end 104 | 105 | define print_eflags 106 | printf "OF <%d> DF <%d> IF <%d> TF <%d>",\ 107 | (($eflags >> 0xB) & 1), (($eflags >> 0xA) & 1), \ 108 | (($eflags >> 9) & 1), (($eflags >> 8) & 1) 109 | 110 | #printf " SF <%d> ZF <%d> AF <%d> PF <%d> CF <%d>\n",\ 111 | # (($eflags >> 7) & 1), (($eflags >> 6) & 1),\ 112 | # (($eflags >> 4) & 1), (($eflags >> 2) & 1), ($eflags & 1) 113 | 114 | 115 | printf " SF <%d> ZF <%d> AF <%d> PF <%d> CF <%d>",\ 116 | (($eflags >> 7) & 1), (($eflags >> 6) & 1),\ 117 | (($eflags >> 4) & 1), (($eflags >> 2) & 1), ($eflags & 1) 118 | 119 | 120 | 121 | printf "ID <%d> VIP <%d> VIF <%d> AC <%d>",\ 122 | (($eflags >> 0x15) & 1), (($eflags >> 0x14) & 1), \ 123 | (($eflags >> 0x13) & 1), (($eflags >> 0x12) & 1) 124 | 125 | #printf " VM <%d> RF <%d> NT <%d> IOPL <%d>\n",\ 126 | # (($eflags >> 0x11) & 1), (($eflags >> 0x10) & 1),\ 127 | # (($eflags >> 0xE) & 1), (($eflags >> 0xC) & 3) 128 | 129 | 130 | printf " VM <%d> RF <%d> NT <%d> IOPL <%d>",\ 131 | (($eflags >> 0x11) & 1), (($eflags >> 0x10) & 1),\ 132 | (($eflags >> 0xE) & 1), (($eflags >> 0xC) & 3) 133 | 134 | 135 | 136 | end 137 | document print_eflags 138 | Print eflags register. 139 | end 140 | 141 | # dump content of bytes in memory 142 | # arg0 : addr 143 | # arg1 : nb of bytes 144 | define _dump_memb 145 | if $argc < 2 146 | printf "Arguments: address number_of_bytes\n" 147 | else 148 | set $_nb = $arg1 149 | set $_i = 0 150 | set $_addr = $arg0 151 | while ($_i < $_nb) 152 | printf "%02X ", *((unsigned char*)$_addr + $_i) 153 | set $_i++ 154 | end 155 | end 156 | end 157 | 158 | # dump content of memory in words 159 | # arg0 : addr 160 | # arg1 : nb of words 161 | define _dump_memw 162 | if $argc < 2 163 | printf "Arguments: address number_of_words\n" 164 | else 165 | set $_nb = $arg1 166 | set $_i = 0 167 | set $_addr = $arg0 168 | while ($_i < $_nb) 169 | printf "%04X ", *((unsigned short*)$_addr + $_i) 170 | set $_i++ 171 | end 172 | end 173 | end 174 | 175 | # display data at given address 176 | define print_data 177 | if ($argc > 0) 178 | set $seg = $arg0 179 | set $off = $arg1 180 | set $raddr = ($arg0 << 16) + $arg1 181 | set $maddr = ($arg0 << 4) + $arg1 182 | 183 | set $w = 16 184 | set $i = (int)0 185 | while ($i < 4) 186 | printf "%08X: ", ($raddr + $i * $w) 187 | set $j = (int)0 188 | while ($j < $w) 189 | printf "%02X ", *(unsigned char*)($maddr + $i * $w + $j) 190 | set $j++ 191 | end 192 | printf " " 193 | set $j = (int)0 194 | while ($j < $w) 195 | set $c = *(unsigned char*)($maddr + $i * $w + $j) 196 | if ($c > 32) && ($c < 128) 197 | printf "%c", $c 198 | else 199 | printf "." 200 | end 201 | set $j++ 202 | end 203 | printf "\n" 204 | set $i++ 205 | end 206 | 207 | 208 | end 209 | end 210 | 211 | define context 212 | #printf "---------------------------[ STACK ]---\n" 213 | printf "STACK" 214 | _dump_memw $r_ss_sp 42 215 | #printf "\n" 216 | set $_a = $r_ss_sp + 16 217 | #_dump_memw $_a 8 218 | #printf "\n" 219 | #printf "---------------------------[ DS:SI ]---\n" 220 | #print_data $ds $rsi 221 | #printf "---------------------------[ ES:DI ]---\n" 222 | #print_data $es $rdi 223 | 224 | #printf "----------------------------[ CPU ]----\n" 225 | printf "REGISTERS" 226 | print_regs 227 | print_eflags 228 | #printf "---------------------------[ CODE ]----\n" 229 | printf "CODE" 230 | 231 | set $_code_size = $CODE_SIZE 232 | #set $_code_size = 128 233 | set $_code_size = 16 234 | set $aux_counter = 1 235 | 236 | 237 | # disassemble 238 | # first call x/i with an address 239 | # subsequent calls to x/i will increment address 240 | if ($_code_size > 0) 241 | x /i $rip 242 | set $_code_size-- 243 | end 244 | while ($_code_size > 0) 245 | x /i $rip+$aux_counter 246 | set $_code_size-- 247 | set $aux_counter++ 248 | end 249 | end 250 | document context 251 | Print context window, i.e. regs, stack, ds:esi and disassemble cs:eip. 252 | end 253 | 254 | define hook-stop 255 | compute_regs 256 | if ($SHOW_CONTEXT > 0) 257 | context 258 | end 259 | end 260 | document hook-stop 261 | !!! FOR INTERNAL USE ONLY - DO NOT CALL !!! 262 | end 263 | 264 | # add a breakpoint on an interrupt 265 | define break_int 266 | set $offset = (unsigned short)*($arg0 * 4) 267 | set $segment = (unsigned short)*($arg0 * 4 + 2) 268 | 269 | break *$offset 270 | end 271 | 272 | define break_int_if_ah 273 | if ($argc < 2) 274 | printf "Arguments: INT_N AH\n" 275 | else 276 | set $addr = (unsigned short)*($arg0 * 4) 277 | set $segment = (unsigned short)*($arg0 * 4 + 2) 278 | break *$addr if ((unsigned long)$eax & 0xFF00) == ($arg1 << 8) 279 | end 280 | end 281 | document break_int_if_ah 282 | Install a breakpoint on INT N only if AH is equal to the expected value 283 | end 284 | 285 | define break_int_if_ax 286 | if ($argc < 2) 287 | printf "Arguments: INT_N AX\n" 288 | else 289 | set $addr = (unsigned short)*($arg0 * 4) 290 | set $segment = (unsigned short)*($arg0 * 4 + 2) 291 | break *$addr if ((unsigned long)$eax & 0xFFFF) == $arg1 292 | end 293 | end 294 | document break_int_if_ax 295 | Install a breakpoint on INT N only if AX is equal to the expected value 296 | end 297 | 298 | define stepo 299 | ## we know that an opcode starting by 0xE8 has a fixed length 300 | ## for the 0xFF opcodes, we can enumerate what is possible to have 301 | 302 | set $lip = $rip 303 | set $offset = 0 304 | 305 | # first, get rid of segment prefixes, if any 306 | set $_byte1 = *(unsigned char *)$rip 307 | # CALL DS:xx CS:xx, etc. 308 | if ($_byte1 == 0x3E || $_byte1 == 0x26 || $_byte1 == 0x2E || $_byte1 == 0x36 || $_byte1 == 0x3E || $_byte1 == 0x64 || $_byte1 == 0x65) 309 | set $lip = $rip + 1 310 | set $_byte1 = *(unsigned char*)$lip 311 | set $offset = 1 312 | end 313 | set $_byte2 = *(unsigned char *)($lip+1) 314 | set $_byte3 = *(unsigned char *)($lip+2) 315 | 316 | set $noffset = 0 317 | 318 | if ($_byte1 == 0xE8) 319 | # call near 320 | set $noffset = 3 321 | else 322 | if ($_byte1 == 0xFF) 323 | # A "ModR/M" byte follows 324 | set $_mod = ($_byte2 & 0xC0) >> 6 325 | set $_reg = ($_byte2 & 0x38) >> 3 326 | set $_rm = ($_byte2 & 7) 327 | #printf "mod: %d reg: %d rm: %d\n", $_mod, $_reg, $_rm 328 | 329 | # only for CALL instructions 330 | if ($_reg == 2 || $_reg == 3) 331 | 332 | # default offset 333 | set $noffset = 2 334 | 335 | if ($_mod == 0) 336 | if ($_rm == 6) 337 | # a 16bit address follows 338 | set $noffset = 4 339 | end 340 | else 341 | if ($_mod == 1) 342 | # a 8bit displacement follows 343 | set $noffset = 3 344 | else 345 | if ($_mod == 2) 346 | # 16bit displacement 347 | set $noffset = 4 348 | end 349 | end 350 | end 351 | 352 | end 353 | # end of _reg == 2 or _reg == 3 354 | 355 | else 356 | # else byte1 != 0xff 357 | if ($_byte1 == 0x9A) 358 | # call far 359 | set $noffset = 5 360 | else 361 | if ($_byte1 == 0xCD) 362 | # INTERRUPT CASE 363 | set $noffset = 2 364 | end 365 | end 366 | 367 | end 368 | # end of byte1 == 0xff 369 | end 370 | # else byte1 != 0xe8 371 | 372 | # if we have found a call to bypass we set a temporary breakpoint on next instruction and continue 373 | if ($noffset != 0) 374 | set $_nextaddress = $eip + $offset + $noffset 375 | printf "Setting BP to %04X\n", $_nextaddress 376 | tbreak *$_nextaddress 377 | continue 378 | # else we just single step 379 | else 380 | nexti 381 | end 382 | end 383 | document stepo 384 | Step over calls 385 | This function will set a temporary breakpoint on next instruction after the call so the call will be bypassed 386 | You can safely use it instead nexti since it will single step code if it's not a call instruction (unless you want to go into the call function) 387 | end 388 | 389 | define step_until_iret 390 | set $SHOW_CONTEXT=0 391 | set $_found = 0 392 | while (!$_found) 393 | if (*(unsigned char*)$rip == 0xCF) 394 | set $_found = 1 395 | else 396 | stepo 397 | end 398 | end 399 | set $SHOW_CONTEXT=1 400 | context 401 | end 402 | 403 | define step_until_ret 404 | set $SHOW_CONTEXT=0 405 | set $_found = 0 406 | while (!$_found) 407 | set $_p = *(unsigned char*)$rip 408 | if ($_p == 0xC3 || $_p == 0xCB || $_p == 0xC2 || $_p == 0xCA) 409 | set $_found = 1 410 | else 411 | stepo 412 | end 413 | end 414 | set $SHOW_CONTEXT=1 415 | context 416 | end 417 | 418 | define step_until_int 419 | set $SHOW_CONTEXT = 0 420 | 421 | while (*(unsigned char*)$rip != 0xCD) 422 | stepo 423 | end 424 | set $SHOW_CONTEXT = 1 425 | context 426 | end 427 | 428 | # Find a pattern in memory 429 | # The pattern is given by a string as arg0 430 | # If another argument is present it gives the starting address (0 otherwise) 431 | define find_in_mem 432 | if ($argc >= 2) 433 | set $_addr = $arg1 434 | else 435 | set $_addr = 0 436 | end 437 | set $_found = 0 438 | set $_tofind = $arg0 439 | while ($_addr < $ADDRESS_MASK) && (!$_found) 440 | if ($_addr % 0x100 == 0) 441 | printf "%08X\n", $_addr 442 | end 443 | set $_i = 0 444 | set $_found = 1 445 | while ($_tofind[$_i] != 0 && $_found == 1) 446 | set $_b = *((char*)$_addr + $_i) 447 | set $_t = (char)$_tofind[$_i] 448 | if ($_t != $_b) 449 | set $_found = 0 450 | end 451 | set $_i++ 452 | end 453 | if ($_found == 1) 454 | printf "Code found at 0x%05X\n", $_addr 455 | end 456 | set $_addr++ 457 | end 458 | end 459 | document find_in_mem 460 | Find a pattern in memory 461 | The pattern is given by a string as arg0 462 | If another argument is present it gives the starting address (0 otherwise) 463 | end 464 | 465 | 466 | define step_until_code 467 | set $_tofind = $arg0 468 | set $SHOW_CONTEXT = 0 469 | 470 | set $_found = 0 471 | while (!$_found) 472 | set $_i = 0 473 | set $_found = 1 474 | 475 | while ($_tofind[$_i] != 0 && $_found == 1) 476 | set $_b = *((char*)$rip + $_i) 477 | set $_t = (char)$_tofind[$_i] 478 | if ($_t != $_b) 479 | set $_found = 0 480 | end 481 | set $_i++ 482 | end 483 | 484 | if ($_found == 0) 485 | stepo 486 | end 487 | end 488 | 489 | set $SHOW_CONTEXT = 1 490 | context 491 | 492 | end 493 | --------------------------------------------------------------------------------