diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index f27d53d4c..7cd5a7a7a 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -5,19 +5,30 @@ env: jobs: tests: runs-on: ${{ matrix.os }} - name: Python ${{ matrix.python-version }} on ${{ matrix.os }} + name: Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.container }} strategy: fail-fast: false matrix: - os: [windows-2019, macos-10.15, ubuntu-18.04, ubuntu-20.04] - python-version: [3.6.8, 3.7.6] + os: [ubuntu-18.04, ubuntu-20.04] + python-version: [3.6.8, 3.7.6, 3.8.5] exclude: - - os: windows-2019 - python-version: 3.7.6 - - os: macos-10.15 - python-version: 3.7.6 + # - os: windows-2019 + # python-version: 3.7.6 + # - os: macos-10.15 + # python-version: 3.7.6 - os: ubuntu-20.04 python-version: 3.7.6 +# - os: windows-2019 +# python-version: 3.8.5 +# - os: macos-10.15 +# python-version: 3.8.5 + - os: ubuntu-20.04 + python-version: 3.8.5 + include: + - os: ubuntu-20.04 + python-version: 3.6.8 + container: Docker + steps: - uses: actions/checkout@v2 @@ -26,41 +37,41 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: win setup MSVC - if: contains(matrix.os, 'windows') - uses: microsoft/setup-msbuild@v1 +# - name: win setup MSVC +# if: contains(matrix.os, 'windows') +# uses: microsoft/setup-msbuild@v1 - - name: win run tests - if: contains(matrix.os, 'windows') - shell: bash - run: | - powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableArchiveScanning \$true'" - powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableBehaviorMonitoring \$true'" - powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableRealtimeMonitoring \$true'" - powershell Add-MpPreference -ExclusionPath $GITHUB_WORKSPACE - pip3 install setuptools wheel - pip3 install . - cmd.exe //C 'examples\scripts\dllscollector.bat' - cd $GITHUB_WORKSPACE/examples/rootfs/x86_windows/bin - unzip -Pinfected wannacry.bin.zip - unzip -Pinfected UselessDisk.bin.zip - unzip -Pinfected GandCrab502.bin.zip - unzip -Pinfected al-khaser.bin.zip - unzip -Pinfected sality.dll.zip - cd $GITHUB_WORKSPACE/tests - cmd.exe //C '.\test_pe.bat' - - name: mac run tests - if: contains(matrix.os, 'macos') - continue-on-error: true - shell: bash - run: | - pip3 install setuptools wheel - pip3 install . - ./examples/scripts/dylibcollector.sh - cd $GITHUB_WORKSPACE/examples/rootfs/x8664_macos/kext - unzip -Pinfected SuperRootkit.kext.zip - cd $GITHUB_WORKSPACE/tests - ./test_macho.sh +# - name: win run tests +# if: contains(matrix.os, 'windows') +# shell: bash +# run: | +# powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableArchiveScanning \$true'" +# powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableBehaviorMonitoring \$true'" +# powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableRealtimeMonitoring \$true'" +# powershell Add-MpPreference -ExclusionPath $GITHUB_WORKSPACE +# pip3 install setuptools wheel +# pip3 install . +# cmd.exe //C 'examples\scripts\dllscollector.bat' +# cd $GITHUB_WORKSPACE/examples/rootfs/x86_windows/bin +# unzip -Pinfected wannacry.bin.zip +# unzip -Pinfected UselessDisk.bin.zip +# unzip -Pinfected GandCrab502.bin.zip +# unzip -Pinfected al-khaser.bin.zip +# unzip -Pinfected sality.dll.zip +# cd $GITHUB_WORKSPACE/tests +# cmd.exe //C '.\test_pe.bat' +# - name: mac run tests +# if: contains(matrix.os, 'macos') +# continue-on-error: true +# shell: bash +# run: | +# pip3 install setuptools wheel +# pip3 install . +# ./examples/scripts/dylibcollector.sh +# cd $GITHUB_WORKSPACE/examples/rootfs/x8664_macos/kext +# unzip -Pinfected SuperRootkit.kext.zip +# cd $GITHUB_WORKSPACE/tests +# ./test_macho.sh - name: linux run tests if: contains(matrix.os, 'ubuntu') shell: 'script -q -e -c "bash {0}"' @@ -69,11 +80,13 @@ jobs: pip3 install setuptools wheel flake8 flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics pip3 install . - cd tests && ./test_elf.sh + cd examples/rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip + cd ../../../../tests && ./test_elf.sh elif [ ${{ matrix.os }} == 'ubuntu-20.04' ]; then - docker run -it --rm -v ${GITHUB_WORKSPACE}:/qiling qilingframework/qiling:dev bash -c "pip3 install . && cd tests && ./test_elf.sh" + docker run -it --rm -v ${GITHUB_WORKSPACE}:/qiling qilingframework/qiling:dev bash -c "pip3 install . && cd examples/rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip && cd ../../../../tests && ./test_elf.sh" else pip3 install setuptools wheel pip3 install . - cd tests && ./test_elf.sh + cd examples/rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip + cd ../../../../tests && ./test_elf.sh fi diff --git a/.gitignore b/.gitignore index 5ac3651a2..edce01914 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .idea *.pyc *.cache +*.cache2 .*.swp *.raw diff --git a/.travis.yml b/.travis.yml index c479544f3..166738fab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,3 +33,58 @@ matrix: script: - ./test_macho.sh + - name: "Python 3.6.8 on Windows" + os: windows + language: shell + env: + - PATH="/c/Python36:/c/Python36/Scripts:$PATH" + cache: + directories: +# - $HOME/AppData/Local/Temp/chocolatey + - $HOME/AppData/Local/pip/Cache + - /c/Python36 +# - $HOME/AppData/Local/NuGet/Cache + before_install: + - | + if [[ ! -f /c/Python36/python ]]; then + choco install python --version=3.6.8 + fi + # Prevent worker settings failure + - powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableArchiveScanning \$true'" + - powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableBehaviorMonitoring \$true'" + - powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Set-MpPreference -DisableRealtimeMonitoring \$true'" + #- choco install kb2999226 + #- python -m pip install --upgrade pip + install: +# - pip3 install wheel 'capstone>=4.0.1' 'pefile>=2019.4.18' 'python-registry>=1.3.1' 'unicorn>=1.0.2rc3' +# - | +# if [ ! -f $HOME/dist/keystone*.zip ]; then +# git clone https://github.com/keystone-engine/keystone && \ +# cd keystone && \ +# mkdir build && \ +# cd build && \ +# cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&' cmd.exe //C '..\nmake-dll.bat' '&&' nmake instalL && \ +# cd ../bindings/python && \ +# python setup.py install && \ +# python setup.py bdist --formats=zip && \ +# cp dist/*.zip $HOME/dist/ && \ +# cp /c/Program\ Files\ \(x86\)/keystone/lib/keystone.dll /c/Python36/Lib/site-packages/keystone/ && \ +# cp /c/Program\ Files\ \(x86\)/keystone/lib/keystone.dll $HOME/dist/; +# else +# unzip $HOME/dist/*.zip -d /c && \ +# cp $HOME/dist/keystone.dll /c/Python36/Lib/site-packages/keystone/; +# fi + - cd $TRAVIS_BUILD_DIR + - pip3 install . +# - cp /c/Program\ Files\ \(x86\)/keystone/lib/keystone.dll /c/Python36/Lib/site-packages/keystone/ + before_script: + - cmd.exe //C 'examples\scripts\dllscollector.bat' + - cd $TRAVIS_BUILD_DIR/examples/rootfs/x86_windows/bin + - unzip -Pinfected wannacry.bin.zip + - unzip -Pinfected UselessDisk.bin.zip + - unzip -Pinfected GandCrab502.bin.zip + - unzip -Pinfected al-khaser.bin.zip + - unzip -Pinfected sality.dll.zip + - cd $TRAVIS_BUILD_DIR/tests + script: + - cmd.exe //C '.\test_pe.bat' diff --git a/CREDITS.TXT b/CREDITS.TXT index e10a43bfd..7856f07d8 100644 --- a/CREDITS.TXT +++ b/CREDITS.TXT @@ -1,17 +1,21 @@ This file credits all the contributors of the Qiling Framework project. + Project Leader ============== LAU kaijern (xwings) + Advisor ======= NGUYEN Anh Quynh + Travis, Website and Documentations ================================== FOO Kevin (chfl4gs) + Core Developers =============== DING tianze (D1iv3) @@ -22,11 +26,6 @@ Earl MARCUS (klks84) klks84@gmail.com WU chenxu (kabeor) KONG ziqiao (lazymio) -Demigod team (https://groundx.io/demigod) -========================================= -NGUYEN Anh Quynh -NGUYEN Hong Quang -DO Minh Tuan Key Contributors (in no particular order) ========================================= @@ -35,6 +34,18 @@ liba2k assafcarlsbad ucgJhe jhumble +Mark Jansen (learn-more) +cq674350529 +elicn +bkerler (viperbjk) + + +Demigod team (https://groundx.io/demigod) +========================================= +NGUYEN Anh Quynh +NGUYEN Hong Quang +DO Minh Tuan + Contributors (in no particular order) ===================================== @@ -48,9 +59,8 @@ danielhenrymantilla iamyeh alfink bambu -bkerler (viperbjk) -Mark Jansen (learn-more) -cq674350529 + + Alpha testers (in no particular order, named by github id) ========================================================== diff --git a/ChangeLog b/ChangeLog index b364cc875..2a9e8bba4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,9 +7,30 @@ BREAK CHANGE - ql.filename is renamed to ql.argv. - ql.output and ql.verbose now has slightly different meanings and can be adjusted runtime. See their docstring for details. - ql.filter now accepts a regular expression. +- Remove ql.log_dir, ql.log_split, ql.append but add ql.log_file instead. + + +------------------------------------ +[Version 1.2.3]: March [SOMETHING], 2021 +- + ------------------------------------ -[Version 1.3]: January [SOMETHING], 2021 +[Version 1.2.2]: February 8, 2021 + +- Fix _acmdln and _wcmdln handling +- More UEFI refactor +- Refactor common OS space +- Bring sality test to work again +- Clean up more test case +- First stage multithread rewrite done +- Updated Qiling(shellcode=) to Qiling(code=), still keeping Qiling(shellcode=) for legacy purpose +- Added support for SMM_RUNTIME_SERVICES_TABLE +- Fixed regression in code coverage collection +- Added generic ql.mem.read_ptr helper function +- merged UEFI, windows, linux and macos print_function +- merged UEFI, windows, linux and macos fncc +- make MacOS uses more Qiling API ------------------------------------ diff --git a/Dockerfile b/Dockerfile index c9bdaf877..c78ec580c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,8 +8,9 @@ RUN apt-get update \ && apt-get -y upgrade \ && apt-get install -y --no-install-recommends cmake build-essential gcc git -RUN git clone -b dev https://github.com/qilingframework/qiling.git \ - && cd qiling \ +COPY . /qiling + +RUN cd /qiling \ && pip wheel . -w wheels FROM python:3.6-slim AS base @@ -19,6 +20,7 @@ COPY --from=builder /qiling /qiling WORKDIR /qiling RUN apt-get update \ + && apt-get install -y --no-install-recommends unzip \ && rm -rf /var/lib/apt/lists/* \ && pip3 install wheels/*.whl \ && rm -rf wheels diff --git a/README.md b/README.md index 2be6f5959..5056369bc 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,10 @@ Contact us at email info@qiling.io, or via Twitter [@qiling_io](https://twitter. - assafcarlsbad - ucgJhe - jhumble +- Mark Jansen (learn-more) +- cq674350529 +- elicn +- bkerler (viperbjk) --- diff --git a/docs/bg_page.png b/docs/bg_page.png new file mode 100755 index 000000000..4f9858c92 Binary files /dev/null and b/docs/bg_page.png differ diff --git a/examples/adcache_x86_windows_debug.py b/examples/adcache_x86_windows_debug.py index 2e4fdb7f0..71b9f7573 100644 --- a/examples/adcache_x86_windows_debug.py +++ b/examples/adcache_x86_windows_debug.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys from zipfile import ZipFile @@ -13,6 +13,6 @@ with ZipFile("shellcodes/win32_https_download.zip") as zip_reader: with zip_reader.open('win32_https_download.bin', 'r', b'infected') as f: sc = f.read() - ql = Qiling(shellcoder=sc, archtype="x86", ostype="windows", + ql = Qiling(code=sc, archtype="x86", ostype="windows", rootfs="rootfs/x86_windows", output="debug") ql.run() diff --git a/examples/cachedlls_x8664_windows.py b/examples/cachedlls_x8664_windows.py index 39f07e995..92e9e11f6 100644 --- a/examples/cachedlls_x8664_windows.py +++ b/examples/cachedlls_x8664_windows.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/crackme_x86_linux.py b/examples/crackme_x86_linux.py index abfa6078f..ea6ebea0a 100644 --- a/examples/crackme_x86_linux.py +++ b/examples/crackme_x86_linux.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os from unicorn import * diff --git a/examples/crackme_x86_windows.py b/examples/crackme_x86_windows.py index b71c47738..c5faa90e3 100644 --- a/examples/crackme_x86_windows.py +++ b/examples/crackme_x86_windows.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn import * from unicorn.x86_const import * diff --git a/examples/crackme_x86_windows_auto.py b/examples/crackme_x86_windows_auto.py index e87ec0bbd..2f7033b19 100644 --- a/examples/crackme_x86_windows_auto.py +++ b/examples/crackme_x86_windows_auto.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn.x86_const import * diff --git a/examples/crackme_x86_windows_setcallback.py b/examples/crackme_x86_windows_setcallback.py index 028355a54..88d35ac03 100644 --- a/examples/crackme_x86_windows_setcallback.py +++ b/examples/crackme_x86_windows_setcallback.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/crackme_x86_windows_unpatch.py b/examples/crackme_x86_windows_unpatch.py index d93234109..743c18e85 100644 --- a/examples/crackme_x86_windows_unpatch.py +++ b/examples/crackme_x86_windows_unpatch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn.x86_const import * import sys diff --git a/examples/doogie_8086_crack.py b/examples/doogie_8086_crack.py index 959ba2475..c76bd6f2d 100644 --- a/examples/doogie_8086_crack.py +++ b/examples/doogie_8086_crack.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import sys, curses, math, struct, string, time, logging +import sys, curses, math, struct, string, time sys.path.append("..") from qiling import * from qiling.const import * @@ -131,8 +131,7 @@ def third_stage(keys): # To setup terminal again, we have to restart the whole program. ql = Qiling(["rootfs/8086/doogie/doogie.DOS_MBR"], "rootfs/8086", - console=False, - log_dir=".") + console=False) ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80)) ql.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT) hk = ql.hook_code(stop, begin=0x8018, end=0x8018) @@ -172,7 +171,7 @@ def read_until_zero(ql: Qiling, addr): return buf def set_required_datetime(ql: Qiling): - logging.info("Setting Feburary 06, 1990") + ql.log.info("Setting Feburary 06, 1990") ql.reg.ch = BIN2BCD(19) ql.reg.cl = BIN2BCD(1990%100) ql.reg.dh = BIN2BCD(2) @@ -185,8 +184,7 @@ def stop(ql, addr, data): def first_stage(): ql = Qiling(["rootfs/8086/doogie/doogie.DOS_MBR"], "rootfs/8086", - console=False, - log_dir=".") + console=False) ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80)) # Doogie suggests that the datetime should be 1990-02-06. ql.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT) diff --git a/examples/extensions/idaplugin/custom_script.py b/examples/extensions/idaplugin/custom_script.py index b67a7bb99..1b23cdc3d 100644 --- a/examples/extensions/idaplugin/custom_script.py +++ b/examples/extensions/idaplugin/custom_script.py @@ -1,5 +1,4 @@ from qiling import * -import logging class QILING_IDA(): def __init__(self): @@ -10,14 +9,14 @@ def _show_context(self, ql:Qiling): for idx in range(0, len(registers), 3): regs = registers[idx:idx+3] s = "\t".join(map(lambda v: f"{v:4}: {ql.reg.__getattribute__(v):016x}", regs)) - logging.info(s) + ql.log.info(s) def custom_prepare(self, ql:Qiling): - logging.info('Context before starting emulation:') + ql.log.info('Context before starting emulation:') self._show_context(ql) def custom_continue(self, ql:Qiling): - logging.info('custom_continue hook.') + ql.log.info('custom_continue hook.') self._show_context(ql) hook = [] return hook @@ -27,12 +26,12 @@ def step_hook(ql, addr, size): logging.info(f"Executing: {hex(addr)}") self._show_context(ql) - logging.info('custom_step hook') + ql.log.info('custom_step hook') hook = [] hook.append(ql.hook_code(step_hook)) return hook def custom_execute_selection(self, ql:Qiling): - logging.info('custom execute selection hook') + ql.log.info('custom execute selection hook') hook = [] return hook \ No newline at end of file diff --git a/examples/extensions/report/hello_x86_windows_json.py b/examples/extensions/report/hello_x86_windows_json.py index 73506bbfa..b2b371d23 100644 --- a/examples/extensions/report/hello_x86_windows_json.py +++ b/examples/extensions/report/hello_x86_windows_json.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys diff --git a/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py b/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py index 6e2a041bd..7c2fa84a6 100644 --- a/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py +++ b/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # Everything about the bug and firmware https://www.exploit-db.com/exploits/33863 diff --git a/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py b/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py index 263e2b0a4..2e6e3204b 100644 --- a/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py +++ b/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # 1. Download AC15 Firmware from https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip diff --git a/examples/fuzzing/tenda_ac15/saver_tendaac15_httpd.py b/examples/fuzzing/tenda_ac15/saver_tendaac15_httpd.py index febf80ab9..eb74592bb 100644 --- a/examples/fuzzing/tenda_ac15/saver_tendaac15_httpd.py +++ b/examples/fuzzing/tenda_ac15/saver_tendaac15_httpd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # 1. Download AC15 Firmware from https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip @@ -58,7 +58,7 @@ def patcher(ql): def check_pc(ql): print("=" * 50) - print("[!] Hit fuzz point, stop at PC = 0x%x" % ql.reg.arch_pc) + print("Hit fuzz point, stop at PC = 0x%x" % ql.reg.arch_pc) print("=" * 50) ql.emu_stop() diff --git a/examples/hello_8086_dos.py b/examples/hello_8086_dos.py index 377dbd020..4ef4151dd 100644 --- a/examples/hello_8086_dos.py +++ b/examples/hello_8086_dos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") diff --git a/examples/hello_arm_linux_custom_syscall.py b/examples/hello_arm_linux_custom_syscall.py index 0ab8de5db..a90bc39fe 100644 --- a/examples/hello_arm_linux_custom_syscall.py +++ b/examples/hello_arm_linux_custom_syscall.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") @@ -14,12 +14,12 @@ def my_syscall_write(ql, write_fd, write_buf, write_count, *args, **kw): try: buf = ql.mem.read(write_buf, write_count) - logging.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) + ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) ql.os.fd[write_fd].write(buf) regreturn = write_count except: regreturn = -1 - logging.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) + ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) if ql.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP): raise diff --git a/examples/hello_arm_linux_debug.py b/examples/hello_arm_linux_debug.py index 88e114681..51406e073 100644 --- a/examples/hello_arm_linux_debug.py +++ b/examples/hello_arm_linux_debug.py @@ -1,18 +1,17 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * def run_sandbox(path, rootfs, output): - ql = Qiling(path, rootfs, output = output) - ql.multithread = False - ql.debugger = "qdb:rr" # switch on record and replay with rr - # ql.debugger = "qdb:" # enable qdb without options - # ql.debugger = "qdb:0x1030c" # enable qdb and setup breakpoin at 0x1030c + ql = Qiling(path, rootfs, output = output, multithread=True) + # ql.debugger = "qdb::rr" # switch on record and replay with rr + # ql.debugger = "qdb" # enable qdb without options + ql.debugger = "qdb:0x1030c" # enable qdb and setup breakpoin at 0x1030c ql.run() diff --git a/examples/hello_arm_set_filter.py b/examples/hello_arm_set_filter.py index 0e1d2f7f5..6cdb00c8b 100644 --- a/examples/hello_arm_set_filter.py +++ b/examples/hello_arm_set_filter.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * if __name__ == "__main__": - ql = Qiling(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", log_dir="qlog") - ql.filter = ["open"] + ql = Qiling(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux") + ql.filters = ["^open"] ql.run() diff --git a/examples/hello_linuxx8664_intercept.py b/examples/hello_linuxx8664_intercept.py index 13045070e..7a1ef0fab 100644 --- a/examples/hello_linuxx8664_intercept.py +++ b/examples/hello_linuxx8664_intercept.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/hello_mips32el_linux_debug.py b/examples/hello_mips32el_linux_debug.py index 3376ace96..46879fdf3 100644 --- a/examples/hello_mips32el_linux_debug.py +++ b/examples/hello_mips32el_linux_debug.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys diff --git a/examples/hello_mips32el_linux_function_hook.py b/examples/hello_mips32el_linux_function_hook.py index 69dfec940..ec6c2d302 100644 --- a/examples/hello_mips32el_linux_function_hook.py +++ b/examples/hello_mips32el_linux_function_hook.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys diff --git a/examples/hello_x8664_gdb_macos.py b/examples/hello_x8664_gdb_macos.py index 79f6ea9af..53a0cb556 100644 --- a/examples/hello_x8664_gdb_macos.py +++ b/examples/hello_x8664_gdb_macos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") diff --git a/examples/hello_x8664_linux_customapi.py b/examples/hello_x8664_linux_customapi.py index 43b4fdd4c..3d2b3d435 100644 --- a/examples/hello_x8664_linux_customapi.py +++ b/examples/hello_x8664_linux_customapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/hello_x8664_linux_disasm.py b/examples/hello_x8664_linux_disasm.py index 3077a5b28..da481ada7 100644 --- a/examples/hello_x8664_linux_disasm.py +++ b/examples/hello_x8664_linux_disasm.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn import * from capstone import * diff --git a/examples/hello_x8664_linux_part_exec.py b/examples/hello_x8664_linux_part_exec.py index b9a7ae039..553ec5671 100644 --- a/examples/hello_x8664_linux_part_exec.py +++ b/examples/hello_x8664_linux_part_exec.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") diff --git a/examples/hello_x8664_macos.py b/examples/hello_x8664_macos.py index 223ec2c22..13ec6e948 100644 --- a/examples/hello_x8664_macos.py +++ b/examples/hello_x8664_macos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") diff --git a/examples/hello_x8664_windows_customapi.py b/examples/hello_x8664_windows_customapi.py index bb32ecc5a..1fb2a632a 100644 --- a/examples/hello_x8664_windows_customapi.py +++ b/examples/hello_x8664_windows_customapi.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import sys, logging +import sys sys.path.append("..") from qiling import * @@ -16,7 +16,7 @@ @winsdkapi(cc=CDECL, replace_params={"str": STRING}) def my_puts(ql, address, params): ret = 0 - logging.info("\n+++++++++\nmy random Windows API\n+++++++++\n") + ql.log.info("\n+++++++++\nmy random Windows API\n+++++++++\n") string = params["str"] ret = len(string) return ret diff --git a/examples/hello_x86_linux_fake_urandom.py b/examples/hello_x86_linux_fake_urandom.py index 68f830935..e3feef6ef 100644 --- a/examples/hello_x86_linux_fake_urandom.py +++ b/examples/hello_x86_linux_fake_urandom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling import * from qiling.os.mapper import QlFsMappedObject diff --git a/examples/mem_invalid_access.py b/examples/mem_invalid_access.py index ebb56524d..c0606c6d6 100644 --- a/examples/mem_invalid_access.py +++ b/examples/mem_invalid_access.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/multithreading_arm64_linux.py b/examples/multithreading_arm64_linux.py index 6d300c101..f19117009 100644 --- a/examples/multithreading_arm64_linux.py +++ b/examples/multithreading_arm64_linux.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") diff --git a/examples/multithreading_mips32el_linux.py b/examples/multithreading_mips32el_linux.py index 05bf351af..e2f91d3ce 100644 --- a/examples/multithreading_mips32el_linux.py +++ b/examples/multithreading_mips32el_linux.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") diff --git a/examples/multithreading_x86_windows.py b/examples/multithreading_x86_windows.py index c0fa96a2d..07fea0ae3 100644 --- a/examples/multithreading_x86_windows.py +++ b/examples/multithreading_x86_windows.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") diff --git a/examples/netgear_6220_mips32el_linux.py b/examples/netgear_6220_mips32el_linux.py index 85f5f490e..5133575f1 100644 --- a/examples/netgear_6220_mips32el_linux.py +++ b/examples/netgear_6220_mips32el_linux.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # After mapping /proc there will be a /dev/mtdblock11 missing and crash # To fix this, diff --git a/examples/ntQuerySystemInfo_x86.py b/examples/ntQuerySystemInfo_x86.py index 5016ba7fe..ff16b74bf 100644 --- a/examples/ntQuerySystemInfo_x86.py +++ b/examples/ntQuerySystemInfo_x86.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/petya_8086_crack.py b/examples/petya_8086_crack.py index cb2676e53..6a16f5981 100644 --- a/examples/petya_8086_crack.py +++ b/examples/petya_8086_crack.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") @@ -46,8 +46,7 @@ def input_key(ql, addr, data): ql = Qiling(["rootfs/8086/petya/petya.DOS_MBR"], "rootfs/8086", console=False, - output="debug", - log_dir=".") + output="debug") ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/petya/out_1M.raw", 0x80)) ql.hook_code(pass_red, begin=0x886d, end=0x886d) ql.hook_code(input_key, begin=0x85f0, end=0x85f0) @@ -90,8 +89,7 @@ def first_stage(): ql = Qiling(["rootfs/8086/petya/petya.DOS_MBR"], "rootfs/8086", console=False, - output="debug", - log_dir=".") + output="debug") ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/petya/out_1M.raw", 0x80)) # Workaround for `until` in uc_emu_start not working with dynamic loaded code. ql.hook_code(stop, begin=petya_2nd_stage_start, end=petya_2nd_stage_start) diff --git a/examples/regdemo_x86_windows.py b/examples/regdemo_x86_windows.py index a6ecae237..80260385e 100644 --- a/examples/regdemo_x86_windows.py +++ b/examples/regdemo_x86_windows.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/rootfs/x8664_linux/kernel/hello.ko b/examples/rootfs/x8664_linux/kernel/hello.ko new file mode 100644 index 000000000..9b2505307 Binary files /dev/null and b/examples/rootfs/x8664_linux/kernel/hello.ko differ diff --git a/examples/rootfs/x8664_linux/kernel/m0hamed_rootkit.ko b/examples/rootfs/x8664_linux/kernel/m0hamed_rootkit.ko deleted file mode 100644 index 102fef831..000000000 Binary files a/examples/rootfs/x8664_linux/kernel/m0hamed_rootkit.ko and /dev/null differ diff --git a/examples/rootfs/x8664_windows/bin/cmdln64.exe b/examples/rootfs/x8664_windows/bin/cmdln64.exe new file mode 100644 index 000000000..7bb23d89e Binary files /dev/null and b/examples/rootfs/x8664_windows/bin/cmdln64.exe differ diff --git a/examples/rootfs/x86_linux/kernel/.gitignore b/examples/rootfs/x86_linux/kernel/.gitignore new file mode 100644 index 000000000..c52b18054 --- /dev/null +++ b/examples/rootfs/x86_linux/kernel/.gitignore @@ -0,0 +1,2 @@ +# dont keep raw malware +m0hamed_rootkit.ko diff --git a/examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko b/examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko deleted file mode 100644 index ae40b9567..000000000 Binary files a/examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko and /dev/null differ diff --git a/examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko.zip b/examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko.zip new file mode 100644 index 000000000..0e8fad666 Binary files /dev/null and b/examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko.zip differ diff --git a/examples/rootfs/x86_windows/bin/GandCrab.bin.zip b/examples/rootfs/x86_windows/bin/GandCrab.bin.zip deleted file mode 100644 index bcb0b3eec..000000000 Binary files a/examples/rootfs/x86_windows/bin/GandCrab.bin.zip and /dev/null differ diff --git a/examples/rootfs/x86_windows/bin/cmdln32.exe b/examples/rootfs/x86_windows/bin/cmdln32.exe new file mode 100644 index 000000000..ddaacf44c Binary files /dev/null and b/examples/rootfs/x86_windows/bin/cmdln32.exe differ diff --git a/examples/sality.py b/examples/sality.py index 1298b9160..6ed76ed85 100644 --- a/examples/sality.py +++ b/examples/sality.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import struct, sys, logging +import sys + +from unicorn import UcError sys.path.append("..") -from qiling import * -from qiling.os.windows.fncc import * +from qiling import Qiling +from qiling.os.const import STDCALL, POINTER, DWORD, STRING, HANDLE +from qiling.os.windows.fncc import winsdkapi +from qiling.os.windows.utils import string_appearance from qiling.os.windows.dlls.kernel32.fileapi import _CreateFile -from qiling.os.windows.utils import canonical_path -from qiling.loader.utils import ql_pe_check_archtype def init_unseen_symbols(ql, address, name, ordinal, dll_name): @@ -29,7 +31,7 @@ def init_unseen_symbols(ql, address, name, ordinal, dll_name): # LPDWORD lpThreadId # ); @winsdkapi(cc=STDCALL, dllname='kernel32_dll') -def sality_CreateThread(ql, address, params): +def hook_CreateThread(ql, address, params): # set thread handle return 1 @@ -51,7 +53,7 @@ def sality_CreateThread(ql, address, params): "dwFlagsAndAttributes": DWORD, "hTemplateFile": HANDLE }) -def sality_CreateFileA(ql, address, params): +def hook_CreateFileA(ql, address, params): lpFileName = params["lpFileName"] if lpFileName.startswith("\\\\.\\"): if ql.amsint32_driver: @@ -68,7 +70,8 @@ def _WriteFile(ql, address, params): lpBuffer = params["lpBuffer"] nNumberOfBytesToWrite = params["nNumberOfBytesToWrite"] lpNumberOfBytesWritten = params["lpNumberOfBytesWritten"] - lpOverlapped = params["lpOverlapped"] + #lpOverlapped = params["lpOverlapped"] + if hFile == 0xfffffff5: s = ql.mem.read(lpBuffer, nNumberOfBytesToWrite) ql.os.stdout.write(s) @@ -94,7 +97,7 @@ def _WriteFile(ql, address, params): "lpNumberOfBytesWritten": POINTER, "lpOverlapped": POINTER }) -def sality_WriteFile(ql, address, params): +def hook_WriteFile(ql, address, params): hFile = params["hFile"] lpBuffer = params["lpBuffer"] nNumberOfBytesToWrite = params["nNumberOfBytesToWrite"] @@ -105,7 +108,7 @@ def sality_WriteFile(ql, address, params): r, nNumberOfBytesToWrite = ql.amsint32_driver.os.io_Write(buffer) ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite)) except Exception as e: - logging.exception("") + ql.log.exception("") print("Exception = %s" % str(e)) r = 1 if r: @@ -121,15 +124,15 @@ def sality_WriteFile(ql, address, params): # DWORD dwNumServiceArgs, # LPCSTR *lpServiceArgVectors # ); -@winsdkapi(cc=STDCALL, dllname='kernel32_dll') -def sality_StartServiceA(ql, address, params): +@winsdkapi(cc=STDCALL, dllname='advapi32_dll') +def hook_StartServiceA(ql, address, params): try: hService = params["hService"] service_handle = ql.os.handle_manager.get(hService) if service_handle.name == "amsint32": if service_handle.name in ql.os.services: service_path = ql.os.services[service_handle.name] - service_path = canonical_path(ql, service_path) + service_path = ql.os.transform_to_real_path(service_path) ql.amsint32_driver = Qiling([service_path], ql.rootfs, output="debug") init_unseen_symbols(ql.amsint32_driver, ql.amsint32_driver.loader.dlls["ntoskrnl.exe"]+0xb7695, b"NtTerminateProcess", 0, "ntoskrnl.exe") #ql.amsint32_driver.debugger= ":9999" @@ -144,7 +147,7 @@ def sality_StartServiceA(ql, address, params): else: return 1 except Exception as e: - logging.exception("") + ql.log.exception("") print (e) @@ -160,10 +163,10 @@ def hook_stop_address(ql): # for this module ql.amsint32_driver = None # emulate some Windows API - ql.set_api("CreateThread", sality_CreateThread) - ql.set_api("CreateFileA", sality_CreateFileA) - ql.set_api("WriteFile", sality_WriteFile) - ql.set_api("StartServiceA", sality_StartServiceA) + ql.set_api("CreateThread", hook_CreateThread) + ql.set_api("CreateFileA", hook_CreateFileA) + ql.set_api("WriteFile", hook_WriteFile) + ql.set_api("StartServiceA", hook_StartServiceA) #init sality ql.hook_address(hook_stop_address, 0x40EFFB) ql.run() @@ -171,9 +174,9 @@ def hook_stop_address(ql): ql.os.set_function_args([0]) ql.hook_address(hook_stop_address, 0x4055FA) ql.run(0x4053B2) - logging.info("[+] test kill thread") + ql.log.info("test kill thread") if ql.amsint32_driver: - ql.amsint32_driver.os.io_Write(struct.pack(" +#include + +// These definitions are wrong, but tcc does not properly read them otherwise! +extern char** _acmdln; +extern wchar_t** _wcmdln; + +extern +char **__p__acmdln(void); +extern +wchar_t **__p__wcmdln(void); + +int main(int argc, char* argv[]) +{ + int i; + for (i = 0; i < argc; ++i) + { + printf("arg[%i]: <%s>\n", i, argv[i]); + } + + printf("_acmdln: <%s>\n", *_acmdln); + wprintf(L"_wcmdln: <%s>\n", *_wcmdln); + +#if !defined(_WIN64) + // Not present on x64 msvcrt.dll + printf("__p__acmdln: <%s>\n", *__p__acmdln()); + wprintf(L"__p__wcmdln: <%s>\n", *__p__wcmdln()); +#endif + + printf("GetCommandLineA: <%s>\n", GetCommandLineA()); + wprintf(L"GetCommandLineW: <%s>\n", GetCommandLineW()); + + return 0; +} diff --git a/examples/tendaac1518_httpd.py b/examples/tendaac1518_httpd.py index 750cc91b9..70e704d3c 100644 --- a/examples/tendaac1518_httpd.py +++ b/examples/tendaac1518_httpd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # 1. Download AC15 Firmware from https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip @@ -13,7 +13,7 @@ # notes: we are using rootfs in this example, so rootfs = squashfs-root # -import os, socket, sys, threading, logging +import os, socket, sys, threading sys.path.append("..") from qiling import * @@ -54,7 +54,7 @@ def nvram_listener(): def myvfork(ql): regreturn = 0 - logging.info("vfork() = %d" % regreturn) + ql.log.info("vfork() = %d" % regreturn) return regreturn diff --git a/examples/uefi_sanitized_heap.py b/examples/uefi_sanitized_heap.py index 4eb29fa8c..0f51188d4 100644 --- a/examples/uefi_sanitized_heap.py +++ b/examples/uefi_sanitized_heap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os, sys import sys diff --git a/examples/uselessdisk_x86_windows.py b/examples/uselessdisk_x86_windows.py index 2d5dffe56..6446f267f 100644 --- a/examples/uselessdisk_x86_windows.py +++ b/examples/uselessdisk_x86_windows.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys diff --git a/examples/wannacry_x86_windows_hookaddress.py b/examples/wannacry_x86_windows_hookaddress.py index 3d35157b6..10a580c22 100644 --- a/examples/wannacry_x86_windows_hookaddress.py +++ b/examples/wannacry_x86_windows_hookaddress.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys sys.path.append("..") from qiling import * diff --git a/examples/windows_trace.py b/examples/windows_trace.py index bba408ce1..e5e1ac92d 100644 --- a/examples/windows_trace.py +++ b/examples/windows_trace.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import argparse import sys diff --git a/qiling/__init__.py b/qiling/__init__.py index 7a9200d1c..5a5ddbac9 100644 --- a/qiling/__init__.py +++ b/qiling/__init__.py @@ -1,2 +1,2 @@ from .core import Qiling -from .__version__ import __version__ +from .__version__ import __version__ \ No newline at end of file diff --git a/qiling/__version__.py b/qiling/__version__.py index a955fdae1..e9b75806b 100644 --- a/qiling/__version__.py +++ b/qiling/__version__.py @@ -1 +1,2 @@ -__version__ = "1.2.1" +# NOTE: use "-dev" for dev branch +__version__ = "1.3" + "-dev" diff --git a/qiling/arch/a8086.py b/qiling/arch/a8086.py index 64564d70b..6bb78f715 100644 --- a/qiling/arch/a8086.py +++ b/qiling/arch/a8086.py @@ -1,3 +1,8 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + from unicorn import * from unicorn.x86_const import * diff --git a/qiling/arch/arch.py b/qiling/arch/arch.py index 5ee86663a..b81ca3d96 100644 --- a/qiling/arch/arch.py +++ b/qiling/arch/arch.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from abc import ABC, abstractmethod import struct +from .utils import * +from qiling.const import QL_ARCH, QL_ARCH_ALL, QL_ENDIAN, QL_OS, QL_OS_ALL, QL_OUTPUT, QL_DEBUGGER + class QlArch(ABC): def __init__(self, ql): self.ql = ql @@ -68,3 +71,19 @@ def context_save(self): # Unicorn's CPU state restore method def context_restore(self, saved_context): self.ql.uc.context_restore(saved_context) + + + def create_disassembler(self): + if self.ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB): + reg_cpsr = self.ql.reg.cpsr + else: + reg_cpsr = None + return ql_create_disassembler(self.ql.archtype, self.ql.archendian, reg_cpsr) + + + def create_assembler(self): + if self.ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB): + reg_cpsr = self.ql.reg.cpsr + else: + reg_cpsr = None + return ql_create_assembler(self.ql.archtype, self.ql.archendian, reg_cpsr) diff --git a/qiling/arch/arm.py b/qiling/arch/arm.py index d2d87fafb..7a262e9b7 100644 --- a/qiling/arch/arm.py +++ b/qiling/arch/arm.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) - -import logging +# from unicorn import * from unicorn.arm_const import * @@ -12,6 +10,7 @@ from .arch import QlArch from .arm_const import * + class QlArchARM(QlArch): def __init__(self, ql): super(QlArchARM, self).__init__(ql) @@ -79,7 +78,7 @@ def enable_vfp(self): #self.ql.reg.fpexc = 0x00000040 else: self.ql.reg.fpexc = 0x40000000 - logging.debug("[+] Enable ARM VFP") + self.ql.log.debug("Enable ARM VFP") def check_thumb(self): @@ -93,5 +92,5 @@ def check_thumb(self): mode = UC_MODE_ARM if (reg_cpsr & reg_cpsr_v) != 0: mode = UC_MODE_THUMB - logging.debug("[+] Enable ARM THUMB") + self.ql.log.debug("Enable ARM THUMB") return mode diff --git a/qiling/arch/arm64.py b/qiling/arch/arm64.py index b303c963c..80f0e43f1 100644 --- a/qiling/arch/arm64.py +++ b/qiling/arch/arm64.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn import * from unicorn.arm64_const import * diff --git a/qiling/arch/arm64_const.py b/qiling/arch/arm64_const.py index 13b756866..c706d10c9 100644 --- a/qiling/arch/arm64_const.py +++ b/qiling/arch/arm64_const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn.arm64_const import * diff --git a/qiling/arch/arm_const.py b/qiling/arch/arm_const.py index 45d7ebf28..db5e88822 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn.arm_const import * diff --git a/qiling/arch/mips.py b/qiling/arch/mips.py index 9cdf848d1..bb1ec0462 100644 --- a/qiling/arch/mips.py +++ b/qiling/arch/mips.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn import * from unicorn.mips_const import * diff --git a/qiling/arch/mips_const.py b/qiling/arch/mips_const.py index 9bb4445ef..8dd8d48a9 100644 --- a/qiling/arch/mips_const.py +++ b/qiling/arch/mips_const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn.mips_const import * diff --git a/qiling/arch/register.py b/qiling/arch/register.py index 103f086a6..2ecaa5fe7 100644 --- a/qiling/arch/register.py +++ b/qiling/arch/register.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# class QlRegisterManager(): """ diff --git a/qiling/arch/utils.py b/qiling/arch/utils.py index 33682b765..48f602745 100644 --- a/qiling/arch/utils.py +++ b/qiling/arch/utils.py @@ -1,8 +1,106 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# """ This module is intended for general purpose functions that are only used in qiling.arch -""" \ No newline at end of file +""" + +from unicorn import UcError, UC_ERR_READ_UNMAPPED, UC_ERR_FETCH_UNMAPPED +from keystone import * +from capstone import * + +from qiling.const import QL_ARCH, QL_ARCH_ALL, QL_ENDIAN, QL_OS, QL_OS_ALL, QL_OUTPUT, QL_DEBUGGER, QL_ARCH_32BIT, QL_ARCH_64BIT, QL_ARCH_16BIT +from qiling.exception import * + + +def ql_create_disassembler(archtype, archendian, reg_cpsr=None): + if archtype == QL_ARCH.ARM: # QL_ARM + mode = CS_MODE_ARM + if archendian == QL_ENDIAN.EB: + # TODO: Test for big endian. + reg_cpsr_v = 0b100000 + # reg_cpsr_v = 0b000000 + else: + reg_cpsr_v = 0b100000 + + if reg_cpsr & reg_cpsr_v != 0: + mode = CS_MODE_THUMB + + if archendian == QL_ENDIAN.EB: + md = Cs(CS_ARCH_ARM, mode) + # md = Cs(CS_ARCH_ARM, mode + CS_MODE_BIG_ENDIAN) + else: + md = Cs(CS_ARCH_ARM, mode) + + elif archtype == QL_ARCH.ARM_THUMB: + md = Cs(CS_ARCH_ARM, CS_MODE_THUMB) + + elif archtype == QL_ARCH.X86: # QL_X86 + md = Cs(CS_ARCH_X86, CS_MODE_32) + + elif archtype == QL_ARCH.X8664: # QL_X86_64 + md = Cs(CS_ARCH_X86, CS_MODE_64) + + elif archtype == QL_ARCH.ARM64: # QL_ARM64 + md = Cs(CS_ARCH_ARM64, CS_MODE_ARM) + + elif archtype == QL_ARCH.A8086: # QL_A8086 + md = Cs(CS_ARCH_X86, CS_MODE_16) + + elif archtype == QL_ARCH.MIPS: # QL_MIPS32 + if archendian == QL_ENDIAN.EB: + md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN) + else: + md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) + + else: + raise QlErrorArch("Unknown arch defined in utils.py (debug output mode)") + + return md + +def ql_create_assembler(archtype, archendian, reg_cpsr=None): + if archtype == QL_ARCH.ARM: # QL_ARM + mode = KS_MODE_ARM + if archendian == QL_ENDIAN.EB: + # TODO: Test for big endian. + reg_cpsr_v = 0b100000 + # reg_cpsr_v = 0b000000 + else: + reg_cpsr_v = 0b100000 + + if reg_cpsr & reg_cpsr_v != 0: + mode = KS_MODE_THUMB + + if archendian == QL_ENDIAN.EB: + ks = Ks(KS_ARCH_ARM, mode) + # md = Cs(CS_ARCH_ARM, mode + CS_MODE_BIG_ENDIAN) + else: + ks = Ks(KS_ARCH_ARM, mode) + + elif archtype == QL_ARCH.ARM_THUMB: + ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB) + + elif archtype == QL_ARCH.X86: # QL_X86 + ks = Ks(KS_ARCH_X86, KS_MODE_32) + + elif archtype == QL_ARCH.X8664: # QL_X86_64 + ks = Ks(KS_ARCH_X86, KS_MODE_64) + + elif archtype == QL_ARCH.ARM64: # QL_ARM64 + ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN) + + elif archtype == QL_ARCH.A8086: # QL_A8086 + ks = Ks(KS_ARCH_X86, KS_MODE_16) + + elif archtype == QL_ARCH.MIPS: # QL_MIPS32 + if archendian == QL_ENDIAN.EB: + ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + KS_MODE_BIG_ENDIAN) + else: + ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + KS_MODE_LITTLE_ENDIAN) + + else: + raise QlErrorArch("Unknown arch defined in utils.py (debug output mode)") + + return ks \ No newline at end of file diff --git a/qiling/arch/x86.py b/qiling/arch/x86.py index f3b4ef11b..e5205951b 100644 --- a/qiling/arch/x86.py +++ b/qiling/arch/x86.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + from unicorn import * from unicorn.x86_const import * @@ -12,6 +13,7 @@ from qiling.const import * from qiling.exception import * + class QlArchX86(QlArch): def __init__(self, ql): super(QlArchX86, self).__init__(ql) @@ -70,7 +72,7 @@ def __init__(self, ql): x64_register_mappings = [ reg_map_8, reg_map_16, reg_map_32, reg_map_64, - reg_map_cr, reg_map_st, reg_map_misc, reg_map_r + reg_map_cr, reg_map_st, reg_map_misc, reg_map_r, reg_map_seg_base ] for reg_maper in x64_register_mappings: @@ -122,6 +124,8 @@ def get_reg_bit(self, register): class GDTManager: # Added GDT management module. def __init__(self, ql, GDT_ADDR = QL_X86_GDT_ADDR, GDT_LIMIT = QL_X86_GDT_LIMIT, GDT_ENTRY_ENTRIES = 16): + ql.log.debug(f"Map GDT at {hex(GDT_ADDR)} with GDT_LIMIT={GDT_LIMIT}") + if ql.mem.is_mapped(GDT_ADDR, GDT_LIMIT) == False: ql.mem.map(GDT_ADDR, GDT_LIMIT, info="[GDT]") @@ -142,11 +146,12 @@ def register_gdt_segment(self, index, SEGMENT_ADDR, SEGMENT_SIZE, SPORT, RPORT): self.ql.mem.map(SEGMENT_ADDR, SEGMENT_ADDR, info="[FS/GS]") if index < 0 or index >= self.gdt_number: - raise QlGDTError("[!] Ql GDT register index error!") + raise QlGDTError("Ql GDT register index error!") # create GDT entry, then write GDT entry into GDT table gdt_entry = self._create_gdt_entry(SEGMENT_ADDR, SEGMENT_SIZE, SPORT, QL_X86_F_PROT_32) self.ql.mem.write(self.gdt_addr + (index << 3), gdt_entry) # self.gdt_used[index] = True + self.ql.log.debug(f"Write to {hex(self.gdt_addr + (index << 3))} for new entry {gdt_entry}") def get_gdt_buf(self, start, end): diff --git a/qiling/arch/x86_const.py b/qiling/arch/x86_const.py index 6786fc292..8e15e702b 100644 --- a/qiling/arch/x86_const.py +++ b/qiling/arch/x86_const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn.x86_const import * @@ -106,6 +106,11 @@ "rip": UC_X86_REG_RIP, } +reg_map_seg_base = { + "fsbase" : UC_X86_REG_FS_BASE, + "gsbase" : UC_X86_REG_GS_BASE +} + reg_map_r = { "r8b": UC_X86_REG_R8B, "r9b": UC_X86_REG_R9B, diff --git a/qiling/const.py b/qiling/const.py index ec2c6137f..541739cf0 100644 --- a/qiling/const.py +++ b/qiling/const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from enum import IntEnum @@ -48,13 +48,6 @@ class QL_INTERCEPT(IntEnum): ENTER = 2 EXIT = 3 - -D_INFO = 1 # General debug information -D_PROT = 2 # Protocol level debug, print out open file flag -D_CTNT = 3 # Print out content. File content or content of a tcp stream -D_RPRT = 4 # Reporting output, main summarizing purposes -D_DRPT = 5 # Detailed Report, with address - QL_DEBUGGER_ALL = [QL_DEBUGGER.IDAPRO, QL_DEBUGGER.GDB, QL_DEBUGGER.QDB] QL_ARCH_ALL = [QL_ARCH.X86, QL_ARCH.X8664, QL_ARCH.ARM, QL_ARCH.ARM_THUMB, QL_ARCH.ARM64, QL_ARCH.MIPS, QL_ARCH.A8086] QL_ARCH_16BIT = [QL_ARCH.A8086] diff --git a/qiling/core.py b/qiling/core.py index f01809285..084169be5 100644 --- a/qiling/core.py +++ b/qiling/core.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from configparser import ConfigParser -import ctypes, logging, ntpath, os, pickle, platform +import ctypes, ntpath, os, pickle, platform import io from sys import stdin, stdout # See https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports @@ -17,7 +17,7 @@ from .os.memory import QlMemoryManager from .loader.loader import QlLoader -from .const import D_DRPT, QL_ARCH_ENDIAN, QL_ENDIAN, QL_INTERCEPT, QL_OS_POSIX, QL_OS_ALL, QL_OUTPUT, QL_OS +from .const import QL_ARCH_ENDIAN, QL_ENDIAN, QL_INTERCEPT, QL_OS_POSIX, QL_OS_ALL, QL_OUTPUT, QL_OS from .exception import QlErrorFileNotFound, QlErrorArch, QlErrorOsType, QlErrorOutput from .utils import * from .core_struct import QlCoreStructs @@ -31,6 +31,7 @@ def __init__( argv=None, rootfs=None, env=None, + code=None, shellcoder=None, ostype=None, archtype=None, @@ -39,11 +40,11 @@ def __init__( verbose=1, profile=None, console=True, - log_dir=None, - log_split=None, - append=None, + log_file=None, + log_override=None, libcache = False, multithread = False, + filters = None, stop_on_stackpointer = False, stop_on_exit_trap = False, stdin=0, @@ -63,6 +64,7 @@ def __init__( self._argv = argv self._rootfs = rootfs self._env = env if env else {} + self._code = code self._shellcoder = shellcoder self._ostype = ostype self._archtype = archtype @@ -71,11 +73,12 @@ def __init__( self._pointersize = None self._profile = profile self._console = console - self._log_dir = log_dir - self._log_split = log_split - self._append = append + self._log_file = log_file self._multithread = multithread self._log_file_fd = None + self._log_filter = None + self._log_override = log_override + self._filters = filters self._platform = ostype_convert(platform.system().lower()) self._internal_exception = None self._uc = None @@ -95,7 +98,6 @@ def __init__( self._debug_stop = False self._debugger = None self._root = False - self._filter = None ############################### # Properties configured later # @@ -113,20 +115,28 @@ def __init__( ############## # Shellcode? # ############## + + # for Legacy if self._shellcoder: - if (self._ostype and type(self._ostype) == str) and (self._archtype and type(self._archtype) == str): - self._ostype = ostype_convert(self._ostype.lower()) + self._code = self._shellcoder + + if self._code or (self._archtype and type(self._archtype) == str): + if (self._archtype and type(self._archtype) == str): self._archtype = arch_convert(self._archtype.lower()) - self._argv = ["qilingshellcoder"] - if self._rootfs is None: - self._rootfs = "." + + if (self._ostype and type(self._ostype) == str): + self._ostype = ostype_convert(self._ostype.lower()) + + self._argv = ["qilingcode"] + if self._rootfs is None: + self._rootfs = "." # file check - if self._shellcoder is None: + if self._code is None: if not os.path.exists(str(self._argv[0])): - raise QlErrorFileNotFound("[!] Target binary not found") + raise QlErrorFileNotFound("Target binary not found") if not os.path.exists(self._rootfs): - raise QlErrorFileNotFound("[!] Target rootfs not found") + raise QlErrorFileNotFound("Target rootfs not found") self._path = (str(self._argv[0])) self._targetname = ntpath.basename(self._argv[0]) @@ -134,7 +144,7 @@ def __init__( ########## # Loader # ########## - if self._shellcoder is None: + if self._code is None: guessed_archtype, guessed_ostype, guessed_archendian = ql_guess_emu_env(self._path) if self._ostype is None: self._ostype = guessed_ostype @@ -144,41 +154,35 @@ def __init__( self._archendian = guessed_archendian if not ql_is_valid_ostype(self._ostype): - raise QlErrorOsType("[!] Invalid OSType") + raise QlErrorOsType("Invalid OSType") if not ql_is_valid_arch(self._archtype): - raise QlErrorArch("[!] Invalid Arch %s" % self._archtype) + raise QlErrorArch("Invalid Arch %s" % self._archtype) self._loader = loader_setup(self._ostype, self) ##################### # Profile & Logging # ##################### - self._profile = profile_setup(self.ostype, self.profile, self) - if self._append == None: - self._append = self._profile["MISC"]["append"] - if self._log_dir == None: - self._log_dir = self._profile["LOG"]["dir"] - if self._log_split == None: - self._log_split = self._profile.getboolean('LOG', 'split') + self._profile, debugmsg = profile_setup(self.ostype, self.profile, self) # Log's configuration # Setup output mode. self._output = output_convert(self._output) - # We only use the root logger now. - ql_setup_logger(self, - self._log_dir, - self._targetname + self._append + ".qlog", - self._log_split, self._console, - self._filter, - self._multithread) - # For compatibility. - self._log_file_fd = logging.getLogger() - - ql_resolve_logger_level(self._output, self._verbose) - + self._log_file_fd, self._log_filter = ql_setup_logger(self, + self._log_file, + self._console, + self._filters, + self._multithread, + self._log_override) + + self.log.setLevel(ql_resolve_logger_level(self._output, self._verbose)) + + # Now that the logger is configured, we can log profile debug msg: + self.log.debug(debugmsg) + ######################## # Archbit & Endianness # ######################## @@ -186,7 +190,7 @@ def __init__( self._pointersize = (self.archbit // 8) # Endian for shellcode needs to set manually - if self._shellcoder: + if self._code: self._archendian = QL_ENDIAN.EL if bigendian == True and self._archtype in (QL_ARCH_ENDIAN): self._archendian = QL_ENDIAN.EB @@ -261,42 +265,22 @@ def os(self) -> "QlOs": """ return self._os - ################## - # Qiling Options # - ################## - - # If an option doesn't have a setter, it means that it can be only set during Qiling.__init__ - # TODO: Rename to suffix? @property - def append(self) -> str: - """ Suffix appended to the filename. - Used when writing to file (e.g. logging). - - Type: str - Example: Qiling(append="dbg") - """ - return self._append + def log(self) -> logging.Logger: + """ Returns the logger this Qiling instance uses. - @property - def log_dir(self) -> str: - """ Specify the logging directory. - Use with ql.log_split. + You can override this log by passing `log_override=your_log` to Qiling.__init__ - Type: str - Example: Qiling(log_dir=".") + Type: logging.Logger + Example: ql.log.info("This goes to terminal") """ - return self._log_dir - - @property - def log_split(self) -> bool: - """ Specify whether spliting logs within multiprocess/multithread context. - Use with ql.log_dir. + return self._log_file_fd - Type: bool - Example: Qiling(log_split=True) - """ - return self._log_split + ################## + # Qiling Options # + ################## + # If an option doesn't have a setter, it means that it can be only set during Qiling.__init__ @property def console(self) -> bool: """ Specify whether enabling console output. @@ -306,6 +290,15 @@ def console(self) -> bool: """ return self._console + @property + def log_file(self) -> str: + """ Log to a file. + + Type: str + Example: Qiling(log_file="./ql.log") + """ + return self._log_file + @property def multithread(self) -> bool: """ Specify whether multithread has been enabled. @@ -371,7 +364,7 @@ def ostype(self) -> int: - "windows" : Windows - "uefi" : UEFI - "dos" : DOS - Example: Qiling(shellcoder=b"\x90", ostype="macos", archtype="x8664", bigendian=False) + Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False) """ return self._ostype @@ -391,7 +384,7 @@ def archtype(self) -> int: - "arm_thumb" : ARM with thumb mode. - "arm64" : ARM64 - "a8086" : 8086 - Example: Qiling(shellcoder=b"\x90", ostype="macos", archtype="x8664", bigendian=False) + Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False) """ return self._archtype @@ -403,7 +396,7 @@ def archendian(self) -> int: This option only takes effect for shellcode. Type: int - Example: Qiling(shellcoder=b"\x90", ostype="macos", archtype="x8664", bigendian=False) + Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False) """ return self._archendian @@ -424,15 +417,15 @@ def pointersize(self) -> int: return self._pointersize @property - def shellcoder(self) -> bytes: + def code(self) -> bytes: """ The shellcode to execute. Note: It can't be used with "argv" parameter. Type: bytes - Example: Qiling(shellcoder=b"\x90", ostype="macos", archtype="x8664", bigendian=False) + Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False) """ - return self._shellcoder + return self._code @property def path(self) -> str: @@ -450,14 +443,6 @@ def targetname(self) -> str: """ return self._targetname - @property - def log_file_fd(self) -> logging.Logger: - """ Only reserved for compatibility, never use it directly. - - Type: logging.Logger - """ - return self._log_file_fd - @property def platform(self): """ Specify current platform where Qiling runs on. @@ -487,8 +472,8 @@ def stdin(self) -> io.IOBase: """ Stdin of the program. Can be any object which implements (even part of) io.IOBase. Type: io.Base - Example: ql = Qiling(stdin=sys.stdin) - ql.stdin = sys.stdin + Example: - ql = Qiling(stdin=sys.stdin) + - ql.stdin = sys.stdin """ return self._stdin @@ -501,8 +486,8 @@ def stdout(self) -> io.IOBase: """ Stdout of the program. Can be any object which implements (even part of) io.IOBase. Type: io.Base - Example: ql = Qiling(stdout=sys.stdout) - ql.stdout = sys.stdout + Example: - ql = Qiling(stdout=sys.stdout) + - ql.stdout = sys.stdout """ return self._stdout @@ -515,8 +500,8 @@ def stderr(self) -> io.IOBase: """ Stdout of the program. Can be any object which implements (even part of) io.IOBase. Type: io.Base - Example: ql = Qiling(stderr=sys.stderr) - ql.stderr = sys.stderr + Example: - ql = Qiling(stderr=sys.stderr) + - ql.stderr = sys.stderr """ return self._stderr @@ -529,8 +514,8 @@ def libcache(self) -> bool: """ Whether cache dll files. Only take effect in Windows emulation. Type: bool - Example: ql = Qiling(libcache=False) - ql.libcache = True + Example: - ql = Qiling(libcache=False) + - ql.libcache = True """ return self._libcache @@ -551,8 +536,8 @@ def output(self) -> int: - "debug": set the log level to logging.DEBUG. - "disasm": diasm each executed instruction. - "dump": the most verbose output, dump registers and diasm the function blocks. - Example: ql = Qiling(output="off") - ql.output = "off" + Example: - ql = Qiling(output="off") + - ql.output = "off" """ return self._output @@ -562,7 +547,7 @@ def output(self, op): self._output = output_convert(op) else: self._output = op - ql_resolve_logger_level(self._output, self._verbose) + self.log.setLevel(ql_resolve_logger_level(self._output, self._verbose)) self.os.setup_output() @property @@ -577,15 +562,15 @@ def verbose(self): - 0 : logging.WARNING, almost no additional logs except the program output. - >=1: logging.INFO, the default logging level. - >=4: logging.DEBUG. - Example: ql = Qiling(verbose=5) - ql.verbose = 0 + Example: - ql = Qiling(verbose=5) + - ql.verbose = 0 """ return self._verbose @verbose.setter def verbose(self, v): self._verbose = v - ql_resolve_logger_level(self._output, self._verbose) + self.log.setLevel(ql_resolve_logger_level(self._output, self._verbose)) self.os.setup_output() @property @@ -653,17 +638,23 @@ def root(self, root): self._root = root @property - def filter(self) -> List[str]: + def filters(self) -> List[str]: """ Filter logs with regex. Type: List[str] - Example: ql.filter = [r'^open'] + Example: - Qiling(filters=[r'^exit']) + - ql.filters = [r'^open'] """ - return self._filter + return self._filters - @filter.setter - def filter(self, ft): - self._filter = ft + @filters.setter + def filters(self, ft): + self._filters = ft + if self._log_filter is None: + self._log_filter = RegexFilter(ft) + self.log.addFilter(self._log_filter) + else: + self._log_filter.update_filters(ft) @property def uc(self): @@ -717,7 +708,7 @@ def _check_sp(ql, address, size): if not ql.loader.skip_exit_check: sp = ql._initial_sp - ql.reg.arch_sp if sp < 0: - logging.info('Process returned from entrypoint (stackpointer)!') + self.log.info('Process returned from entrypoint (stackpointer)!') ql.emu_stop() self.hook_code(_check_sp) @@ -725,7 +716,7 @@ def _check_sp(ql, address, size): # Stop when running to exit trap address if self.stop_options.exit_trap: def _exit_trap(ql): - logging.info('Process returned from entrypoint (exit_trap)!') + self.log.info('Process returned from entrypoint (exit_trap)!') ql.emu_stop() self.hook_address(_exit_trap, self._exit_trap_addr) @@ -734,10 +725,10 @@ def write_exit_trap(self): self._initial_sp = self.reg.arch_sp if self.stop_options.any: if not self.loader.skip_exit_check: - logging.debug(f'Setting up exit trap at 0x{hex(self._exit_trap_addr)}') + self.log.debug(f'Setting up exit trap at 0x{hex(self._exit_trap_addr)}') self.stack_write(0, self._exit_trap_addr) elif self.stop_options.exit_trap: - logging.debug(f'Loader {self.loader} requested to skip exit_trap!') + self.log.debug(f'Loader {self.loader} requested to skip exit_trap!') ############### @@ -776,54 +767,6 @@ def patch(self, addr, code, file_name=b''): else: self.patch_lib.append((addr, code, file_name.decode())) - # Depreciated. Please use logging directly. - # Will be removed in later release. - def nprint(self, *args, **kw): - if type(self.console) is bool: - pass - else: - raise QlErrorOutput("[!] console must be True or False") - - # FIXME: this is due to console must be able to update during runtime - if self.log_file_fd is not None: - if self.multithread == True and self.os.thread_management is not None and self.os.thread_management.cur_thread is not None: - fd = self.os.thread_management.cur_thread.log_file_fd - else: - fd = self.log_file_fd - - args = map(str, args) - msg = kw.get("sep", " ").join(args) - - if kw.get("end", None) != None: - msg += kw["end"] - - fd.info(msg) - - # Depreciated. Please use logging directly. - # Will be removed in later release. - def dprint(self, level, *args, **kw): - try: - self.verbose = int(self.verbose) - except: - raise QlErrorOutput("[!] Verbose muse be int") - - if type(self.verbose) != int or self.verbose > 99 or (self.verbose > 1 and self.output not in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP)): - raise QlErrorOutput("[!] Verbose > 1 must use with QL_OUTPUT.DEBUG or else ql.verbose must be 0") - - if self.output == QL_OUTPUT.DUMP: - self.verbose = 99 - - if int(self.verbose) >= level and self.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP): - if int(self.verbose) >= D_DRPT: - try: - current_pc = self.reg.arch_pc - except: - current_pc = 0 - - args = (("0x%x:" % current_pc), *args) - - self.nprint(*args, **kw) - # save all qiling instance states def save(self, reg=True, mem=True, fd=False, cpu_context=False, os_context=False, loader=False, snapshot=None): @@ -880,6 +823,7 @@ def restore(self, saved_states=None, snapshot=None): if "loader" in saved_states: self.loader.restore(saved_states["loader"]) + # Either hook or replace syscall/api with custom api/syscall # - if intercept is None, replace syscall with custom function # - if intercept is ENTER/EXIT, hook syscall at enter/exit with custom function @@ -888,107 +832,66 @@ def restore(self, saved_states=None, snapshot=None): # - ql.set_syscall("write", my_syscall_write) # TODO: Add correspoinding API in ql.os! def set_syscall(self, target_syscall, intercept_function, intercept = None): - if intercept == QL_INTERCEPT.ENTER: - if isinstance(target_syscall, int): - self.os.dict_posix_onEnter_syscall_by_num[target_syscall] = intercept_function - else: - syscall_name = "ql_syscall_" + str(target_syscall) - self.os.dict_posix_onEnter_syscall[syscall_name] = intercept_function - - elif intercept == QL_INTERCEPT.EXIT: - if self.ostype in (QL_OS_POSIX): - if isinstance(target_syscall, int): - self.os.dict_posix_onExit_syscall_by_num[target_syscall] = intercept_function - else: - syscall_name = "ql_syscall_" + str(target_syscall) - self.os.dict_posix_onExit_syscall[syscall_name] = intercept_function - - else: - if self.ostype in (QL_OS_POSIX): - if isinstance(target_syscall, int): - self.os.dict_posix_syscall_by_num[target_syscall] = intercept_function - else: - syscall_name = "ql_syscall_" + str(target_syscall) - self.os.dict_posix_syscall[syscall_name] = intercept_function - - elif self.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): - self.set_api(target_syscall, intercept_function) + self.os.set_syscall(target_syscall, intercept_function, intercept) # Either replace or hook API # - if intercept is None, replace API with custom function # - if intercept is ENTER/EXIT, hook API at enter/exit with custom function def set_api(self, api_name, intercept_function, intercept = None): - if self.ostype == QL_OS.UEFI: - api_name = "hook_" + str(api_name) - - if intercept == QL_INTERCEPT.ENTER: - if self.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): - self.os.user_defined_api_onenter[api_name] = intercept_function - else: - self.os.add_function_hook(api_name, intercept_function, intercept) - - elif intercept == QL_INTERCEPT.EXIT: - if self.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): - self.os.user_defined_api_onexit[api_name] = intercept_function - else: - self.os.add_function_hook(api_name, intercept_function, intercept) - - else: - if self.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): - self.os.user_defined_api[api_name] = intercept_function - else: - self.os.add_function_hook(api_name, intercept_function) + self.os.set_api(api_name, intercept_function, intercept) + # Map "ql_path" to any objects which implements QlFsMappedObject. def add_fs_mapper(self, ql_path, real_dest): self.os.fs_mapper.add_fs_mapping(ql_path, real_dest) + # push to stack bottom, and update stack register def stack_push(self, data): return self.arch.stack_push(data) + # pop from stack bottom, and update stack register def stack_pop(self): return self.arch.stack_pop() + # read from stack, at a given offset from stack bottom # NOTE: unlike stack_pop(), this does not change stack register def stack_read(self, offset): return self.arch.stack_read(offset) + # write to stack, at a given offset from stack bottom # NOTE: unlike stack_push(), this does not change stack register def stack_write(self, offset, data): return self.arch.stack_write(offset, data) + # Assembler/Diassembler API @property def assembler(self): return self.create_assembler() + @property - def disassember(self): + def disassembler(self): return self.create_disassembler() - + + def create_disassembler(self): - if self.archtype in (QL_ARCH.ARM, QL_ARCH.ARM64, QL_ARCH.ARM_THUMB): - reg_cpsr = self.reg.cpsr - else: - reg_cpsr = None - return ql_create_disassembler(self.archtype, self.archendian, reg_cpsr) - + return self.arch.create_disassembler() + + def create_assembler(self): - if self.archtype in (QL_ARCH.ARM, QL_ARCH.ARM64, QL_ARCH.ARM_THUMB): - reg_cpsr = self.reg.cpsr - else: - reg_cpsr = None - return ql_create_assembler(self.archtype, self.archendian, reg_cpsr) + return self.arch.create_assembler() # stop emulation def emu_stop(self): self.uc.emu_stop() + # start emulation def emu_start(self, begin, end, timeout=0, count=0): self.uc.emu_start(begin, end, timeout, count) diff --git a/qiling/core_hooks.py b/qiling/core_hooks.py index a0ac023b5..438180160 100644 --- a/qiling/core_hooks.py +++ b/qiling/core_hooks.py @@ -1,14 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# ############################################## # These are part of the core.py Qiling class # # handling hooks # ############################################## -import logging from unicorn import * from .utils import catch_KeyboardInterrupt @@ -55,7 +54,7 @@ def __init__(self, callback, intno, user_data=None): def check(self, ql, intno): - logging.debug("[+] Received Interupt: %i Hooked Interupt: %i" % (intno, self.intno)) + ql.log.debug("Received Interupt: %i Hooked Interupt: %i" % (intno, self.intno)) if intno < 0 or self.intno == intno: return True return False diff --git a/qiling/core_struct.py b/qiling/core_struct.py index bb13de84d..43aac3704 100644 --- a/qiling/core_struct.py +++ b/qiling/core_struct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# ############################################## # These are part of the core.py Qiling class # @@ -10,6 +10,7 @@ ############################################## import struct + from .const import QL_ENDIAN from .exception import QlErrorStructConversion @@ -34,7 +35,7 @@ def __init__(self, endian, bit): } if bit not in handlers: - raise QlErrorStructConversion("[!] Unsupported Qiling struct conversion") + raise QlErrorStructConversion("Unsupported Qiling struct conversion") p, ps, up, ups = handlers[bit] diff --git a/qiling/debugger/debugger.py b/qiling/debugger/debugger.py index 5f2c46738..233f655e6 100644 --- a/qiling/debugger/debugger.py +++ b/qiling/debugger/debugger.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -from qiling import * +# + +from qiling import Qiling class QlDebugger(): diff --git a/qiling/debugger/disassember.py b/qiling/debugger/disassember.py index 037eeecda..d04671897 100644 --- a/qiling/debugger/disassember.py +++ b/qiling/debugger/disassember.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + from elftools.elf.elffile import ELFFile -from qiling import * + +from qiling import Qiling from qiling.const import * from capstone import * diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index a1d9837df..6495072d0 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # gdbserver --remote-debug 0.0.0.0:9999 /path/to binary # documentation: according to https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol from unicorn import * -import struct, os, re, socket, logging +import struct, os, re, socket from binascii import unhexlify from .utils import QlGdbUtils @@ -31,14 +31,6 @@ GDB_SIGNAL_TRAP = 5 GDB_SIGNAL_BUS = 10 -def checksum(data): - checksum = 0 - for c in data: - if type(c) == str: - checksum += (ord(c)) - else: - checksum += c - return checksum & 0xff class QlGdb(QlDebugger, object): """docstring for Debugsession""" @@ -61,16 +53,16 @@ def __init__(self, ql, ip, port): self.ip = ip self.port = port - if ql.shellcoder: + if ql.code: load_address = ql.os.entry_point - exit_point = load_address + len(ql.shellcoder) + exit_point = load_address + len(ql.code) else: load_address = ql.loader.load_address exit_point = load_address + os.path.getsize(ql.path) self.gdb.initialize(self.ql, exit_point=exit_point, mappings=[(hex(load_address))]) - if self.ql.ostype in (QL_OS.LINUX, QL_OS.FREEBSD) and not self.ql.shellcoder: + if self.ql.ostype in (QL_OS.LINUX, QL_OS.FREEBSD) and not self.ql.code: self.entry_point = self.ql.os.elf_entry else: self.entry_point = self.ql.os.entry_point @@ -130,7 +122,7 @@ def incomplete_hex_check(hexchar): return unhexlify(rawbin_escape) def setup_server(self): - logging.info("gdb> Listening on %s:%u" % (self.ip, self.port)) + self.ql.log.info("gdb> Listening on %s:%u" % (self.ip, self.port)) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -452,7 +444,7 @@ def handle_P(subcmd): if reg_name == self.ql.reg.arch_pc_name: self.gdb.current_address = reg_data - logging.info("gdb> Write to register %s with %x\n" % (self.tables[self.ql.archtype][reg_index], reg_data)) + self.ql.log.info("gdb> Write to register %s with %x\n" % (self.tables[self.ql.archtype][reg_index], reg_data)) self.send('OK') @@ -496,7 +488,7 @@ def handle_q(subcmd): file_contents = f.read() self.send("l%s" % file_contents) else: - logging.info("gdb> Platform is not supported by xml or xml file not found: %s\n" % (xfercmd_file)) + self.ql.log.info("gdb> Platform is not supported by xml or xml file not found: %s\n" % (xfercmd_file)) self.send("l") @@ -508,7 +500,7 @@ def handle_q(subcmd): self.send("l" + file_contents) elif subcmd.startswith('Xfer:auxv:read::'): - if self.ql.shellcoder: + if self.ql.code: return if self.ql.ostype in (QL_OS.LINUX, QL_OS.FREEBSD) : if self.ql.archbit == 64: @@ -688,7 +680,7 @@ def handle_v(subcmd): else: file_abspath = self.ql.os.transform_to_real_path(file_path) - logging.debug("gdb> target file: %s" % (file_abspath)) + self.ql.log.debug("gdb> target file: %s" % (file_abspath)) if os.path.exists(file_abspath) and not (file_path).startswith("/proc"): fd = os.open(file_abspath, flags, mode) self.send("F%x" % fd) @@ -721,7 +713,7 @@ def handle_v(subcmd): self.send('OK') elif subcmd.startswith('Cont'): - logging.debug("gdb> Cont command received: %s" % subcmd) + self.ql.log.debug("gdb> Cont command received: %s" % subcmd) if subcmd == 'Cont?': self.send('vCont;c;C;t;s;S;r') elif subcmd.startswith ("Cont;"): @@ -812,9 +804,9 @@ def handle_exclaim(subcmd): if cmd not in commands: self.send('') - logging.info("gdb> Command not supported: %s\n" %(cmd)) + self.ql.log.info("gdb> Command not supported: %s\n" %(cmd)) continue - logging.debug("gdb> received: %s%s" % (cmd, subcmd)) + self.ql.log.debug("gdb> received: %s%s" % (cmd, subcmd)) commands[cmd](subcmd) self.close() @@ -852,16 +844,24 @@ def receive(self): self.close() raise + def checksum(self, data): + checksum = 0 + for c in data: + if type(c) == str: + checksum += (ord(c)) + else: + checksum += c + return checksum & 0xff def send(self, msg): """Send a packet to the GDB client""" if type(msg) == str: - self.send_raw('$%s#%.2x' % (msg, checksum(msg))) + self.send_raw('$%s#%.2x' % (msg, self.checksum(msg))) else: - self.clientsocket.send(b'$%s#%.2x' % (msg, checksum(msg))) + self.clientsocket.send(b'$%s#%.2x' % (msg, self.checksum(msg))) self.netout.flush() - logging.debug("gdb> send: $%s#%.2x" % (msg, checksum(msg))) + self.ql.log.debug("gdb> send: $%s#%.2x" % (msg, self.checksum(msg))) def send_raw(self, r): self.netout.write(r) diff --git a/qiling/debugger/gdb/utils.py b/qiling/debugger/gdb/utils.py index 9a8fdc618..fed8dd84e 100644 --- a/qiling/debugger/gdb/utils.py +++ b/qiling/debugger/gdb/utils.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import logging from unicorn import * from qiling.const import * + class QlGdbUtils(object): def __init__(self): self.current_address = 0x0 @@ -56,7 +56,7 @@ def dbg_hook(self, ql, address, size): self.breakpoint_count += 1 self.ql.os.stop() self.last_bp = address - logging.info("gdb> Breakpoint found, stop at address: 0x%x" % address) + ql.log.info("gdb> Breakpoint found, stop at address: 0x%x" % address) elif address == self.last_bp: self.last_bp = 0x0 @@ -64,11 +64,11 @@ def dbg_hook(self, ql, address, size): self.has_soft_bp = hit_soft_bp if self.current_address + size == self.exit_point: - logging.debug("gdb> emulation entrypoint at 0x%x" % (self.entry_point)) - logging.debug("gdb> emulation exitpoint at 0x%x" % (self.exit_point)) + ql.log.debug("gdb> emulation entrypoint at 0x%x" % (self.entry_point)) + ql.log.debug("gdb> emulation exitpoint at 0x%x" % (self.exit_point)) except KeyboardInterrupt as ex: - logging.info("gdb> Paused at 0x%x, instruction size = %u" % (address, size)) + ql.log.info("gdb> Paused at 0x%x, instruction size = %u" % (address, size)) self.ql.os.stop() except: raise @@ -77,12 +77,12 @@ def dbg_hook(self, ql, address, size): def bp_insert(self, addr): if addr not in self.bp_list: self.bp_list.append(addr) - logging.info('gdb> Breakpoint added at: 0x%x' % addr) + self.ql.log.info('gdb> Breakpoint added at: 0x%x' % addr) def bp_remove(self, addr, type = None, len = None): self.bp_list.remove(addr) - logging.info('gdb> Breakpoint removed at: 0x%x' % addr) + self.ql.log.info('gdb> Breakpoint removed at: 0x%x' % addr) def resume_emu(self, address=None, skip_bp=0): @@ -99,6 +99,6 @@ def resume_emu(self, address=None, skip_bp=0): self.skip_bp_count = skip_bp if self.exit_point is not None: - logging.info('gdb> Resume at: 0x%x' % self.current_address) + self.ql.log.info('gdb> Resume at: 0x%x' % self.current_address) self.ql.emu_start(self.current_address, self.exit_point) \ No newline at end of file diff --git a/qiling/debugger/qdb/const.py b/qiling/debugger/qdb/const.py new file mode 100644 index 000000000..bb5ca921a --- /dev/null +++ b/qiling/debugger/qdb/const.py @@ -0,0 +1,38 @@ +# class for colorful prints +class color: + CYAN = '\033[96m' + PURPLE = '\033[95m' + BLUE = '\033[94m' + YELLOW = '\033[93m' + GREEN = '\033[92m' + RED = '\033[91m' + DARKGRAY = '\033[90m' + WHITE = '\033[48m' + DARKCYAN = '\033[36m' + BLACK = '\033[35m' + UNDERLINE = '\033[4m' + BOLD = '\033[1m' + END = '\033[0m' + + +FORMAT_LETTER = { + "o", # octal + "x", # hex + "d", # decimal + "u", # unsigned decimal + "t", # binary + "f", # float + "a", # address + "i", # instruction + "c", # char + "s", # string + "z", # hex, zero padded on the left + } + + +SIZE_LETTER = { + "b": 1, # 1-byte, byte + "h": 2, # 2-byte, halfword + "w": 4, # 4-byte, word + "g": 8, # 8-byte, giant + } diff --git a/qiling/debugger/qdb/frontend.py b/qiling/debugger/qdb/frontend.py index 34a01597a..d09f55650 100644 --- a/qiling/debugger/qdb/frontend.py +++ b/qiling/debugger/qdb/frontend.py @@ -1,47 +1,58 @@ #!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# import math, copy, os from contextlib import contextmanager from qiling.const import QL_ARCH +from .utils import dump_regs, get_arm_flags, disasm +from .const import color -from .utils import dump_regs, get_arm_flags +# read data from memory of qiling instance +def examine_mem(ql, addr, fmt): + def unpack(bs, sz): + return { + 1: lambda x: x[0], + 2: ql.unpack16, + 4: ql.unpack32, + 8: ql.unpack64, + }.get(sz)(bs) -# class for colorful prints -class color: - CYAN = '\033[96m' - PURPLE = '\033[95m' - BLUE = '\033[94m' - YELLOW = '\033[93m' - GREEN = '\033[92m' - RED = '\033[91m' - DARKGRAY = '\033[90m' - WHITE = '\033[48m' - DARKCYAN = '\033[36m' - BLACK = '\033[35m' - UNDERLINE = '\033[4m' - BOLD = '\033[1m' - END = '\033[0m' + ft, sz, ct = fmt + if ft == "i": -# read data from memory of qiling instance -def examine_mem(ql, xaddr, count): + for offset in range(addr, addr+ct*4, 4): + line = disasm(ql, offset) + if line: + print("0x{:x}: {}\t{}".format(line.address, line.mnemonic, line.op_str)) - lines = 1 if count <= 4 else math.ceil(count / 4) + print() - mem_read = [ql.mem.read(xaddr+(offset*4), 4) for offset in range(count)] + else: + lines = 1 if ct <= 4 else math.ceil(ct / 4) - for line in range(lines): - offset = line * 0x10 - print("0x%08x:\t" % (xaddr+offset), end="") + mem_read = [ql.mem.read(addr+(offset*sz), sz) for offset in range(ct)] - idx = line * 4 - for each in mem_read[idx:idx+4]: - print("0x%08x\t" % (ql.unpack(each)), end="") + for line in range(lines): + offset = line * sz * 4 + print("0x{:x}:\t".format(addr+offset), end="") + + idx = line * 4 + for each in mem_read[idx:idx+4]: + data = unpack(each, sz) + prefix = "0x" if ft in ("x", "a") else "" + pad = '0' + str(sz*2) if ft in ('x', 'a', 't') else '' + ft = ft.lower() if ft in ("x", "o", "b", "d") else ft.lower().replace("t", "b").replace("a", "x") + + print("{}{{:{}{}}}\t".format(prefix, pad, ft).format(data), end="") + + print() - print() # get terminal window height and width def get_terminal_size(): diff --git a/qiling/debugger/qdb/qdb.py b/qiling/debugger/qdb/qdb.py index 0f821c6e1..7aafbf496 100644 --- a/qiling/debugger/qdb/qdb.py +++ b/qiling/debugger/qdb/qdb.py @@ -1,4 +1,7 @@ #!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# import cmd from functools import partial @@ -7,8 +10,9 @@ from qiling.const import * from qiling.debugger import QlDebugger -from .frontend import context_printer, context_reg, context_asm, examine_mem, color +from .frontend import context_printer, context_reg, context_asm, examine_mem from .utils import parse_int, handle_bnj, is_thumb, diff_snapshot_save, diff_snapshot_restore, CODE_END +from .const import * class QlQdb(cmd.Cmd, QlDebugger): @@ -23,26 +27,35 @@ def __init__(self, ql, init_hook=None, rr=False): super().__init__() - # setup a breakpoint at entry point - init_hook = self._ql.loader.entry_point if not init_hook else int(init_hook, 16) + # setup a breakpoint at entry point or user specified address + init_hook = self._ql.loader.entry_point if not init_hook else parse_int(init_hook) + self.set_breakpoint(init_hook, _is_temp=True, _inter=True) - self._ql.hook_address(self.attach, init_hook) - @classmethod - def attach(cls, ql, *args, **kwargs): - print(color.RED, "[+] Qdb attached", color.END, sep="") - print(color.RED, "[!] All hooks of qiling instance will be disabled in Qdb", color.END, sep="") - - # clear all hooks - for i in ql._addr_hook_fuc.keys(): - ql.uc.hook_del(ql._addr_hook_fuc[i]) + def parseline(self, line): + """Parse the line into a command name and a string containing + the arguments. Returns a tuple containing (command, args, line). + 'command' and 'args' may be None if the line couldn't be parsed. + """ + line = line.strip() + if not line: + return None, None, line + elif line[0] == '?': + line = 'help ' + line[1:] + elif line.startswith('!'): + if hasattr(self, 'do_shell'): + line = 'shell ' + line[1:] + else: + return None, None, line + i, n = 0, len(line) + while i < n and line[i] in self.identchars: i = i+1 + cmd, arg = line[:i], line[i:].strip() + return cmd, arg, line - return cls(ql, *args, **kwargs).interactive() def interactive(self, *args): - self.do_context() - self.cmdloop() - return True + return self.cmdloop() + def emptyline(self, *args): """ @@ -52,6 +65,7 @@ def emptyline(self, *args): if _lastcmd: return _lastcmd() + def del_breakpoint(self, address): """ handle internal breakpoint removing operation @@ -60,11 +74,12 @@ def del_breakpoint(self, address): if _bp: _bp["hook"].remove() - def set_breakpoint(self, address, _is_temp=False): + + def set_breakpoint(self, address, _is_temp=False, _inter=False): """ handle internal breakpoint adding operation """ - _bp_func = partial(self._breakpoint_handler, _is_temp=_is_temp) + _bp_func = partial(self._breakpoint_handler, _is_temp=_is_temp, _inter=_inter) _hook = self._ql.hook_address(_bp_func, address) self.breakpoints.update({address: {"hook": _hook, "hitted": False, "temp": _is_temp}}) @@ -72,7 +87,8 @@ def set_breakpoint(self, address, _is_temp=False): if _is_temp == False: print("Breakpoint at 0x%08x" % address) - def _breakpoint_handler(self, ql, _is_temp=False): + + def _breakpoint_handler(self, ql, _is_temp, _inter): """ handle all breakpoints """ @@ -90,6 +106,10 @@ def _breakpoint_handler(self, ql, _is_temp=False): self.do_context() self._ql.emu_stop() + if _inter: + self.interactive() + + def do_context(self, *args): """ show context information for current location @@ -97,6 +117,7 @@ def do_context(self, *args): context_reg(self._ql, self._saved_states) context_asm(self._ql, self._ql.reg.arch_pc, 4) + def do_run(self, *args): """ launch qiling instance @@ -106,6 +127,7 @@ def do_run(self, *args): self.run(entry) + def run(self, address=None): """ handle qiling instance launching @@ -120,6 +142,7 @@ def run(self, address=None): self._ql.emu_start(address, 0) + def do_backward(self, *args): if getattr(self, "_states_list", None) is None or self._states_list[-1] is None: @@ -130,6 +153,7 @@ def do_backward(self, *args): self._ql.restore(diff_snapshot_restore(current_state_dicts, self._states_list.pop())) self.do_context() + def do_step(self, *args): """ execute one instruction at a time @@ -161,6 +185,7 @@ def do_step(self, *args): self.run(_cur_addr) + def do_start(self, *args): """ pause at entry point by setting a temporary breakpoint on it @@ -171,9 +196,12 @@ def do_start(self, *args): if self._ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB) and entry & 1: entry -= 1 - self.set_breakpoint(entry, _is_temp=True) + if entry not in self.breakpoints.keys(): + self.set_breakpoint(entry, _is_temp=True) + self.do_run() + def do_breakpoint(self, address): """ set breakpoint on specific address @@ -182,6 +210,7 @@ def do_breakpoint(self, address): baddr = parse_int(address) self.set_breakpoint(baddr) + def do_continue(self, *args): """ continue execution till next breakpoint or the end @@ -192,34 +221,54 @@ def do_continue(self, *args): self.run(_cur_addr) - def do_examine(self, args): + + def do_examine(self, line): """ - read data from memory of qiling instance + Examine memory: x/FMT ADDRESS. + format letter: o(octal), x(hex), d(decimal), u(unsigned decimal), t(binary), f(float), a(address), i(instruction), c(char), s(string) and z(hex, zero padded on the left) + size letter: b(byte), h(halfword), w(word), g(giant, 8 bytes) + e.g. x/4wx 0x41414141 , print 4 word size begin from address 0x41414141 in hex """ - _args = args.split() + _args = line.split() + DEFAULT_FMT = ('x', 4, 1) + + if line.startswith("/"): # followed by format letter and size letter + + def get_fmt(text): + def extract_count(t): + return "".join([s for s in t if s.isdigit()]) + + f, s, c = DEFAULT_FMT + if extract_count(text): + c = int(extract_count(text)) + + for char in text.strip(str(c)): + if char in SIZE_LETTER.keys(): + s = SIZE_LETTER.get(char) - if len(_args) == 1: - _xaddr = parse_int(_args[0]) - _count = 1 + elif char in FORMAT_LETTER: + f = char - elif len(_args) == 2: - _xaddr, _count = _args - _xaddr = parse_int(_xaddr) - _count = parse_int(_count) + return (f, s, c) + + fmt, addr = line.strip("/").split() + addr = parse_int(addr) + fmt = get_fmt(fmt) + + elif len(_args) == 1: # only address + addr = parse_int(_args[0]) + fmt = DEFAULT_FMT else: - print("wrong format\nUsage: x ADDRESS [SIZE]") + self.do_help("examine") return - examine_mem(self._ql, _xaddr, _count) + try: + examine_mem(self._ql, addr, fmt) + except: + print("something went wrong") - def do_context(self, *args): - """ - show context information for current location - """ - context_reg(self._ql, self._saved_states) - context_asm(self._ql, self._ql.reg.arch_pc, 4) def do_show(self, *args): """ @@ -229,12 +278,14 @@ def do_show(self, *args): print("Qdb:", [(hex(idx), val) for idx, val in self.breakpoints.items()]) print("internal:", [(hex(idx), val) for idx, val in self._ql._addr_hook.items()]) + def do_disassemble(self, address): """ disassemble instructions from address specified """ context_asm(self._ql, parse_int(address), 4) + def do_shell(self, *command): """ run python code,also a space between exclamation mark and command was necessary @@ -244,6 +295,7 @@ def do_shell(self, *command): except: print("something went wrong") + def do_quit(self, *args): """ exit Qdb diff --git a/qiling/debugger/qdb/utils.py b/qiling/debugger/qdb/utils.py index ae168444e..a4c628dda 100644 --- a/qiling/debugger/qdb/utils.py +++ b/qiling/debugger/qdb/utils.py @@ -1,4 +1,7 @@ #!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# from functools import partial @@ -7,7 +10,6 @@ CODE_END = True - def dump_regs(ql): if ql.archtype == QL_ARCH.MIPS: diff --git a/qiling/debugger/utils.py b/qiling/debugger/utils.py index c6b480233..5fa75e330 100644 --- a/qiling/debugger/utils.py +++ b/qiling/debugger/utils.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -from qiling import * +# from elftools.common.exceptions import ELFError from elftools.common.py3compat import ( @@ -30,6 +29,8 @@ from elftools.elf.constants import E_FLAGS from elftools.elf.constants import E_FLAGS_MASKS +from qiling import Qiling + class QlReadELF(object): def __init__(self, ql:Qiling, elf_stream): diff --git a/qiling/exception.py b/qiling/exception.py index c5d88df07..e6d460609 100644 --- a/qiling/exception.py +++ b/qiling/exception.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys, traceback diff --git a/qiling/extensions/coverage/formats/base.py b/qiling/extensions/coverage/formats/base.py index 80e5c7a51..6cd4e8652 100644 --- a/qiling/extensions/coverage/formats/base.py +++ b/qiling/extensions/coverage/formats/base.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from abc import ABC, abstractmethod + class QlBaseCoverage(ABC): """ An abstract base class for concrete code coverage collectors. diff --git a/qiling/extensions/coverage/formats/drcov.py b/qiling/extensions/coverage/formats/drcov.py index 7501ec4f4..07d13043d 100644 --- a/qiling/extensions/coverage/formats/drcov.py +++ b/qiling/extensions/coverage/formats/drcov.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from ctypes import Structure from ctypes import c_uint32, c_uint16 + from .base import QlBaseCoverage + # Adapted from https://www.ayrx.me/drcov-file-format class bb_entry(Structure): _fields_ = [ diff --git a/qiling/extensions/coverage/formats/drcov_exact.py b/qiling/extensions/coverage/formats/drcov_exact.py index 0bd809e3e..685c6c044 100644 --- a/qiling/extensions/coverage/formats/drcov_exact.py +++ b/qiling/extensions/coverage/formats/drcov_exact.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .drcov import QlDrCoverage + class QlDrCoverageExact(QlDrCoverage): """ Collects emulated code coverage and formats it in accordance with the DynamoRIO based diff --git a/qiling/extensions/coverage/utils.py b/qiling/extensions/coverage/utils.py index d5b6f33da..fb9fd1aa0 100644 --- a/qiling/extensions/coverage/utils.py +++ b/qiling/extensions/coverage/utils.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -from .formats import * from contextlib import contextmanager +from .formats import * + + # Returns subclasses recursively. def get_all_subclasses(cls): all_subclasses = [] diff --git a/qiling/extensions/idaplugin/qilingida.py b/qiling/extensions/idaplugin/qilingida.py index 01f0ac5d5..613af1a0e 100644 --- a/qiling/extensions/idaplugin/qilingida.py +++ b/qiling/extensions/idaplugin/qilingida.py @@ -1,5 +1,4 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework Plugin For IDA -# Built on top of Unicorn emulator (www.unicorn-engine.org) # Learn how to use? Please visit https://docs.qiling.io/en/latest/ida/ UseAsScript = True @@ -8,27 +7,11 @@ import collections import time import struct -import logging import re from enum import Enum from elftools.elf.elffile import ELFFile from json import load -# Qiling -from qiling import * -from qiling.const import * -from qiling.arch.x86_const import reg_map_32 as x86_reg_map_32 -from qiling.arch.x86_const import reg_map_64 as x86_reg_map_64 -from qiling.arch.x86_const import reg_map_misc as x86_reg_map_misc -from qiling.arch.x86_const import reg_map_st as x86_reg_map_st -from qiling.arch.arm_const import reg_map as arm_reg_map -from qiling.arch.arm64_const import reg_map as arm64_reg_map -from qiling.arch.mips_const import reg_map as mips_reg_map -from qiling.utils import ql_get_arch_bits -from qiling import __version__ as QLVERSION -from qiling.os.filestruct import ql_file -from keystone import * - # IDA Python SDK from idaapi import * from idc import * @@ -56,6 +39,22 @@ from PyQt5 import QtCore, QtWidgets from PyQt5.QtWidgets import (QPushButton, QHBoxLayout) +# Qiling +from qiling import * +from qiling.const import * +from qiling.arch.x86_const import reg_map_32 as x86_reg_map_32 +from qiling.arch.x86_const import reg_map_64 as x86_reg_map_64 +from qiling.arch.x86_const import reg_map_misc as x86_reg_map_misc +from qiling.arch.x86_const import reg_map_st as x86_reg_map_st +from qiling.arch.arm_const import reg_map as arm_reg_map +from qiling.arch.arm64_const import reg_map as arm64_reg_map +from qiling.arch.mips_const import reg_map as mips_reg_map +from qiling.utils import ql_get_arch_bits +from qiling import __version__ as QLVERSION +from qiling.os.filestruct import ql_file +from keystone import * + + QilingHomePage = 'https://www.qiling.io' QilingStableVersionURL = 'https://raw.githubusercontent.com/qilingframework/qiling/master/qiling/__version__.py' logging.basicConfig(level=logging.INFO, format='[%(levelname)s][%(module)s:%(lineno)d] %(message)s') @@ -952,7 +951,7 @@ def save(self): savepath = savedlg.path_name.value self.ql.save(reg=True, mem=True,fd=True, cpu_context=True, snapshot=savepath) - logging.info('Save to ' + savepath) + self.ql.log.info('Save to ' + savepath) return True def load(self): @@ -965,7 +964,7 @@ def load(self): loadname = loaddlg.file_name.value self.ql.restore(snapshot=loadname) - logging.info('Restore from ' + loadname) + self.ql.log.info('Restore from ' + loadname) return True def remove_ql(self): @@ -1018,11 +1017,11 @@ def __init__(self): def init(self): # init data - logging.info('---------------------------------------------------------------------------------------') - logging.info('Qiling Emulator Plugin For IDA, by Qiling Team. Version {0}, 2020'.format(QLVERSION)) - logging.info('Based on Qiling v{0}'.format(QLVERSION)) - logging.info('Find more information about Qiling at https://qiling.io') - logging.info('---------------------------------------------------------------------------------------') + self.ql.log.info('---------------------------------------------------------------------------------------') + self.ql.log.info('Qiling Emulator Plugin For IDA, by Qiling Team. Version {0}, 2020'.format(QLVERSION)) + self.ql.log.info('Based on Qiling v{0}'.format(QLVERSION)) + self.ql.log.info('Find more information about Qiling at https://qiling.io') + self.ql.log.info('---------------------------------------------------------------------------------------') self.qlemu = QlEmuQiling() self.ql_hook_ui_actions() return PLUGIN_KEEP @@ -1067,13 +1066,13 @@ def ql_load_user_script(self): if self.qlinit : self.ql_get_user_script(is_reload=True, is_start=True) else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_reload_user_script(self): if self.qlinit: self.ql_get_user_script(is_reload=True) else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_continue(self): if self.qlinit: @@ -1100,7 +1099,7 @@ def ql_continue(self): self.qlemu.ql.hook_del(hook) self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql) else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def _color_path(self, color): def _cb(ql, addr, size): @@ -1128,7 +1127,7 @@ def ql_run_selection(self): self.qlemu.status = self.qlemu.ql.save() self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql) else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_set_pc(self): if self.qlinit: @@ -1138,7 +1137,7 @@ def ql_set_pc(self): self.qlemu.status = self.qlemu.ql.save() self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql) else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_run_to_here(self): if self.qlinit: @@ -1163,7 +1162,7 @@ def ql_run_to_here(self): self.qlemu.status = self.qlemu.ql.save() self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql) else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_step(self): if self.qlinit: @@ -1179,21 +1178,21 @@ def ql_step(self): self.qlemu.ql.hook_del(hook) self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql) else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_save(self): if self.qlinit: if self.qlemu.save() != True: - logging.error('Fail to save the snapshot.') + self.ql.log.error('Fail to save the snapshot.') else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_load(self): if self.qlinit: if self.qlemu.load() != True: - logging.error('Fail to load the snapshot.') + self.ql.log.error('Fail to load the snapshot.') else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_chang_reg(self): if self.qlinit: @@ -1201,7 +1200,7 @@ def ql_chang_reg(self): self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql) self.qlemu.status = self.qlemu.ql.save() else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_reset(self): if self.qlinit: @@ -1209,7 +1208,7 @@ def ql_reset(self): self.qlemu = QlEmuQiling() self.ql_start() else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_close(self): if self.qlinit: @@ -1220,9 +1219,9 @@ def ql_close(self): del self.qlemu self.qlemu = None self.qlinit = False - logging.info('Qiling is deleted.') + self.ql.log.info('Qiling is deleted.') else: - logging.error('Qiling is not started.') + self.ql.log.error('Qiling is not started.') def ql_show_reg_view(self): if self.qlinit: @@ -1234,7 +1233,7 @@ def ql_show_reg_view(self): self.qlemuregview.Show() self.qlemuregview.Refresh() else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_show_stack_view(self): if self.qlinit: @@ -1245,7 +1244,7 @@ def ql_show_stack_view(self): self.qlemustackview.Show() self.qlemustackview.Refresh() else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_show_mem_view(self, addr=get_screen_ea(), size=0x10): if self.qlinit: @@ -1279,7 +1278,7 @@ def ql_show_mem_view(self, addr=get_screen_ea(), size=0x10): self.qlemumemview[mem_addr].Show() self.qlemumemview[mem_addr].Refresh() else: - logging.error('Qiling should be setup firstly.') + self.ql.log.error('Qiling should be setup firstly.') def ql_unload_plugin(self): heads = Heads(get_segm_start(get_screen_ea()), get_segm_end(get_screen_ea())) @@ -1288,7 +1287,7 @@ def ql_unload_plugin(self): self.ql_close() self.ql_detach_main_menu_actions() self.ql_unregister_menu_actions() - logging.info('Unload plugin successfully!') + self.ql.log.info('Unload plugin successfully!') def ql_menu_null(self): pass @@ -2039,11 +2038,11 @@ def get_user_scripts_obj(scriptpath:str, classname:str, is_reload:bool): self.userobj = get_user_scripts_obj(self.customscriptpath, 'QILING_IDA', is_reload) if self.userobj is not None: if is_reload and not is_start: - logging.info('Custom user script is reloaded.') + ql.log.info('Custom user script is reloaded.') else: - logging.info('Custom user script is loaded successfully.') + ql.log.info('Custom user script is loaded successfully.') else: - logging.info('Custom user script not found.') + ql.log.info('Custom user script not found.') ### Dialog diff --git a/qiling/extensions/report/report.py b/qiling/extensions/report/report.py index d78b4cf7f..f564528f0 100644 --- a/qiling/extensions/report/report.py +++ b/qiling/extensions/report/report.py @@ -1,6 +1,12 @@ -from qiling.const import * +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + import json +from qiling.const import * + class Report: def __init__(self, ql): diff --git a/qiling/extensions/sanitizers/heap.py b/qiling/extensions/sanitizers/heap.py index dd7638c29..8fc476f79 100644 --- a/qiling/extensions/sanitizers/heap.py +++ b/qiling/extensions/sanitizers/heap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import random from enum import Enum diff --git a/qiling/extensions/windows_sdk/winsdk_dump.py b/qiling/extensions/windows_sdk/winsdk_dump.py index 2b795fdb4..2b8f84046 100644 --- a/qiling/extensions/windows_sdk/winsdk_dump.py +++ b/qiling/extensions/windows_sdk/winsdk_dump.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # windows sdk data file can download here: 'https://github.com/ohjeongwook/windows_sdk_data.git' diff --git a/qiling/loader/dos.py b/qiling/loader/dos.py index ff007ea59..7d899c3b5 100644 --- a/qiling/loader/dos.py +++ b/qiling/loader/dos.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import math, sys, traceback from .loader import QlLoader + from qiling.os.disk import QlDisk -import sys -import traceback -import math -import logging + class QlLoaderDOS(QlLoader): def __init__(self, ql): @@ -20,7 +20,7 @@ def __init__(self, ql): def excepthook(self, tp, value, tb): if self.ql.os.stdscr is not None: tbmsg = "".join(traceback.format_exception(tp, value, tb)) - logging.info(f"{tbmsg}") + self.ql.log.info(f"{tbmsg}") self.old_excepthook(tp, value, tb) def _round_to_4k(self, addr): diff --git a/qiling/loader/elf.py b/qiling/loader/elf.py index 97d5f2211..4a2cd57cc 100644 --- a/qiling/loader/elf.py +++ b/qiling/loader/elf.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import sys -import os -import string, logging +# + +import os, string, sys + from heapq import heappush, heappop from elftools.elf.elffile import ELFFile @@ -13,6 +13,7 @@ from elftools.elf.descriptions import describe_reloc_type from qiling.const import * + from qiling.exception import * from .loader import QlLoader from qiling.os.linux.function_hook import FunctionHook @@ -66,7 +67,7 @@ def __init__(self, path, ql): self.elfdata = elfdata.ljust(52, b'\x00') if self.elffile.e_ident_raw[: 4] != b'\x7fELF': - raise QlErrorELFFormat("[!] ERROR: NOT a ELF") + raise QlErrorELFFormat("ERROR: NOT a ELF") self.elfhead = self.parse_header() if self.elfhead['e_type'] == "ET_REL": # kernel driver @@ -93,10 +94,10 @@ def __init__(self, ql): self.ql = ql def run(self): - if self.ql.shellcoder: - self.ql.mem.map(self.ql.os.entry_point, self.ql.os.shellcoder_ram_size, info="[shellcode_stack]") + if self.ql.code: + self.ql.mem.map(self.ql.os.entry_point, self.ql.os.code_ram_size, info="[shellcode_stack]") self.ql.os.entry_point = (self.ql.os.entry_point + 0x200000 - 0x1000) - self.ql.mem.write(self.ql.os.entry_point, self.ql.shellcoder) + self.ql.mem.write(self.ql.os.entry_point, self.ql.code) self.ql.reg.arch_sp = self.ql.os.entry_point return @@ -178,7 +179,7 @@ def load_with_ld(self, stack_addr, load_address=-1, argv=[], env={}): if elfhead['e_type'] == 'ET_EXEC': load_address = 0 elif elfhead['e_type'] != 'ET_DYN': - logging.debug("[+] Some error in head e_type: %i!", elfhead['e_type']) + self.ql.log.debug("Some error in head e_type: %i!", elfhead['e_type']) return -1 # We need to sort the memory segments first, sometimes they are unordered @@ -240,9 +241,9 @@ def load_with_ld(self, stack_addr, load_address=-1, argv=[], env={}): self.ql.mem.map(_mem_s, _mem_e - _mem_s, perms=_perms, info=self.path) if _mem_e > _highestmapped_e: _highestmapped_e = _mem_e - logging.debug("[+] load 0x%x - 0x%x" % (_mem_s, _mem_e)) + self.ql.log.debug("load 0x%x - 0x%x" % (_mem_s, _mem_e)) except Exception as e: - logging.debug("[!] load 0x%x - 0x%x => %s" % (_mem_s, _mem_e, str(e))) + self.ql.log.debug("load 0x%x - 0x%x => %s" % (_mem_s, _mem_e, str(e))) continue # Now we write the segment data to the memory @@ -253,18 +254,18 @@ def load_with_ld(self, stack_addr, load_address=-1, argv=[], env={}): data = super().getelfdata(entry['p_offset'], entry['p_filesz']) self.ql.mem.write(_mem_s, data) except Exception as e: - logging.debug("[!] segment data 0x%x - Length 0x%x => %s" % (_mem_s, len(data), str(e))) + self.ql.log.debug("segment data 0x%x - Length 0x%x => %s" % (_mem_s, len(data), str(e))) continue loaded_mem_end = load_address + mem_end if loaded_mem_end > _mem_e: self.ql.mem.map(_mem_e, loaded_mem_end - _mem_e, info=self.path) - logging.debug("[+] load 0x%x - 0x%x" % ( + self.ql.log.debug("load 0x%x - 0x%x" % ( _mem_e, loaded_mem_end)) # make sure we map all PT_LOAD tagged area entry_point = elfhead['e_entry'] + load_address self.ql.os.elf_mem_start = mem_start - logging.debug("[+] mem_start: 0x%x mem_end: 0x%x" % (mem_start, mem_end)) + self.ql.log.debug("mem_start: 0x%x mem_end: 0x%x" % (mem_start, mem_end)) self.brk_address = mem_end + load_address + 0x2000 @@ -275,7 +276,7 @@ def load_with_ld(self, stack_addr, load_address=-1, argv=[], env={}): interp = ELFParse(self.ql.rootfs + interp_path, self.ql) interphead = interp.parse_header() - logging.debug("[+] interp is : %s" % (self.ql.rootfs + interp_path)) + self.ql.log.debug("interp is : %s" % (self.ql.rootfs + interp_path)) interp_mem_size = -1 for i in interp.parse_segments(): @@ -285,14 +286,14 @@ def load_with_ld(self, stack_addr, load_address=-1, argv=[], env={}): interp_mem_size = i['p_vaddr'] + i['p_memsz'] interp_mem_size = (interp_mem_size // 0x1000 + 1) * 0x1000 - logging.debug("[+] interp_mem_size is : 0x%x" % int(interp_mem_size)) + self.ql.log.debug("interp_mem_size is : 0x%x" % int(interp_mem_size)) if self.ql.archbit == 64: self.interp_address = int(self.ql.os.profile.get("OS64", "interp_address"), 16) elif self.ql.archbit == 32: self.interp_address = int(self.ql.os.profile.get("OS32", "interp_address"), 16) - logging.debug("[+] interp_address is : 0x%x" % (self.interp_address)) + self.ql.log.debug("interp_address is : 0x%x" % (self.interp_address)) self.ql.mem.map(self.interp_address, int(interp_mem_size), info=os.path.abspath(self.ql.rootfs + interp_path)) @@ -309,7 +310,7 @@ def load_with_ld(self, stack_addr, load_address=-1, argv=[], env={}): else: self.mmap_address = int(self.ql.os.profile.get("OS32", "mmap_address"), 16) - logging.debug("[+] mmap_address is : 0x%x" % (self.mmap_address)) + self.ql.log.debug("mmap_address is : 0x%x" % (self.mmap_address)) # Set elf table elf_table = b'' @@ -381,7 +382,7 @@ def load_with_ld(self, stack_addr, load_address=-1, argv=[], env={}): # for i in range(120): # buf = self.ql.mem.read(new_stack + i * 0x8, 8) - # logging.info("0x%08x : 0x%08x " % (new_stack + i * 0x4, self.ql.unpack64(buf)) + ' '.join(['%02x' % i for i in buf]) + ' ' + ''.join([chr(i) if i in string.printable[ : -5].encode('ascii') else '.' for i in buf])) + # self.ql.log.info("0x%08x : 0x%08x " % (new_stack + i * 0x4, self.ql.unpack64(buf)) + ' '.join(['%02x' % i for i in buf]) + ' ' + ''.join([chr(i) if i in string.printable[ : -5].encode('ascii') else '.' for i in buf])) self.ql.os.entry_point = self.entry_point = entry_point self.ql.os.elf_entry = self.elf_entry = load_address + elfhead['e_entry'] @@ -427,11 +428,11 @@ def lkm_get_init(self, ql): for nsym, symbol in enumerate(section.iter_symbols()): if symbol.name == 'init_module': addr = symbol.entry.st_value + elffile.get_section(symbol['st_shndx'])['sh_offset'] - logging.info("[+] init_module = 0x%x" % addr) + ql.log.info("init_module = 0x%x" % addr) return addr # not found. FIXME: report error on invalid module?? - logging.warning("[!] invalid module? symbol init_module not found") + ql.log.warning("invalid module? symbol init_module not found") return -1 def lkm_dynlinker(self, ql, mem_start): @@ -447,7 +448,7 @@ def get_symbol(elffile, name): all_symbols = [] self.ql.os.hook_addr = API_HOOK_MEM # map address to symbol name - ql.import_symbols = {} + self.import_symbols = {} # reverse dictionary to map symbol name -> address rev_reloc_symbols = {} @@ -504,8 +505,8 @@ def get_symbol(elffile, name): self.ql.os.hook_addr = (int( self.ql.os.hook_addr / self.ql.pointersize) + 1) * self.ql.pointersize # print("hook_addr = %x" %self.ql.os.hook_addr) - ql.import_symbols[self.ql.os.hook_addr] = symbol_name - # logging.info(":: Demigod is hooking %s(), at slot %x" %(symbol_name, self.ql.os.hook_addr)) + self.import_symbols[self.ql.os.hook_addr] = symbol_name + # ql.log.info(":: Demigod is hooking %s(), at slot %x" %(symbol_name, self.ql.os.hook_addr)) if symbol_name == "page_offset_base": # FIXME: this is for rootkit to scan for syscall table from page_offset_base @@ -522,12 +523,12 @@ def get_symbol(elffile, name): all_symbols.append(symbol_name) _section = elffile.get_section(_symbol['st_shndx']) rev_reloc_symbols[symbol_name] = _section['sh_offset'] + _symbol['st_value'] + mem_start - # logging.info(":: Add reverse lookup for %s to %x (%x, %x)" %(symbol_name, rev_reloc_symbols[symbol_name], _section['sh_offset'], _symbol['st_value'])) - # logging.info(":: Add reverse lookup for %s to %x" %(symbol_name, rev_reloc_symbols[symbol_name])) + # ql.log.info(":: Add reverse lookup for %s to %x (%x, %x)" %(symbol_name, rev_reloc_symbols[symbol_name], _section['sh_offset'], _symbol['st_value'])) + # ql.log.info(":: Add reverse lookup for %s to %x" %(symbol_name, rev_reloc_symbols[symbol_name])) else: sym_offset = rev_reloc_symbols[symbol_name] - mem_start - # logging.info("Relocating symbol %s -> 0x%x" %(symbol_name, rev_reloc_symbols[symbol_name])) + # ql.log.info("Relocating symbol %s -> 0x%x" %(symbol_name, rev_reloc_symbols[symbol_name])) loc = elffile.get_section(section['sh_info'])['sh_offset'] + rel['r_offset'] loc += mem_start @@ -537,11 +538,11 @@ def get_symbol(elffile, name): if rel['r_addend']: val = sym_offset + rel['r_addend'] val += mem_start - # logging.info('R_X86_64_32S %s: [0x%x] = 0x%x' %(symbol_name, loc, val & 0xFFFFFFFF)) + # ql.log.info('R_X86_64_32S %s: [0x%x] = 0x%x' %(symbol_name, loc, val & 0xFFFFFFFF)) ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF)) else: # print("sym_offset = %x, rel = %x" %(sym_offset, rel['r_addend'])) - # logging.info('R_X86_64_32S %s: [0x%x] = 0x%x' %(symbol_name, loc, rev_reloc_symbols[symbol_name] & 0xFFFFFFFF)) + # ql.log.info('R_X86_64_32S %s: [0x%x] = 0x%x' %(symbol_name, loc, rev_reloc_symbols[symbol_name] & 0xFFFFFFFF)) ql.mem.write(loc, ql.pack32(rev_reloc_symbols[symbol_name] & 0xFFFFFFFF)) elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_X86_64_64': @@ -549,7 +550,7 @@ def get_symbol(elffile, name): val = sym_offset + rel['r_addend'] val += 0x2000000 # init_module position: FIXME # finally patch this reloc - # logging.info('R_X86_64_64 %s: [0x%x] = 0x%x' %(symbol_name, loc, val)) + # ql.log.info('R_X86_64_64 %s: [0x%x] = 0x%x' %(symbol_name, loc, val)) ql.mem.write(loc, ql.pack64(val)) elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_X86_64_PC32': @@ -557,7 +558,7 @@ def get_symbol(elffile, name): val = rel['r_addend'] - loc val += rev_reloc_symbols[symbol_name] # finally patch this reloc - # logging.info('R_X86_64_PC32 %s: [0x%x] = 0x%x' %(symbol_name, loc, val & 0xFFFFFFFF)) + # ql.log.info('R_X86_64_PC32 %s: [0x%x] = 0x%x' %(symbol_name, loc, val & 0xFFFFFFFF)) ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF)) elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_386_PC32': @@ -585,7 +586,7 @@ def get_symbol(elffile, name): ql.mem.write(loc + 2, ql.pack16(val & 0xFFFF)) else: - raise QlErrorNotImplemented("[!] Relocation type %s not implemented" % describe_reloc_type(rel['r_info_type'], elffile)) + raise QlErrorNotImplemented("Relocation type %s not implemented" % describe_reloc_type(rel['r_info_type'], elffile)) return rev_reloc_symbols @@ -614,7 +615,7 @@ def load_driver(self, ql, stack_addr, loadbase=0): # map some memory to intercept external functions of Linux kernel ql.mem.map(API_HOOK_MEM, 0x1000, info="[api_mem]") - logging.info("[+] loadbase: %x, mem_start: %x, mem_end: %x" % (loadbase, mem_start, mem_end)) + ql.log.info("loadbase: %x, mem_start: %x, mem_end: %x" % (loadbase, mem_start, mem_end)) ql.mem.map(loadbase + mem_start, mem_end - mem_start, info=ql.path) ql.mem.write(loadbase + mem_start, elfdata_mapping) @@ -627,7 +628,7 @@ def load_driver(self, ql, stack_addr, loadbase=0): else: self.mmap_address = int(self.ql.os.profile.get("OS32", "mmap_address"), 16) - logging.debug("[+] mmap_address is : 0x%x" % (self.mmap_address)) + ql.log.debug("mmap_address is : 0x%x" % (self.mmap_address)) new_stack = stack_addr new_stack = self.alignment(new_stack) @@ -655,7 +656,7 @@ def load_driver(self, ql, stack_addr, loadbase=0): tmp_sc = sc.replace("sys_", "NR_") if tmp_sc in globals(): syscall_id = globals()[tmp_sc] - logging.debug("Writing syscall %s to [0x%x]" % (sc, SYSCALL_MEM + ql.pointersize * syscall_id)) + ql.log.debug("Writing syscall %s to [0x%x]" % (sc, SYSCALL_MEM + ql.pointersize * syscall_id)) ql.mem.write(SYSCALL_MEM + ql.pointersize * syscall_id, ql.pack(rev_reloc_symbols[sc])) # write syscall addresses into syscall table @@ -667,9 +668,9 @@ def load_driver(self, ql, stack_addr, loadbase=0): ql.mem.write(SYSCALL_MEM + 2 * ql.pointersize, ql.pack(self.ql.os.hook_addr + 2 * ql.pointersize)) # setup hooks for read/write/open syscalls - ql.import_symbols[self.ql.os.hook_addr] = hook_sys_read - ql.import_symbols[self.ql.os.hook_addr + 1 * ql.pointersize] = hook_sys_write - ql.import_symbols[self.ql.os.hook_addr + 2 * ql.pointersize] = hook_sys_open + self.import_symbols[self.ql.os.hook_addr] = hook_sys_read + self.import_symbols[self.ql.os.hook_addr + 1 * ql.pointersize] = hook_sys_write + self.import_symbols[self.ql.os.hook_addr + 2 * ql.pointersize] = hook_sys_open def get_elfdata_mapping(self): elfdata_mapping = bytearray() diff --git a/qiling/loader/loader.py b/qiling/loader/loader.py index 3485c49ff..fa9683642 100644 --- a/qiling/loader/loader.py +++ b/qiling/loader/loader.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import pefile +# +from collections import namedtuple + from qiling.const import QL_OS, QL_OS_ALL, QL_ARCH, QL_ENDIAN, QL_OUTPUT from qiling.exception import QlErrorArch, QlErrorOsType, QlErrorOutput -from collections import namedtuple + class QlLoader(): def __init__(self, ql): diff --git a/qiling/loader/macho.py b/qiling/loader/macho.py index 0cc8fa765..7f24957f0 100644 --- a/qiling/loader/macho.py +++ b/qiling/loader/macho.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import os, plistlib, struct, logging +import os, plistlib, struct from .loader import QlLoader @@ -21,7 +21,7 @@ from qiling.os.macos.kernel_func import FileSystem, map_commpage from qiling.os.macos.mach_port import MachPort, MachPortManager from qiling.os.macos.subsystems import MachHostServer, MachTaskServer -from qiling.os.macos.utils import env_dict_to_array, ql_real_to_vm_abspath, page_align_end +from qiling.os.macos.utils import env_dict_to_array, page_align_end from qiling.os.macos.thread import QlMachoThreadManagement, QlMachoThread @@ -99,11 +99,11 @@ def run(self): self.stack_address = stack_address self.stack_size = stack_size - if self.ql.shellcoder: - self.ql.mem.map(self.ql.os.entry_point, self.ql.os.shellcoder_ram_size, info="[shellcode_stack]") + if self.ql.code: + self.ql.mem.map(self.ql.os.entry_point, self.ql.os.code_ram_size, info="[shellcode_stack]") self.ql.os.entry_point = (self.ql.os.entry_point + 0x200000 - 0x1000) - self.ql.mem.write(self.entry_point, self.ql.shellcoder) + self.ql.mem.write(self.entry_point, self.ql.code) self.ql.reg.arch_sp = self.ql.os.entry_point return @@ -116,7 +116,7 @@ def run(self): self.ql.os.macho_task_server = MachTaskServer(self.ql) self.envs = env_dict_to_array(self.env) - self.apples = ql_real_to_vm_abspath(self.ql, self.ql.path) + self.apples = self.ql.os.transform_to_relative_path(self.ql.path) self.ql.os.heap = QlMemoryHeap(self.ql, self.heap_address, self.heap_address + self.heap_size) # FIXME: Not working due to overlarge mapping, need to fix it @@ -154,7 +154,7 @@ def run(self): self.ql.os.macho_task.min_offset = page_align_end(self.vm_end_addr, PAGE_SIZE) def loadDriver(self, stack_addr, loadbase = -1, argv = [], env = {}): - self.ql.import_symbols = {} + self.import_symbols = {} PAGE_SIZE = 0x1000 if loadbase < 0: loadbase = 0xffffff7000000000 @@ -168,7 +168,7 @@ def loadDriver(self, stack_addr, loadbase = -1, argv = [], env = {}): self.kext_size = self.vm_end_addr - loadbase kernel_path = os.path.join(self.ql.rootfs, "System/Library/Kernels/kernel.development") - logging.info("[+] Parsing kernel:") + self.ql.log.info("Parsing kernel:") self.kernel = MachoParser(self.ql, kernel_path) # Create memory for external static symbol jmp code @@ -176,7 +176,7 @@ def loadDriver(self, stack_addr, loadbase = -1, argv = [], env = {}): self.static_size = PAGE_SIZE self.ql.mem.map(self.static_addr, self.static_size, info="[STATIC]") self.vm_end_addr += PAGE_SIZE - logging.info("[+] Memory for external static symbol is created at 0x%x with size 0x%x" % (self.static_addr, self.static_size)) + self.ql.log.info("Memory for external static symbol is created at 0x%x with size 0x%x" % (self.static_addr, self.static_size)) self.static_symbols = {} # Load kernel @@ -190,7 +190,7 @@ def loadDriver(self, stack_addr, loadbase = -1, argv = [], env = {}): self.kernel_base = cmd.vm_address self.loadSegment64(cmd, False) - logging.info("[+] Kernel loaded at 0x%x" % self.kernel_base) + self.ql.log.info("Kernel loaded at 0x%x" % self.kernel_base) # Resolve local relocation for relocation in self.macho_file.dysymbol_table.locreloc: @@ -200,7 +200,7 @@ def loadDriver(self, stack_addr, loadbase = -1, argv = [], env = {}): seg = segment break current_value, = struct.unpack("= 8: lc = LoadCommand(self.lc_raw[offset:]) else: - logging.info("[-] cmd size overflow") + self.ql.log.info("cmd size overflow") return False if self.header.lc_size >= offset + lc.cmd_size: complete_cmd = lc.get_complete() pass else: - logging.info("[-] cmd size overflow") + self.ql.log.info("cmd size overflow") return False self.commands.append(complete_cmd) diff --git a/qiling/loader/macho_parser/utils.py b/qiling/loader/macho_parser/utils.py index dfffbfce2..d640b4b4a 100644 --- a/qiling/loader/macho_parser/utils.py +++ b/qiling/loader/macho_parser/utils.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# class FileReader: def __init__(self, binary): diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index dc9bad06a..9129bed7c 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -1,18 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import sys -import os -import string -import pefile -import pickle -import traceback -import secrets -import logging +import os, pefile, pickle, secrets, string, sys, traceback from unicorn.x86_const import * + + from qiling.os.windows.utils import * from qiling.os.windows.structs import * from qiling.exception import * @@ -21,6 +16,35 @@ from .loader import QlLoader from qiling.os.memory import QlMemoryHeap + +class QlPeCacheEntry: + def __init__(self, data, cmdlines, import_symbols, import_table): + self.data = data + self.cmdlines = cmdlines + self.import_symbols = import_symbols + self.import_table = import_table + +# A default simple cache implementation +class QlPeCache: + def create_filename(self, path, address): + return path + ".%x.cache2" % address + + def restore(self, path, address): + fcache = self.create_filename(path, address) + # pickle file cannot be outdated + if os.path.exists(fcache) and os.stat(fcache).st_mtime > os.stat(path).st_mtime: + with open(fcache, "rb") as fcache_file: + return QlPeCacheEntry(*pickle.load(fcache_file)) + return None + + def save(self, path, address, entry): + fcache = self.create_filename(path, address) + data = (entry.data, entry.cmdlines, entry.import_symbols, entry.import_table) + # cache this dll file + with open(fcache, "wb") as fcache_file: + pickle.dump(data, fcache_file) + + class Process(): def __init__(self, ql): self.ql = ql @@ -31,13 +55,10 @@ def align(self, size, unit): def load_dll(self, dll_name, driver=False): dll_name = dll_name.decode() - if self.ql.archtype == QL_ARCH.X86: - self.ql.dlls = os.path.join("Windows", "SysWOW64") - elif self.ql.archtype == QL_ARCH.X8664: - self.ql.dlls = os.path.join("Windows", "System32") + self.ql.dlls = os.path.join("Windows", "System32") if 'C:\\' in dll_name.upper(): - path = canonical_path(self.ql, dll_name) + path = self.ql.os.transform_to_real_path(dll_name) dll_name = path_leaf(dll_name) else: dll_name = dll_name.lower() @@ -46,7 +67,7 @@ def load_dll(self, dll_name, driver=False): path = os.path.join(self.ql.rootfs, self.ql.dlls, dll_name) if not os.path.exists(path): - raise QlErrorFileNotFound("[!] Cannot find dll in %s" % path) + raise QlErrorFileNotFound("Cannot find dll in %s" % path) # If the dll is already loaded if dll_name in self.dlls: @@ -54,48 +75,60 @@ def load_dll(self, dll_name, driver=False): else: self.dlls[dll_name] = self.dll_last_address - logging.info("[+] Loading %s to 0x%x" % (path, self.dll_last_address)) + self.ql.log.info("Loading %s to 0x%x" % (path, self.dll_last_address)) - # cache depends on address base - fcache = path + ".%x.cache" % self.dll_last_address - - # Add dll to IAT - try: - self.import_address_table[dll_name] = {} - except: - pass + if self.libcache: + cached = self.libcache.restore(path, self.dll_last_address) + else: + cached = None - if self.libcache and os.path.exists(fcache) and \ - os.stat(fcache).st_mtime > os.stat(path).st_mtime: # pickle file cannot be outdated - with open(fcache, "rb") as fcache_file: - (data, cmdlines, self.import_symbols, self.import_address_table) = \ - pickle.load(fcache_file) - for entry in cmdlines: + if cached: + data = cached.data + import_symbols = cached.import_symbols + import_table = cached.import_table + for entry in cached.cmdlines: self.set_cmdline(entry['name'], entry['address'], data) + self.ql.log.info("Loaded %s from cache" % path) else: dll = pefile.PE(path, fast_load=True) dll.parse_data_directories() + warnings = dll.get_warnings() + if warnings: + self.ql.log.warning(f'Warnings while loading {path}:') + for warning in warnings: + self.ql.log.warning(f' - {warning}') data = bytearray(dll.get_memory_mapped_image()) cmdlines = [] + import_symbols = {} + import_table = {} for entry in dll.DIRECTORY_ENTRY_EXPORT.symbols: - self.import_symbols[self.dll_last_address + entry.address] = {"name": entry.name, + import_symbols[self.dll_last_address + entry.address] = {"name": entry.name, "ordinal": entry.ordinal, "dll": dll_name.split('.')[0] } - self.import_address_table[dll_name][entry.name] = self.dll_last_address + entry.address - self.import_address_table[dll_name][entry.ordinal] = self.dll_last_address + entry.address + if entry.name: + import_table[entry.name] = self.dll_last_address + entry.address + import_table[entry.ordinal] = self.dll_last_address + entry.address cmdline_entry = self.set_cmdline(entry.name, entry.address, data) if cmdline_entry: cmdlines.append(cmdline_entry) if self.libcache: - # cache this dll file - pickle.dump((data, cmdlines, - self.import_symbols, - self.import_address_table), - open(fcache, "wb")) - logging.info("Cached %s" % path) + cached = QlPeCacheEntry(data, cmdlines, import_symbols, import_table) + self.libcache.save(path, self.dll_last_address, cached) + self.ql.log.info("Cached %s" % path) + + # Add dll to IAT + try: + self.import_address_table[dll_name] = import_table + except Exception as ex: + self.ql.log.exception(f'Unable to add {dll_name} to IAT') + + try: + self.import_symbols.update(import_symbols) + except Exception as ex: + self.ql.log.exception(f'Unable to add {dll_name} import symbols') dll_base = self.dll_last_address dll_len = self.ql.mem.align(len(bytes(data)), 0x1000) @@ -111,28 +144,29 @@ def load_dll(self, dll_name, driver=False): # add DLL to coverage images self.images.append(self.coverage_image(dll_base, dll_base+dll_len, path)) - logging.info("Done with loading %s" % path) + self.ql.log.info("Done with loading %s" % path) return dll_base - def set_cmdline(self, name, address, memory): - if self.ql.archtype == QL_ARCH.X86: - addr = self.ql.os.heap.alloc(len(self.cmdline)) - packed_addr = self.ql.pack32(addr) - else: - addr = self.ql.os.heap.alloc(2 * len(self.cmdline)) - packed_addr = self.ql.pack64(addr) + def _alloc_cmdline(self, wide): + addr = self.ql.os.heap.alloc(len(self.cmdline) * (2 if wide else 1)) + packed_addr = self.ql.pack(addr) + return addr, packed_addr + def set_cmdline(self, name, address, memory): cmdline_entry = None if name == b"_acmdln": + addr, packed_addr = self._alloc_cmdline(wide=False) cmdline_entry = {"name": name, "address": address} memory[address:address + self.ql.pointersize] = packed_addr self.ql.mem.write(addr, self.cmdline) elif name == b"_wcmdln": + addr, packed_addr = self._alloc_cmdline(wide=True) cmdline_entry = {"name": name, "address": address} memory[address:address + self.ql.pointersize] = packed_addr - self.ql.mem.write(addr, str(self.cmdline).encode("utf-16le")) + encoded = self.cmdline.decode('ascii').encode('UTF-16LE') + self.ql.mem.write(addr, encoded) return cmdline_entry @@ -144,7 +178,7 @@ def init_tib(self): self.structure_last_addr += 0x30 teb_addr = self.structure_last_addr - logging.info("TEB addr is 0x%x" %teb_addr) + self.ql.log.info("TEB addr is 0x%x" %teb_addr) teb_size = len(TEB(self.ql).bytes()) teb_data = TEB( @@ -169,7 +203,7 @@ def init_tib(self): def init_peb(self): peb_addr = self.structure_last_addr - logging.info("PEB addr is 0x%x" % peb_addr) + self.ql.log.info("PEB addr is 0x%x" % peb_addr) # we must set an heap, will try to retrieve this value. Is ok to be all \x00 process_heap = self.ql.os.heap.alloc(0x100) @@ -257,7 +291,7 @@ def add_ldr_data_table_entry(self, dll_name): self.ldr_list.append(ldr_table_entry) def init_exports(self): - if self.ql.shellcoder: + if self.ql.code: return if self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT']].VirtualAddress != 0: # Do a full load if IMAGE_DIRECTORY_ENTRY_EXPORT is present so we can load the exports @@ -274,17 +308,17 @@ def init_exports(self): self.import_address_table[dll_name][entry.name] = self.pe_image_address + entry.address self.import_address_table[dll_name][entry.ordinal] = self.pe_image_address + entry.address except: - logging.info('Failed to load exports for %s:\n%s' % (self.ql.argv, traceback.format_exc())) + self.ql.log.info('Failed to load exports for %s:\n%s' % (self.ql.argv, traceback.format_exc())) def init_driver_object(self): # PDRIVER_OBJECT DriverObject driver_object_addr = self.structure_last_addr - logging.info("[+] Driver object addr is 0x%x" %driver_object_addr) + self.ql.log.info("Driver object addr is 0x%x" %driver_object_addr) - if self.ql.archbit == 64: - self.driver_object = DRIVER_OBJECT64(self.ql, driver_object_addr) - else: + if self.ql.archtype == QL_ARCH.X86: self.driver_object = DRIVER_OBJECT32(self.ql, driver_object_addr) + elif self.ql.archtype == QL_ARCH.X8664: + self.driver_object = DRIVER_OBJECT64(self.ql, driver_object_addr) driver_object_size = ctypes.sizeof(self.driver_object) self.ql.mem.write(driver_object_addr, bytes(self.driver_object)) @@ -295,11 +329,13 @@ def init_driver_object(self): def init_registry_path(self): # PUNICODE_STRING RegistryPath regitry_path_addr = self.structure_last_addr - logging.info("[+] Registry path addr is 0x%x" %regitry_path_addr) - if self.ql.archbit == 64: - regitry_path_data = UNICODE_STRING64(0, 0, regitry_path_addr) - else: + self.ql.log.info("Registry path addr is 0x%x" %regitry_path_addr) + + if self.ql.archtype == QL_ARCH.X86: regitry_path_data = UNICODE_STRING32(0, 0, regitry_path_addr) + elif self.ql.archtype == QL_ARCH.X8664: + regitry_path_data = UNICODE_STRING64(0, 0, regitry_path_addr) + regitry_path_size = ctypes.sizeof(regitry_path_data) self.ql.mem.write(regitry_path_addr, bytes(regitry_path_data)) self.structure_last_addr += regitry_path_size @@ -308,12 +344,13 @@ def init_registry_path(self): def init_eprocess(self): addr = self.structure_last_addr - logging.info("[+] EPROCESS is is 0x%x" %addr) + self.ql.log.info("EPROCESS is is 0x%x" %addr) - if self.ql.archbit == 64: - self.eprocess_object = EPROCESS64(self.ql, addr) - else: + + if self.ql.archtype == QL_ARCH.X86: self.eprocess_object = EPROCESS32(self.ql, addr) + elif self.ql.archtype == QL_ARCH.X8664: + self.eprocess_object = EPROCESS64(self.ql, addr) size = ctypes.sizeof(self.eprocess_object) self.ql.mem.write(addr, bytes(self.driver_object)) @@ -328,12 +365,12 @@ def init_ki_user_shared_data(self): struct information: https://doxygen.reactos.org/d8/dae/modules_2rostests_2winetests_2ntdll_2time_8c_source.html ''' - if self.ql.archbit == 32: + if self.ql.archtype == QL_ARCH.X86: KI_USER_SHARED_DATA = 0xFFDF0000 - elif self.ql.archbit == 64: + elif self.ql.archtype == QL_ARCH.X8664: KI_USER_SHARED_DATA = 0xFFFFF78000000000 - logging.info("[+] KI_USER_SHARED_DATA is 0x%x" %KI_USER_SHARED_DATA) + self.ql.log.info("KI_USER_SHARED_DATA is 0x%x" %KI_USER_SHARED_DATA) shared_user_data = KUSER_SHARED_DATA() @@ -346,8 +383,12 @@ class QlLoaderPE(QlLoader, Process): def __init__(self, ql): super(QlLoaderPE, self).__init__(ql) self.ql = ql - self.libcache = self.ql.libcache + if type(self.ql.libcache) == bool: + self.libcache = QlPeCache() if self.ql.libcache else None + else: + self.libcache = self.ql.libcache self.path = self.ql.path + self.is_driver = False def run(self): self.init_dlls = [b"ntdll.dll", b"kernel32.dll", b"user32.dll"] @@ -355,12 +396,12 @@ def run(self): self.pe_entry_point = 0 self.sizeOfStackReserve = 0 - if not self.ql.shellcoder: + if not self.ql.code: self.pe = pefile.PE(self.path, fast_load=True) - self.is_driver = (self.pe.OPTIONAL_HEADER.Subsystem == 1) - if self.is_driver: - self.init_dlls = [b"ntoskrnl.exe"] - self.sys_dlls = [b"ntoskrnl.exe"] + self.is_driver = self.pe.is_driver() + if self.is_driver == True: + self.init_dlls.append(b"ntoskrnl.exe") + self.sys_dlls.append(b"ntoskrnl.exe") if self.ql.archtype == QL_ARCH.X86: self.stack_address = int(self.ql.os.profile.get("OS32", "stack_address"), 16) @@ -395,7 +436,15 @@ def run(self): self.ql.os.heap = QlMemoryHeap(self.ql, self.ql.os.heap_base_address, self.ql.os.heap_base_address + self.ql.os.heap_base_size) self.ql.os.setupComponents() self.ql.os.entry_point = self.entry_point - self.cmdline = bytes(((str(self.ql.os.userprofile)) + "Desktop\\" + (self.ql.targetname) + "\x00"), "utf-8") + cmdline = (str(self.ql.os.userprofile)) + "Desktop\\" + self.ql.targetname + self.filepath = bytes(cmdline + "\x00", "utf-8") + for arg in self.argv[1:]: + if ' ' in arg: + cmdline += f' "{arg}"' + else: + cmdline += f' {arg}' + cmdline += "\x00" + self.cmdline = bytes(cmdline, "utf-8") self.load() @@ -407,10 +456,10 @@ def init_thread_information_block(self): def load(self): # set stack pointer - logging.info("[+] Initiate stack address at 0x%x " % self.stack_address) + self.ql.log.info("Initiate stack address at 0x%x " % self.stack_address) self.ql.mem.map(self.stack_address, self.stack_size, info="[stack]") - if self.path and not self.ql.shellcoder: + if self.path and not self.ql.code: # for simplicity, no image base relocation self.pe_image_address = self.pe.OPTIONAL_HEADER.ImageBase self.pe_image_address_size = self.ql.mem.align(self.pe.OPTIONAL_HEADER.SizeOfImage, 0x1000) @@ -422,8 +471,8 @@ def load(self): self.entry_point = self.pe_entry_point = self.pe_image_address + self.pe.OPTIONAL_HEADER.AddressOfEntryPoint self.sizeOfStackReserve = self.pe.OPTIONAL_HEADER.SizeOfStackReserve - logging.info("[+] Loading %s to 0x%x" % (self.path, self.pe_image_address)) - logging.info("[+] PE entry point at 0x%x" % self.entry_point) + self.ql.log.info("Loading %s to 0x%x" % (self.path, self.pe_image_address)) + self.ql.log.info("PE entry point at 0x%x" % self.entry_point) self.images.append(self.coverage_image(self.pe_image_address, self.pe_image_address + self.pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, self.path)) # Stack should not init at the very bottom. Will cause errors with Dlls @@ -434,13 +483,13 @@ def load(self): self.ql.reg.ebp = sp if self.pe.is_dll(): - logging.debug('[+] Setting up DllMain args') + self.ql.log.debug('Setting up DllMain args') load_addr_bytes = self.pe_image_address.to_bytes(length=4, byteorder='little') - logging.debug('[+] Writing 0x%08X (IMAGE_BASE) to [ESP+4](0x%08X)' % (self.pe_image_address, sp + 0x4)) + self.ql.log.debug('Writing 0x%08X (IMAGE_BASE) to [ESP+4](0x%08X)' % (self.pe_image_address, sp + 0x4)) self.ql.mem.write(sp + 0x4, load_addr_bytes) - logging.debug('[+] Writing 0x01 (DLL_PROCESS_ATTACH) to [ESP+8](0x%08X)' % (sp + 0x8)) + self.ql.log.debug('Writing 0x01 (DLL_PROCESS_ATTACH) to [ESP+8](0x%08X)' % (sp + 0x8)) self.ql.mem.write(sp + 0x8, int(1).to_bytes(length=4, byteorder='little')) elif self.ql.archtype == QL_ARCH.X8664: @@ -448,15 +497,15 @@ def load(self): self.ql.reg.rbp = sp if self.pe.is_dll(): - logging.debug('[+] Setting up DllMain args') + self.ql.log.debug('Setting up DllMain args') - logging.debug('[+] Setting RCX (arg1) to %16X (IMAGE_BASE)' % (self.pe_image_address)) + self.ql.log.debug('Setting RCX (arg1) to %16X (IMAGE_BASE)' % (self.pe_image_address)) self.ql.reg.rcx = self.pe_image_address - logging.debug('[+] Setting RDX (arg2) to 1 (DLL_PROCESS_ATTACH)') + self.ql.log.debug('Setting RDX (arg2) to 1 (DLL_PROCESS_ATTACH)') self.ql.reg.rdx = 1 else: - raise QlErrorArch("[!] Unknown ql.arch") + raise QlErrorArch("Unknown ql.arch") # if this is NOT a driver, init tib/peb/ldr if not self.is_driver: # userland program @@ -473,7 +522,7 @@ def load(self): # setup CR4, some drivers may check this at initialized time self.ql.uc.reg_write(UC_X86_REG_CR4, 0x6f8) - logging.debug('[+] Setting up DriverEntry args') + self.ql.log.debug('Setting up DriverEntry args') self.ql.stop_execution_pattern = 0xDEADC0DE if self.ql.archtype == QL_ARCH.X86: # Win32 @@ -482,16 +531,16 @@ def load(self): # so if the user did not configure stop options, write a sentinel return value self.ql.mem.write(sp, self.ql.stop_execution_pattern.to_bytes(length=4, byteorder='little')) - logging.debug('[+] Writing 0x%08X (PDRIVER_OBJECT) to [ESP+4](0x%08X)' % (self.ql.driver_object_address, sp+0x4)) - logging.debug('[+] Writing 0x%08X (RegistryPath) to [ESP+8](0x%08X)' % (self.ql.regitry_path_address, sp+0x8)) + self.ql.log.debug('Writing 0x%08X (PDRIVER_OBJECT) to [ESP+4](0x%08X)' % (self.ql.driver_object_address, sp+0x4)) + self.ql.log.debug('Writing 0x%08X (RegistryPath) to [ESP+8](0x%08X)' % (self.ql.regitry_path_address, sp+0x8)) elif self.ql.archtype == QL_ARCH.X8664: # Win64 if not self.ql.stop_options.any: # We know that a driver will return, # so if the user did not configure stop options, write a sentinel return value self.ql.mem.write(sp, self.ql.stop_execution_pattern.to_bytes(length=8, byteorder='little')) - logging.debug('[+] Setting RCX (arg1) to %16X (PDRIVER_OBJECT)' % (self.ql.driver_object_address)) - logging.debug('[+] Setting RDX (arg2) to %16X (PUNICODE_STRING)' % (self.ql.regitry_path_address)) + self.ql.log.debug('Setting RCX (arg1) to %16X (PDRIVER_OBJECT)' % (self.ql.driver_object_address)) + self.ql.log.debug('Setting RDX (arg2) to %16X (PUNICODE_STRING)' % (self.ql.regitry_path_address)) # setup args for DriverEntry() self.ql.os.set_function_args((self.ql.driver_object_address, self.ql.regitry_path_address)) @@ -533,13 +582,13 @@ def load(self): super().load_dll(entry.dll, self.is_driver) for imp in entry.imports: # fix IAT - # logging.info(imp.name) - # logging.info(self.import_address_table[imp.name]) + # ql.log.info(imp.name) + # ql.log.info(self.import_address_table[imp.name]) if imp.name: try: addr = self.import_address_table[dll_name][imp.name] except KeyError: - logging.debug("[!] Error in loading function %s" % imp.name.decode()) + self.ql.log.debug("Error in loading function %s" % imp.name.decode()) else: addr = self.import_address_table[dll_name][imp.ordinal] @@ -549,12 +598,11 @@ def load(self): address = self.ql.pack64(addr) self.ql.mem.write(imp.address, address) - logging.debug("[+] Done with loading %s" % self.path) - self.filepath = self.cmdline + self.ql.log.debug("Done with loading %s" % self.path) self.ql.os.entry_point = self.entry_point self.ql.os.pid = 101 - elif self.ql.shellcoder: + elif self.ql.code: self.filepath = b"" if self.ql.archtype == QL_ARCH.X86: self.ql.reg.esp = self.stack_address + 0x3000 @@ -564,12 +612,12 @@ def load(self): self.ql.reg.rbp = self.ql.reg.rsp # load shellcode in - self.ql.mem.map(self.entry_point, self.ql.os.shellcoder_ram_size, info="[shellcode_base]") + self.ql.mem.map(self.entry_point, self.ql.os.code_ram_size, info="[shellcode_base]") # rewrite entrypoint for windows shellcode self.ql.os.entry_point = self.entry_point self.ql.os.pid = 101 - self.ql.mem.write(self.entry_point, self.ql.shellcoder) + self.ql.mem.write(self.entry_point, self.ql.code) self.init_thread_information_block() # load dlls diff --git a/qiling/loader/pe_uefi.py b/qiling/loader/pe_uefi.py index 9f5880799..4773f8686 100644 --- a/qiling/loader/pe_uefi.py +++ b/qiling/loader/pe_uefi.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import logging -from contextlib import contextmanager +from typing import Sequence +from pefile import PE from qiling.const import QL_ARCH +from qiling.exception import QlErrorArch, QlMemoryMappedError +from qiling.loader.loader import QlLoader from qiling.os.memory import QlMemoryHeap -from qiling.os.utils import QlErrorArch, QlErrorFileType from qiling.os.uefi import context, st, smst from qiling.os.uefi.ProcessorBind import CPU_STACK_ALIGNMENT @@ -20,8 +21,6 @@ from qiling.os.uefi.protocols import EfiSmmSwDispatch2Protocol from qiling.os.uefi.protocols import PcdProtocol -from pefile import PE -from .loader import QlLoader class QlLoaderPE_UEFI(QlLoader): def __init__(self, ql): @@ -30,112 +29,137 @@ def __init__(self, ql): self.modules = [] self.events = {} self.notify_list = [] - self.next_image_base = 0x10000 - - def save(self): + self.next_image_base = 0 + + # list of members names to save and restore + __save_members = ( + 'modules', + 'events', + 'notify_list', + 'next_image_base', + 'loaded_image_protocol_modules', + 'tpl', + 'efi_conf_table_array', + 'efi_conf_table_array_ptr', + 'efi_conf_table_data_ptr', + 'efi_conf_table_data_next_ptr' + ) + + def save(self) -> dict: saved_state = super(QlLoaderPE_UEFI, self).save() - # We can't serialize self.modules since it contain pefile objects. let's remove it now and generate it again when loading. - modules = [] - for mod in self.modules: - modules.append(mod[:3]) - saved_state['modules'] = modules - - saved_state['events'] = self.events - #saved_state['handle_dict'] = self.handle_dict - saved_state['notify_list'] = self.notify_list - saved_state['next_image_base'] = self.next_image_base - saved_state['loaded_image_protocol_modules'] = self.loaded_image_protocol_modules - saved_state['tpl'] = self.tpl - saved_state['efi_configuration_table'] = self.efi_configuration_table + for member in QlLoaderPE_UEFI.__save_members: + saved_state[member] = getattr(self, member) + # since this class initialize the heap (that is hosted by the OS object), we will store it here. saved_state['heap'] = self.ql.os.heap.save() + return saved_state - def restore(self, saved_state): + def restore(self, saved_state: dict): super(QlLoaderPE_UEFI, self).restore(saved_state) - self.modules = [] - for mod in saved_state['modules']: - self.modules.append(mod+(PE(mod[0], fast_load=True),)) - self.events = saved_state['events'] - #self.handle_dict = saved_state['handle_dict'] - self.notify_list = saved_state['notify_list'] - self.next_image_base = saved_state['next_image_base'] - self.loaded_image_protocol_modules = saved_state['loaded_image_protocol_modules'] - self.tpl = saved_state['tpl'] - self.efi_configuration_table = saved_state['efi_configuration_table'] - self.ql.os.heap.restore(saved_state['heap']) - @contextmanager - def map_memory(self, addr, size): - self.ql.mem.map(addr, size) + for member in QlLoaderPE_UEFI.__save_members: + setattr(self, member, saved_state[member]) - try: - yield - finally: - self.ql.mem.unmap(addr, size) + self.ql.os.heap.restore(saved_state['heap']) def install_loaded_image_protocol(self, image_base, image_size): fields = { - 'revision' : int(self.ql.os.profile["LOADED_IMAGE_PROTOCOL"]["revision"], 0), 'gST' : self.gST, 'image_base' : image_base, 'image_size' : image_size } - description = EfiLoadedImageProtocol.make_descriptor(fields) - self.dxe_context.install_protocol(description, image_base) + descriptor = EfiLoadedImageProtocol.make_descriptor(fields) + self.dxe_context.install_protocol(descriptor, image_base) self.loaded_image_protocol_modules.append(image_base) - def map_and_load(self, path, execute_now=False): + def map_and_load(self, path: str, exec_now: bool=False): + """Map and load a module into memory. + + The specified module would be mapped and loaded into the address set + in the `next_image_base` member. It is the caller's responsibility to + make sure that the memory is available. + + On success, `next_image_base` will be updated accordingly. + + Args: + path : path of the module binary to load + exec_now : execute module right away; will be enququed if not + + Raises: + QlMemoryMappedError : when `next_image_base` is not available + """ + ql = self.ql pe = PE(path, fast_load=True) - # Make sure no module will occupy the NULL page - if self.next_image_base > pe.OPTIONAL_HEADER.ImageBase: - IMAGE_BASE = self.next_image_base - pe.relocate_image(IMAGE_BASE) + # use image base only if it does not point to NULL + image_base = pe.OPTIONAL_HEADER.ImageBase or self.next_image_base + image_size = ql.mem.align(pe.OPTIONAL_HEADER.SizeOfImage, 0x1000) + + assert (image_base % 0x1000) == 0, 'image base is expected to be page-aligned' + + if image_base != pe.OPTIONAL_HEADER.ImageBase: + pe.relocate_image(image_base) + + pe.parse_data_directories() + data = bytes(pe.get_memory_mapped_image()) + + ql.mem.map(image_base, image_size, info="[module]") + ql.mem.write(image_base, data) + ql.log.info(f'Module {path} loaded to {image_base:#x}') + + entry_point = image_base + pe.OPTIONAL_HEADER.AddressOfEntryPoint + ql.log.info(f'Module entry point at {entry_point:#x}') + + # the 'entry_point' member is used by the debugger. if not set, set it + # to the first loaded module entry point so the debugger can break + if self.entry_point == 0: + self.entry_point = entry_point + + self.install_loaded_image_protocol(image_base, image_size) + + # this would be used later be os.find_containing_image + self.images.append(self.coverage_image(image_base, image_base + image_size, path)) + + # update next memory slot to allow sequencial loading. its availability + # is unknown though + self.next_image_base = image_base + image_size + + module_info = (path, image_base, entry_point) + + # execute the module right away or enqueue it + if exec_now: + # call entry point while retaining the current return address + self.execute_module(*module_info, eoe_trap=None) else: - IMAGE_BASE = pe.OPTIONAL_HEADER.ImageBase - IMAGE_SIZE = ql.mem.align(pe.OPTIONAL_HEADER.SizeOfImage, 0x1000) - - while IMAGE_BASE + IMAGE_SIZE < self.heap_base_address: - if not ql.mem.is_mapped(IMAGE_BASE, 1): - self.next_image_base = IMAGE_BASE + 0x10000 - ql.mem.map(IMAGE_BASE, IMAGE_SIZE) - pe.parse_data_directories() - data = bytearray(pe.get_memory_mapped_image()) - ql.mem.write(IMAGE_BASE, bytes(data)) - logging.info("[+] Loading %s to 0x%x" % (path, IMAGE_BASE)) - entry_point = IMAGE_BASE + pe.OPTIONAL_HEADER.AddressOfEntryPoint - if self.entry_point == 0: - # Setting entry point to the first loaded module entry point, so the debugger can break. - self.entry_point = entry_point - logging.info("[+] PE entry point at 0x%x" % entry_point) - self.install_loaded_image_protocol(IMAGE_BASE, IMAGE_SIZE) - self.images.append(self.coverage_image(IMAGE_BASE, IMAGE_BASE + pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, path)) - if execute_now: - logging.info(f'[+] Running from 0x{entry_point:x} of {path}') - assembler = self.ql.create_assembler() - code = f""" - mov rcx, {IMAGE_BASE} - mov rdx, {self.gST} - mov rax, {entry_point} - call rax - """ - runcode, _ = assembler.asm(code) - ptr = ql.os.heap.alloc(len(runcode)) - ql.mem.write(ptr, bytes(runcode)) - ql.os.exec_arbitrary(ptr, ptr+len(runcode)) - - else: - self.modules.append((path, IMAGE_BASE, entry_point, pe)) - return True - else: - IMAGE_BASE += 0x10000 - pe.relocate_image(IMAGE_BASE) - return False + self.modules.append(module_info) + + def call_function(self, addr: int, args: Sequence[int], ret: int): + """Call a function after properly setting up its arguments and return address. + + Args: + addr : function address + args : a sequence of arguments to pass to the function; may be empty + ret : return address; may be None + """ + + # arguments gpr (ms x64 cc) + regs = ('rcx', 'rdx', 'r8', 'r9') + assert len(args) <= len(regs), f'currently supporting up to {len(regs)} arguments' + + # set up the arguments + for reg, arg in zip(regs, args): + self.ql.reg.write(reg, arg) + + # if provided, set return address + if ret is not None: + self.ql.stack_push(ret) + + self.ql.reg.rip = addr def unload_modules(self): for handle in self.loaded_image_protocol_modules: @@ -145,45 +169,53 @@ def unload_modules(self): unload_ptr = self.ql.unpack64(loaded_image_protocol.Unload) if unload_ptr != 0: - self.ql.stack_push(self.end_of_execution_ptr) - self.ql.reg.rcx = handle - self.ql.reg.rip = unload_ptr + self.ql.log.info(f'Unloading module {handle:#x}, calling {unload_ptr:#x}') + self.call_function(unload_ptr, [handle], self.end_of_execution_ptr) self.loaded_image_protocol_modules.remove(handle) - logging.info(f'Unloading module {handle:#x}, calling {unload_ptr:#x}') return True return False - def execute_module(self, path, image_base, entry_point, EOE_ptr): - self.ql.stack_push(EOE_ptr) - self.ql.reg.rcx = image_base - self.ql.reg.rdx = self.gST - self.ql.reg.rip = entry_point + def execute_module(self, path: str, image_base: int, entry_point: int, eoe_trap: int): + """Start the execution of a UEFI module. + + Args: + image_base : module base address + entry_point : module entry point address + eoe_trap : end-of-execution trap address; may be None + """ + + # use familiar UEFI names + ImageHandle = image_base + SystemTable = self.gST + + self.call_function(entry_point, [ImageHandle, SystemTable], eoe_trap) self.ql.os.entry_point = entry_point - logging.info(f'Running from {entry_point:#010x} of {path}') + self.ql.log.info(f'Running from {entry_point:#010x} of {path}') def execute_next_module(self): - if self.ql.os.notify_before_module_execution(self.ql, self.modules[0][0]): + if not self.modules or self.ql.os.notify_before_module_execution(self.ql, self.modules[0][0]): return - path, image_base, entry_point, _ = self.modules.pop(0) + path, image_base, entry_point = self.modules.pop(0) self.execute_module(path, image_base, entry_point, self.end_of_execution_ptr) def run(self): - # intel architecture uefi implementaion only + # intel architecture uefi implementation only if self.ql.archtype not in (QL_ARCH.X86, QL_ARCH.X8664): - raise QlErrorArch("[!] Unsupported architecture") + raise QlErrorArch("Unsupported architecture") # x86-64 arch only if self.ql.archtype != QL_ARCH.X8664: - raise QlErrorArch("[!] Only 64 bit arch is supported at the moment") + raise QlErrorArch("Only 64 bit arch is supported at the moment") - self.loaded_image_protocol_guid = self.ql.os.profile["LOADED_IMAGE_PROTOCOL"]["guid"] + self.loaded_image_protocol_guid = self.ql.os.profile["LOADED_IMAGE_PROTOCOL"]["Guid"] self.loaded_image_protocol_modules = [] self.tpl = 4 # TPL_APPLICATION + self.user_defined_api = self.ql.os.user_defined_api self.user_defined_api_onenter = self.ql.os.user_defined_api_onenter self.user_defined_api_onexit = self.ql.os.user_defined_api_onexit @@ -196,21 +228,21 @@ def run(self): # -------- init BS / RT / DXE data structures and protocols -------- os_profile = self.ql.os.profile[arch_key] - self.dxe_context = context.UefiContext(self.ql) + self.dxe_context = context.DxeContext(self.ql) # initialize and locate heap heap_base = int(os_profile["heap_address"], 0) heap_size = int(os_profile["heap_size"], 0) self.dxe_context.init_heap(heap_base, heap_size) self.heap_base_address = heap_base - logging.info(f"[+] Located heap at {heap_base:#010x}") + self.ql.log.info(f"Located heap at {heap_base:#010x}") # initialize and locate stack stack_base = int(os_profile["stack_address"], 0) stack_size = int(os_profile["stack_size"], 0) self.dxe_context.init_stack(stack_base, stack_size) sp = stack_base + stack_size - CPU_STACK_ALIGNMENT - logging.info(f"[+] Located stack at {sp:#010x}") + self.ql.log.info(f"Located stack at {sp:#010x}") # TODO: statically allocating 256 KiB for ST, RT, BS, DS and Configuration Tables. # however, this amount of memory is rather arbitrary @@ -220,7 +252,6 @@ def run(self): protocols = ( EfiSmmAccess2Protocol, EfiSmmBase2Protocol, - PcdProtocol ) for proto in protocols: @@ -238,15 +269,13 @@ def run(self): heap_base = int(smm_profile["heap_address"], 0) heap_size = int(smm_profile["heap_size"], 0) self.smm_context.init_heap(heap_base, heap_size) - logging.info(f"[+] Located SMM heap at {heap_base:#010x}") + self.ql.log.info(f"Located SMM heap at {heap_base:#010x}") # TODO: statically allocating 256 KiB for SMM ST. # however, this amount of memory is rather arbitrary gSmst = self.smm_context.heap.alloc(256 * 1024) smst.initialize(self.ql, gSmst) - self.gST = gST - self.gSmst = gSmst self.in_smm = False protocols = ( @@ -273,15 +302,18 @@ def run(self): self.load_address = 0 self.next_image_base = int(os_profile["image_address"], 0) - for dependency in self.ql.argv: - if not self.map_and_load(dependency): - raise QlErrorFileType("Can't map dependency") + try: + for dependency in self.ql.argv: + self.map_and_load(dependency) + except QlMemoryMappedError: + self.ql.log.critical("Couldn't map dependency") - logging.info(f"[+] Done with loading {self.ql.path}") + self.ql.log.info(f"Done with loading {self.ql.path}") - # hack: reuse first byte of ST to set a trap + # set up an end-of-execution hook to regain control when module is done + # executing (i.e. when the entry point function returns). that should be + # set on a non-executable address, so SystemTable's address was picked self.end_of_execution_ptr = gST - self.ql.mem.write(self.end_of_execution_ptr, b'\xcc') self.ql.hook_address(hook_EndOfExecution, self.end_of_execution_ptr) self.execute_next_module() diff --git a/qiling/os/const.py b/qiling/os/const.py index 0158bdc41..ff6cd2245 100644 --- a/qiling/os/const.py +++ b/qiling/os/const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# STDCALL = 1 CDECL = 2 @@ -33,6 +33,7 @@ THREAD_STATUS_TERMINATED = 2 THREAD_STATUS_STOPPED = 4 THREAD_STATUS_TIMEOUT = 3 +THREAD_STATUS_SUSPEND = 5 reptypedict = { "BSTR": "POINTER", @@ -122,6 +123,7 @@ "REFCLSID": "POINTER", "REFIID": "POINTER", "REGSAM": "POINTER", + "SC_HANDLE": "HANDLE", "SHELLEXECUTEINFOA": "POINTER", "SHELLEXECUTEINFOW": "POINTER", "SHFILEINFOW": "POINTER", diff --git a/qiling/os/disk.py b/qiling/os/disk.py index 617783d89..765712ac1 100644 --- a/qiling/os/disk.py +++ b/qiling/os/disk.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .mapper import QlFsMappedObject diff --git a/qiling/os/dos/dos.py b/qiling/os/dos/dos.py index 5445406b3..52c2259b4 100644 --- a/qiling/os/dos/dos.py +++ b/qiling/os/dos/dos.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import types, os, struct, time, logging +import types, os, struct, time from unicorn import * + from qiling.const import QL_INTERCEPT from qiling.os.os import QlOs from qiling.os.utils import PathUtils @@ -308,7 +309,7 @@ def int10(self): elif al == 0x11 or al == 0x12: curses.resizeterm(480, 640) else: - logging.info("Exception: int 10h syscall Not Found, al: %s" % hex(al)) + self.ql.log.info("Exception: int 10h syscall Not Found, al: %s" % hex(al)) raise NotImplementedError() # Quoted from https://linux.die.net/man/3/resizeterm # @@ -317,13 +318,13 @@ def int10(self): # read on the next call to getch. ch = self._get_ch_non_blocking() if ch == curses.KEY_RESIZE: - logging.info(f"[!] You term has been resized!") + self.ql.log.info(f"You term has been resized!") elif ch != -1: curses.ungetch(ch) self.stdscr.scrollok(True) if not curses.has_colors(): - logging.info(f"[!] Warning: your terminal doesn't support colors, content might not be displayed correctly.") + self.ql.log.info(f"Warning: your terminal doesn't support colors, content might not be displayed correctly.") # https://en.wikipedia.org/wiki/BIOS_color_attributes # blink support? @@ -364,8 +365,8 @@ def int10(self): cy, cx = self.stdscr.getyx() attr = self._get_attr(fg, bg) if ch != 0 or cl != 0 or dh != y - 1 or dl != x - 1: - logging.info(f"[!] Warning: Partial scroll is unsupported. Will scroll the whole page.") - logging.info(f"[!] Resolution: {y}x{x} but asked to scroll [({ch},{cl}),({dh}, {dl})]") + self.ql.log.info(f"Warning: Partial scroll is unsupported. Will scroll the whole page.") + self.ql.log.info(f"Resolution: {y}x{x} but asked to scroll [({ch},{cl}),({dh}, {dl})]") if al != 0: self.stdscr.scroll(al) ny = 0 @@ -402,7 +403,7 @@ def int10(self): orig_bg |= 0b1000 self.ql.reg.ah = ((orig_bg << 4) & orig_fg) elif ah == 0xE: - logging.debug(f"Echo: {hex(al)} -> {curses.ascii.unctrl(al)}") + self.ql.log.debug(f"Echo: {hex(al)} -> {curses.ascii.unctrl(al)}") y, x = self.stdscr.getmaxyx() cy, cx = self.stdscr.getyx() fg = self.ql.reg.bl @@ -413,7 +414,7 @@ def int10(self): attr = self.stdscr.inch(cy, cx) & curses.A_COLOR if al == 0xa: # \n will erase current line with echochar, so we have to handle it carefully. - logging.info(f"Resolution: {x}x{y}, Cursor position: {cx},{cy}, Going to get a new line.") + self.ql.log.info(f"Resolution: {x}x{y}, Cursor position: {cx},{cy}, Going to get a new line.") if y-1 == cy: # scroll doesn't affect our cursor self.stdscr.scroll(1) @@ -423,7 +424,7 @@ def int10(self): else: self.stdscr.echochar(al, attr) else: - logging.info("Exception: int 10h syscall Not Found, ah: %s" % hex(ah)) + self.ql.log.info("Exception: int 10h syscall Not Found, ah: %s" % hex(ah)) raise NotImplementedError() if self.stdscr is not None: self.stdscr.refresh() @@ -443,7 +444,7 @@ def int13(self): sector = self.ql.reg.cx & 63 head = self.ql.reg.dh if not self.ql.os.fs_mapper.has_mapping(idx): - self.logging.info(f"[!] Warning: No such disk: {hex(idx)}") + self.ql.log.info(f"Warning: No such disk: {hex(idx)}") self.ql.reg.ah = INT13DiskError.BadCommand.value self.set_cf() return @@ -457,7 +458,7 @@ def int13(self): # https://stanislavs.org/helppc/int_13-8.html idx = self.ql.reg.dl if not self.ql.os.fs_mapper.has_mapping(idx): - logging.info(f"[!] Warning: No such disk: {hex(idx)}") + self.ql.log.info(f"Warning: No such disk: {hex(idx)}") self.ql.reg.ah = INT13DiskError.BadCommand.value self.set_cf() return @@ -494,9 +495,9 @@ def int13(self): idx = self.ql.reg.dl dapbs = self.ql.mem.read(self.calculate_address(ds, si), 0x10) _, _, cnt, offset, segment, lba = self._parse_dap(dapbs) - logging.info(f"Reading {cnt} sectors from disk {hex(idx)} with LBA {lba}") + self.ql.log.info(f"Reading {cnt} sectors from disk {hex(idx)} with LBA {lba}") if not self.ql.os.fs_mapper.has_mapping(idx): - logging.info(f"[!] Warning: No such disk: {hex(idx)}") + self.ql.log.info(f"Warning: No such disk: {hex(idx)}") self.ql.reg.ah = INT13DiskError.BadCommand.value self.set_cf() return @@ -509,9 +510,9 @@ def int13(self): idx = self.ql.reg.dl dapbs = self.ql.mem.read(self.calculate_address(ds, si), 0x10) _, _, cnt, offset, segment, lba = self._parse_dap(dapbs) - logging.info(f"Write {cnt} sectors to disk {hex(idx)} with LBA {lba}") + self.ql.log.info(f"Write {cnt} sectors to disk {hex(idx)} with LBA {lba}") if not self.ql.os.fs_mapper.has_mapping(idx): - logging.info(f"[!] Warning: No such disk: {hex(idx)}") + self.ql.log.info(f"Warning: No such disk: {hex(idx)}") self.ql.reg.ah = INT13DiskError.BadCommand.value self.set_cf() return @@ -521,7 +522,7 @@ def int13(self): self.clear_cf() self.ql.reg.ah = 0 else: - logging.info("Exception: int 13h syscall Not Found, ah: %s" % hex(ah)) + self.ql.log.info("Exception: int 13h syscall Not Found, ah: %s" % hex(ah)) raise NotImplementedError() def int15(self): @@ -539,13 +540,13 @@ def int15(self): self.clear_cf() elif ax == 0x5307: if self.ql.reg.bx == 1 and self.ql.reg.cx == 3: - self.logging.info("[+] Emulation Stop") + self.ql.log.info("Emulation Stop") self.ql.uc.emu_stop() elif ah == 0x86: dx = self.ql.reg.dx cx = self.ql.reg.cx full_secs = ((cx << 16) + dx) / 1000000 - logging.info(f"Goint to sleep {full_secs} seconds") + self.ql.log.info(f"Goint to sleep {full_secs} seconds") time.sleep(full_secs) # Note: Since we are in a single thread environment, we assume @@ -567,7 +568,7 @@ def _get_scan_code(self, ch): if ch in SCANCODES: return SCANCODES[ch] else: - logging.info(f"[!] Warning: scan code for {hex(ch)} doesn't exist!") + self.ql.log.info(f"Warning: scan code for {hex(ch)} doesn't exist!") return 0 def int16(self): @@ -575,7 +576,7 @@ def int16(self): if ah == 0x0: curses.nonl() key = self._parse_key(self.stdscr.getch()) - logging.debug(f"Get key: {hex(key)}") + self.ql.log.debug(f"Get key: {hex(key)}") if curses.ascii.isascii(key): self.ql.reg.al = key else: @@ -591,7 +592,7 @@ def int16(self): self.set_flag(0x40) self.ql.reg.ax = 0 else: - logging.debug(f"Has key: {hex(key)} ({curses.ascii.unctrl(key)})") + self.ql.log.debug(f"Has key: {hex(key)} ({curses.ascii.unctrl(key)})") self.ql.reg.al = key self.ql.reg.ah = self._get_scan_code(key) self.clear_flag(0x40) @@ -664,7 +665,7 @@ def int20(self): pass else: - logging.info("Exception: int 20h syscall Not Found, ah: %s" % hex(ah)) + self.ql.log.info("Exception: int 20h syscall Not Found, ah: %s" % hex(ah)) raise NotImplementedError() def int21(self): @@ -672,17 +673,17 @@ def int21(self): # exit if ah == 0x4C: - logging.info("[+] Emulation Stop") + self.ql.log.info("Emulation Stop") self.ql.uc.emu_stop() # character output elif ah == 0x2 or ah == 0x6: ch = chr(self.ql.reg.dl) self.ql.reg.al = self.ql.reg.dl - logging.info(ch) + self.ql.log.info(ch) # write to screen elif ah == 0x9: s = self.read_dos_string_from_ds_dx() - logging.info(s) + self.ql.log.info(s) elif ah == 0xC: # Clear input buffer pass @@ -772,13 +773,13 @@ def int21(self): self.ql.reg.cx = 0xFFFF self.clear_cf() else: - logging.info("Exception: int 21h syscall Not Found, ah: %s" % hex(ah)) + self.ql.log.info("Exception: int 21h syscall Not Found, ah: %s" % hex(ah)) raise NotImplementedError() def hook_syscall(self): def cb(ql, intno, user_data=None): ah = self.ql.reg.ah - logging.debug(f"INT {intno:x} with ah={hex(ah)}") + self.ql.log.debug(f"INT {intno:x} with ah={hex(ah)}") interrupt_tuple = (intno, ah) before = self.before_interrupt.get(interrupt_tuple, None) after = self.after_interrupt.get(interrupt_tuple, None) @@ -817,7 +818,7 @@ def run(self): self.ql.loader.elf_entry = self.ql.entry_point else: self.ql.entry_point = self.ql.loader.start_address - if not self.ql.shellcoder: + if not self.ql.code: self.start_time = datetime.now() self.ticks_per_second = self.ql.loader.ticks_per_second try: diff --git a/qiling/os/dos/utils.py b/qiling/os/dos/utils.py index ae467529b..3541acdd7 100644 --- a/qiling/os/dos/utils.py +++ b/qiling/os/dos/utils.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# def BIN2BCD(val: int): return val % 10 + (((val//10) % 10) << 4) + (((val//100) % 10) << 8) + (((val//1000) % 10) << 12) diff --git a/qiling/os/filestruct.py b/qiling/os/filestruct.py index 4608bbe36..d45a3d8bb 100644 --- a/qiling/os/filestruct.py +++ b/qiling/os/filestruct.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + import os from qiling.exception import * diff --git a/qiling/os/fncc.py b/qiling/os/fncc.py index e0c6ed710..fe2d92eb3 100644 --- a/qiling/os/fncc.py +++ b/qiling/os/fncc.py @@ -1,5 +1,240 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# +import os + +from typing import Callable, Sequence, Mapping, MutableMapping, Any +from functools import wraps + +from qiling.os.const import * +from qiling.os.windows.utils import * +from qiling.const import * +from qiling.exception import * +from qiling.os.windows.structs import * + +from .utils import * + +class QlOsFncc: + def __init__(self, ql): + self.ql = ql + + # choose a calling convention according to arch and os + if self.ql.archtype in (QL_ARCH.X86, QL_ARCH.A8086): + cc = 'cdecl' + elif self.ql.archtype == QL_ARCH.X8664: + cc = { + QL_OS.LINUX: 'amd64', + QL_OS.MACOS: 'macosx64', + QL_OS.WINDOWS: 'ms', + QL_OS.UEFI: 'ms' + }.get(self.ql.ostype, '') + elif self.ql.archtype == QL_ARCH.MIPS: + cc = { + QL_OS.LINUX: 'mips_o32' + }.get(self.ql.ostype, '') + else: + # do not pick a cc; let class overrides define the necessary handlers + cc = '' + + # register used to pass the return value to the caller + self.__cc_reg_ret = { + QL_ARCH.A8086: UC_X86_REG_AX, + QL_ARCH.X86: UC_X86_REG_EAX, + QL_ARCH.X8664: UC_X86_REG_RAX, + QL_ARCH.MIPS: UC_MIPS_REG_2 + }.get(self.ql.archtype, UC_X86_REG_INVALID) + + # registers used to pass arguments; a None stands for a stack argument + self._cc_args = { + 'amd64': (UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_R10, UC_X86_REG_R8, UC_X86_REG_R9) + (None, ) * 10, + 'macosx64': (UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_RCX, UC_X86_REG_R8, UC_X86_REG_R9) + (None, ) * 10, + 'cdecl': (None, ) * 16, + 'ms': (UC_X86_REG_RCX, UC_X86_REG_RDX, UC_X86_REG_R8, UC_X86_REG_R9) + (None, ) * 12, + 'mips_o32': (UC_MIPS_REG_4, UC_MIPS_REG_5, UC_MIPS_REG_6, UC_MIPS_REG_7) + (None, ) * 31 + }.get(cc, []) + + # shadow stack size in terms of stack items + self._shadow = 4 if cc == 'ms' else 0 + + # arch native address size in bytes + self._asize = self.ql.archbit // 8 + + def get_param_by_index(self, index: int) -> int: + """Get an argument value by its index. + """ + max_args = len(self._cc_args) + assert index < max_args, f'currently supporting up to {max_args} args' + + reg = self._cc_args[index] + + # should arg be read from a reg or the stack? + if reg is None: + if self.ql.archtype == QL_ARCH.MIPS: + return self.ql.stack.read(index * self._asize) + + # get matching stack item + si = index - self._cc_args.index(None) + + # skip return address and shadow space + return self.ql.stack_read((1 + self._shadow + si) * self._asize) + else: + return self.ql.uc.reg_read(reg) + + + def get_function_param(self, nargs: int) -> list: + """Get values of `nargs` first arguments. + """ + + params = [self.get_param_by_index(i) for i in range(nargs)] + + return params[0] if nargs == 1 else params + + def set_function_params(self, in_params: Mapping[str, int], out_params: MutableMapping[str, Any]) -> int: + """Retrieve parameters values according to their assigned type. + + Args: + in_params : a mapping of parameter names to their types + out_params: a mapping of parameter names to their values + Returns: number of consumed arguments; this is only relevant for the cdecl + calling convention, where all the arguments are stored on the stack. in that + case, the value reflects the number of items consumed from the stack + """ + + def __nullptr_or_deref(idx: int, deref: Callable[[int], Any]): + ptr = self.get_param_by_index(idx) + + return deref(ptr) if ptr else 0 + + def __handle_default(idx: int): + return self.get_param_by_index(idx), 1 + + def __handle_POINTER(idx: int): + return __handle_default(idx) + + def __handle_ULONGLONG_32(idx: int): + lo = self.get_param_by_index(idx) + hi = self.get_param_by_index(idx + 1) + + return (hi << 32) | lo, 2 + + def __handle_STRING(idx: int): + return __nullptr_or_deref(idx, self.read_cstring), 1 + + def __handle_WSTRING(idx: int): + return __nullptr_or_deref(idx, self.read_wstring), 1 + + def __handle_GUID(idx: int): + return __nullptr_or_deref(idx, lambda p: str(self.read_guid(p))), 1 + + param_handlers = { + ULONGLONG: __handle_POINTER if self.ql.archbit == 64 else __handle_ULONGLONG_32, + POINTER : __handle_POINTER, + STRING : __handle_STRING, + WSTRING : __handle_WSTRING, + GUID : __handle_GUID + } + + i = 0 + for pname, ptype in in_params.items(): + handler = param_handlers.get(ptype, __handle_default) + out_params[pname], consumed = handler(i) + i += consumed + + return i + + def set_return_value(self, ret: int): + self.ql.uc.reg_write(self.__cc_reg_ret, ret) + + def get_return_value(self) -> int: + return self.ql.uc.reg_read(self.__cc_reg_ret) + + # + # stdcall cdecl fastcall cc + # + def __cc(self, param_num, params, func, args, kwargs, passthru=False): + # read params values + if params is not None: + param_num = self.set_function_params(params, args[2]) + + + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + # if set, fire up the on-enter hook + if callable(self.winapi_func_onenter): + address, params = self.winapi_func_onenter(*args, **kwargs) + + # override original args set + args = (self.ql, address, params) + + # call function + result = func(*args, **kwargs) + + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + # if set, fire up the on-exit hook + if callable(self.winapi_func_onexit): + self.winapi_func_onexit(*args, **kwargs) + + # set return value + if result is not None: + self.set_return_value(result) + + # print + self.print_function(args[1], func.__name__, args[2], result, passthru) + + return result, param_num + + def x86_stdcall(self, param_num, params, func, args, kwargs, passthru=False): + # if we check ret_addr before the call, we can't modify the ret_addr from inside the hook + result, param_num = self.__cc(param_num, params, func, args, kwargs, passthru) + + ret_addr = self.ql.stack_read(0) + + # append syscall to list + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + self._call_api(func.__name__, params, result, self.ql.reg.arch_pc, ret_addr) + if not self.PE_RUN: + return result + + if not passthru: + # callee is responsible for cleaning up the stack; unwind the stack + self.ql.reg.arch_sp = self.ql.reg.arch_sp + ((param_num + 1) * self._asize) + self.ql.reg.arch_pc = ret_addr + + return result + + def x86_cdecl(self, param_num, params, func, args, kwargs, passthru=False): + result, _ = self.__cc(param_num, params, func, args, kwargs) + old_pc = self.ql.reg.arch_pc + + # append syscall to list + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + self._call_api(func.__name__, params, result, old_pc, self.ql.stack_read(0)) + if not self.PE_RUN: + return result + + if not passthru: + self.ql.reg.arch_pc = self.ql.stack_pop() + + return result + + def x8664_fastcall(self, param_num, params, func, args, kwargs, passthru=False): + result, param_num = self.__cc(param_num, params, func, args, kwargs) + old_pc = self.ql.reg.arch_pc + + # append syscall to list + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + self._call_api(func.__name__, params, result, old_pc, self.ql.stack_read(0)) + if not self.PE_RUN: + return result + + if not passthru: + self.ql.reg.arch_pc = self.ql.stack_pop() + + return result + + def mips_o32_call(self, param_num, params, func, args, kwargs): + result, param_num = self.__cc(param_num, params, func, args, kwargs) + self.ql.reg.arch_pc = self.ql.reg.ra + + return result \ No newline at end of file diff --git a/qiling/os/freebsd/const.py b/qiling/os/freebsd/const.py index 791a35dcb..e1cd26a7d 100644 --- a/qiling/os/freebsd/const.py +++ b/qiling/os/freebsd/const.py @@ -1,4 +1,4 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) \ No newline at end of file +# \ No newline at end of file diff --git a/qiling/os/freebsd/freebsd.py b/qiling/os/freebsd/freebsd.py index 1b2086684..ee5c3ddd3 100644 --- a/qiling/os/freebsd/freebsd.py +++ b/qiling/os/freebsd/freebsd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.arch.x86 import * from qiling.const import * @@ -14,13 +14,15 @@ def __init__(self, ql): super(QlOsFreebsd, self).__init__(ql) self.pid = self.profile.getint("KERNEL","pid") self.load() - + + def load(self): self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL) self.gdtm = GDTManager(self.ql) ql_x86_register_cs(self) ql_x86_register_ds_ss_es(self) - + + def hook_syscall(self, intno= None): return self.load_syscall() @@ -33,8 +35,8 @@ def run(self): self.ql.loader.elf_entry = self.ql.entry_point try: - if self.ql.shellcoder: - self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.shellcoder)), self.ql.timeout, self.ql.count) + if self.ql.code: + self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.code)), self.ql.timeout, self.ql.count) else: if self.ql.loader.elf_entry != self.ql.loader.entry_point: self.ql.emu_start(self.ql.loader.entry_point, self.ql.loader.elf_entry, self.ql.timeout) diff --git a/qiling/os/freebsd/map_syscall.py b/qiling/os/freebsd/map_syscall.py index bbb74a658..a8fdce94e 100644 --- a/qiling/os/freebsd/map_syscall.py +++ b/qiling/os/freebsd/map_syscall.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) - -# cols = ("x8664") +# from qiling.const import * diff --git a/qiling/os/freebsd/syscall.py b/qiling/os/freebsd/syscall.py index a5a4939a7..4731a2922 100644 --- a/qiling/os/freebsd/syscall.py +++ b/qiling/os/freebsd/syscall.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import logging +# + + from qiling.arch.x86_const import * def ql_syscall_clock_gettime(ql, clock_gettime_clock_id, clock_gettime_timespec, *args, **kw): - logging.info("clock_gettime()") + ql.log.info("clock_gettime()") return 0 @@ -26,5 +27,5 @@ def ql_syscall_sysarch(ql, op, parms, *args, **kw): #op_buf = ql.pack32(op) #ql.mem.write(parms, op_buf) - logging.info("sysarch(0x%x,0x%x) = %i" % (op, parms, regreturn)) + return regreturn diff --git a/qiling/os/linux/const.py b/qiling/os/linux/const.py index 3356e243f..45f469dea 100644 --- a/qiling/os/linux/const.py +++ b/qiling/os/linux/const.py @@ -1,4 +1,4 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# diff --git a/qiling/os/linux/fncc.py b/qiling/os/linux/fncc.py index 99453fdfd..bb91ba82a 100644 --- a/qiling/os/linux/fncc.py +++ b/qiling/os/linux/fncc.py @@ -1,12 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import struct, logging -from unicorn.x86_const import * -from unicorn.mips_const import * -from qiling.os.utils import * +# +from qiling.const import * +from qiling.exception import * DWORD = 1 UINT = 1 @@ -20,235 +18,6 @@ STRING = 4 WSTRING = 5 - -def read_cstring(ql, address): - result = "" - char = ql.mem.read(address, 1) - while char.decode(errors="ignore") != "\x00": - address += 1 - result += char.decode(errors="ignore") - char = ql.mem.read(address, 1) - return result - - -def _get_param_by_index(ql, index): - if ql.archtype == QL_ARCH.X86: - return _x86_get_params_by_index(ql, index) - elif ql.archtype == QL_ARCH.X8664: - return _x8664_get_params_by_index(ql, index) - elif ql.archtype == QL_ARCH.MIPS: - return _mips32_get_params_by_index(ql, index) - - -# TODO: x86 calling convention of Linux kernel? -def _x86_get_params_by_index(ql, index): - # index starts from 0 - # skip ret_addr - return ql.stack_read((index + 1) * 4) - - -def _x86_get_args(ql, number): - arg_list = [] - for i in range(number): - # skip ret_addr - arg_list.append(ql.stack_read((i + 1) * 4)) - if number == 1: - return arg_list[0] - else: - return arg_list - - -# The kernel interface uses RDI, RSI, RDX, R10, R8 and R9. -def _x8664_get_params_by_index(ql, index): - reg_list = [UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_RCX, UC_X86_REG_R8, UC_X86_REG_R9] - if index < 6: - return ql.uc.reg_read(reg_list[index]) - - index -= 6 - # skip ret_addr - return ql.stack_read((index + 1) * 8) - - -def _x8664_get_args(ql, number): - reg_list = [UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_RCX, UC_X86_REG_R8, UC_X86_REG_R9] - arg_list = [] - reg_num = number - if reg_num > 6: - reg_num = 6 - number -= reg_num - # get args in registers first - for i in reg_list[:reg_num]: - arg_list.append(ql.uc.reg_read(i)) - # the rest args are from stack - for i in range(number): - # skip ret_addr and 32 byte home space - arg_list.append(ql.stack_read((i + 7) * 8)) - if reg_num == 1: - return arg_list[0] - else: - return arg_list - - -def _mips32_get_params_by_index(ql, index): - reg_list = [UC_MIPS_REG_4, UC_MIPS_REG_5, UC_MIPS_REG_6, UC_MIPS_REG_7] - if index < 4: - return ql.uc.reg_read(reg_list[index]) - - return ql.stack.read(index * 4) - - -def set_function_params(ql, in_params, out_params): - index = 0 - for each in in_params: - if in_params[each] == DWORD or in_params[each] == POINTER: - out_params[each] = _get_param_by_index(ql, index) - elif in_params[each] == ULONGLONG: - if ql.archtype == QL_ARCH.X86: - low = _get_param_by_index(ql, index) - index += 1 - high = _get_param_by_index(ql, index) - out_params[each] = high << 8 + low - else: - out_params[each] = _get_param_by_index(ql, index) - elif in_params[each] == STRING: - ptr = _get_param_by_index(ql, index) - if ptr == 0: - out_params[each] = 0 - else: - out_params[each] = read_cstring(ql, ptr) - elif in_params[each] == WSTRING: - ptr = _get_param_by_index(ql, index) - if ptr == 0: - out_params[each] = 0 - else: - out_params[each] = read_wstring(ql, ptr) - index += 1 - return index - - -def set_return_value(ql, ret): - if ql.archtype == QL_ARCH.X86: - ql.uc.reg_write(UC_X86_REG_EAX, ret) - elif ql.archtype == QL_ARCH.X8664: - ql.uc.reg_write(UC_X86_REG_RAX, ret) - elif ql.archtype == QL_ARCH.MIPS: - ql.uc.reg_write(UC_MIPS_REG_2, ret) - - -def get_return_value(ql): - if ql.archtype == QL_ARCH.X86: - return ql.uc.reg_read(UC_X86_REG_EAX) - elif ql.archtype == QL_ARCH.X8664: - return ql.uc.reg_read(UC_X86_REG_RAX) - elif ql.archtype == QL_ARCH.MIPS: - return ql.uc.reg_read(UC_MIPS_REG_2) - - -def print_function(ql, passthru, address, function_name, params, ret): - PRINTK_LEVEL = { - 0: 'KERN_EMERGE', - 1: 'KERN_ALERT', - 1: 'KERN_CRIT', - 2: 'KERN_INFO', - 3: 'KERN_ERR', - 4: 'KERN_WARNING', - 5: 'KERN_NOTICE', - 6: 'KERN_INFO', - 7: 'KERN_DEBUG', - 8: '', - 9: 'KERN_CONT', - } - function_name = function_name.replace('hook_', '') - if function_name in ("__stdio_common_vfprintf", "__stdio_common_vfwprintf", - "printf", "wsprintfW", "sprintf"): - return - log = '0x%0.2x: %s(' % (address, function_name) - for each in params: - value = params[each] - if type(value) == str or type(value) == bytearray: - if function_name == 'printk': - info = value[:2] - try: - level = PRINTK_LEVEL[int(info[1])] - value = value[2:] - log += '%s = %s "%s", ' %(each, level, value) - except: - log += '%s = "%s", ' %(each, value) - else: - log += '%s = "%s", ' %(each, value) - elif type(value) == tuple: - log += '%s = 0x%x, ' % (each, value[0]) - else: - log += '%s = 0x%x, ' % (each, value) - log = log.strip(", ") - log += ')' - if ret is not None: - # do not print result for printk() - if function_name != 'printk': - log += ' = 0x%x' % ret - - if passthru: - log += ' (PASSTHRU)' - - # replace \n - log = log.replace("\n", "\\n") - if ql.output != QL_OUTPUT.DEBUG: - log = log.partition(" ")[-1] - logging.info(log) - else: - logging.debug(log) - - -def __x86_cc(ql, param_num, params, func, args, kwargs): - # read params - if params is not None: - param_num = set_function_params(ql, params, args[2]) - # call function - result = func(*args, **kwargs) - # set return value - if result is not None: - set_return_value(ql, result) - # print - print_function(ql, False, args[1], func.__name__, args[2], result) - return result, param_num - - -def x86_stdcall(ql, param_num, params, func, args, kwargs): - # get ret addr - ret_addr = ql.stack_read(0) - - result, param_num = __x86_cc(ql, param_num, params, func, args, kwargs) - - # update stack pointer - ql.reg.arch_sp += (param_num + 1) * 4 - - ql.reg.arch_pc = ret_addr - - return result - - -def x86_cdecl(ql, param_num, params, func, args, kwargs): - result, param_num = __x86_cc(ql, param_num, params, func, args, kwargs) - - ql.reg.arch_pc = ql.stack_pop() - - return result - - -def x8664_fastcall(ql, param_num, params, func, args, kwargs): - result, param_num = __x86_cc(ql, param_num, params, func, args, kwargs) - - ql.reg.arch_pc = ql.stack_pop() - - return result - - -def mips_call(ql, param_num, params, func, args, kwargs): - result, param_num = __x86_cc(ql, param_num, params, func, args, kwargs) - ql.reg.arch_pc = ql.reg.ra - return result - - def linux_kernel_api(param_num=None, params=None): """ @cc: windows api calling convention, only x86 needs this, x64 is always fastcall @@ -260,15 +29,14 @@ def wrapper(*args, **kwargs): ql = args[0] if ql.archtype == QL_ARCH.X86: # if cc == STDCALL: - return x86_stdcall(ql, param_num, params, func, args, kwargs) + return ql.os.x86_stdcall(param_num, params, func, args, kwargs) #elif cc == CDECL: - # return x86_cdecl(ql, param_num, params, func, args, kwargs) + # return ql.os.x86_cdecl(param_num, params, func, args, kwargs) elif ql.archtype == QL_ARCH.X8664: - return x8664_fastcall(ql, param_num, params, func, args, kwargs) + return ql.os.x8664_fastcall(param_num, params, func, args, kwargs) elif ql.archtype == QL_ARCH.MIPS: - return mips_call(ql, param_num, params, func, args, kwargs) + return ql.os.mips_o32_call(param_num, params, func, args, kwargs) else: - raise QlErrorArch("[!] Unknown ql.archtype") + raise QlErrorArch("Unknown ql.archtype") return wrapper - return decorator - + return decorator \ No newline at end of file diff --git a/qiling/os/linux/function_hook.py b/qiling/os/linux/function_hook.py index 05edeab04..5cbf5920b 100644 --- a/qiling/os/linux/function_hook.py +++ b/qiling/os/linux/function_hook.py @@ -1,13 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import struct, logging -from qiling.const import * +import struct -PT_DYNAMIC = 2 +from qiling.const import * + +PT_DYNAMIC = 2 DT_NULL = 0 DT_NEEDED = 1 DT_PLTRELSZ = 2 @@ -996,12 +997,12 @@ def show_relocation(self, rel): for r in rel: if (r.r_type == self.JMP_SLOT or r.r_type == self.GLOB_DAT) and r.r_sym != 0: rel_name = self.strtab[self.symtab[r.r_sym].st_name] - logging.debug('[+] rel name ' + str(rel_name)) + self.ql.log.debug('rel name ' + str(rel_name)) def show_dynsym_name(self, s, e): for symidx in range(s, e): rel_name = self.strtab[self.symtab[symidx].st_name] - logging.debug('[+] dynsym name ' + str(rel_name)) + self.ql.log.debug('dynsym name ' + str(rel_name)) def _hook_int(self, ql, intno): idx = (self.ql.reg.arch_pc - self.hook_mem) // 0x10 diff --git a/qiling/os/linux/futex.py b/qiling/os/linux/futex.py index 4e19f14f2..1f17b9b23 100644 --- a/qiling/os/linux/futex.py +++ b/qiling/os/linux/futex.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import gevent + -from logging import getLevelName from gevent.event import Event from queue import Queue -import gevent -import logging class QlLinuxFutexManagement: @@ -22,11 +22,11 @@ def wait_list(self): def futex_wait(self, ql, uaddr, t, val, bitset=FUTEX_BITSET_MATCH_ANY): def _sched_wait_event(cur_thread): - logging.debug(f"[Thread {cur_thread.get_id()}] Wait for notifications.") + ql.log.debug(f"Wait for notifications.") event.wait() uaddr_value = ql.unpack32(ql.mem.read(uaddr, 4)) if uaddr_value != val: - logging.debug(f"uaddr: {hex(uaddr_value)} != {hex(val)}") + ql.log.debug(f"uaddr: {hex(uaddr_value)} != {hex(val)}") return -1 ql.emu_stop() if uaddr not in self.wait_list.keys(): @@ -39,7 +39,7 @@ def _sched_wait_event(cur_thread): def get_futex_wake_list(self, ql, addr, number, bitset=FUTEX_BITSET_MATCH_ANY): wakes = [] if addr not in self.wait_list or number == 0: - logging.debug(f"No thread at {hex(addr)}") + ql.log.debug(f"No thread at {hex(addr)}") return wakes thread_queue = self.wait_list[addr] if thread_queue.qsize() < number: @@ -55,7 +55,7 @@ def get_futex_wake_list(self, ql, addr, number, bitset=FUTEX_BITSET_MATCH_ANY): def futex_wake(self, ql, uaddr, t, number, bitset=FUTEX_BITSET_MATCH_ANY): def _sched_set_event(cur_thread): for t, e in wakes: - logging.debug(f"[Thread {cur_thread.get_id()}] Notify [Thread {t.get_id()}].") + ql.log.debug(f"Notify [Thread {t.get_id()}.") e.set() # Give up control. gevent.sleep(0) diff --git a/qiling/os/linux/kernel_api/__init__.py b/qiling/os/linux/kernel_api/__init__.py index 72cfb9c22..b61104202 100644 --- a/qiling/os/linux/kernel_api/__init__.py +++ b/qiling/os/linux/kernel_api/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .kernel_api import * diff --git a/qiling/os/linux/kernel_api/hook.py b/qiling/os/linux/kernel_api/hook.py index bbc13c231..b9552de74 100644 --- a/qiling/os/linux/kernel_api/hook.py +++ b/qiling/os/linux/kernel_api/hook.py @@ -1,17 +1,20 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import types from qiling.os.linux.kernel_api import * -import logging +from qiling.exception import * + # hook Linux kernel API def hook_kernel_api(ql, address, size): # call kernel api # print("check hook_kernel_api: %x" %(address)) - if address in ql.import_symbols: - api_name = ql.import_symbols[address] + if address in ql.loader.import_symbols: + api_name = ql.loader.import_symbols[address] # print("OK, found hook for %s" %api_name) api_func = None @@ -29,11 +32,11 @@ def hook_kernel_api(ql, address, size): try: api_func(ql, address, {}) except Exception: - logging.exception("") - logging.debug("[!] %s Exception Found" % api_name) - raise QlErrorSyscallError("[!] Linux kernel API Implementation Error") + ql.log.exception("") + ql.log.debug("%s Exception Found" % api_name) + raise QlErrorSyscallError("Linux kernel API Implementation Error") else: - logging.warning("[!] %s is not implemented\n" % api_name) + ql.log.warning("%s is not implemented\n" % api_name) if ql.debug_stop: - raise QlErrorSyscallNotFound("[!] Linux kernel API Implementation Not Found") + raise QlErrorSyscallNotFound("Linux kernel API Implementation Not Found") diff --git a/qiling/os/linux/kernel_api/kernel_api.py b/qiling/os/linux/kernel_api/kernel_api.py index d8e158db2..e480d34cf 100644 --- a/qiling/os/linux/kernel_api/kernel_api.py +++ b/qiling/os/linux/kernel_api/kernel_api.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.linux.fncc import * diff --git a/qiling/os/linux/linux.py b/qiling/os/linux/linux.py index cdab48fe9..aa3bcacf3 100644 --- a/qiling/os/linux/linux.py +++ b/qiling/os/linux/linux.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.arch.x86 import * - from qiling.os.posix.posix import QlOsPosix from .const import * from .utils import * @@ -65,22 +64,28 @@ def load(self): ql_x86_register_cs(self) ql_x86_register_ds_ss_es(self) self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL) + # Keep test for _cc + #self.ql.hook_insn(hook_posix_api, UC_X86_INS_SYSCALL) self.thread_class = QlLinuxX8664Thread def hook_syscall(self, int= None, intno= None): - return self.load_syscall(intno) + return self.load_syscall() + def add_function_hook(self, fn, cb, userdata = None): self.function_hook_tmp.append((fn, cb, userdata)) - + + def register_function_after_load(self, function): if function not in self.function_after_load_list: self.function_after_load_list.append(function) + def run_function_after_load(self): for f in self.function_after_load_list: f() + def run(self): for function, callback, userdata in self.ql.os.function_hook_tmp: self.ql.os.function_hook.add_function_hook(function, callback, userdata) @@ -89,45 +94,13 @@ def run(self): self.exit_point = self.ql.exit_point try: - if self.ql.shellcoder: - self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.shellcoder)), self.ql.timeout, self.ql.count) + if self.ql.code: + self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.code)), self.ql.timeout, self.ql.count) else: if self.ql.multithread == True: # start multithreading thread_management = QlLinuxThreadManagement(self.ql) self.ql.os.thread_management = thread_management - main_thread = self.thread_class.spawn(self.ql) - thread_management.main_thread = main_thread - thread_management.cur_thread = main_thread - # Main thread has been created and we have to clear all buffered logging if any. - # This only happens with ql.split_log = True. - try: - msg_before_main_thread = self.ql._msg_before_main_thread - for lvl, msg in msg_before_main_thread: - main_thread.log_file_fd.log(lvl, msg) - except AttributeError: - pass - main_thread.save() - main_thread.set_start_address(self.ql.loader.entry_point) - - - # enable lib patch - if self.ql.loader.elf_entry != self.ql.loader.entry_point: - main_thread.exit_point = self.ql.loader.elf_entry - thread_management.run() - if main_thread.ql.arch.get_pc() != self.ql.loader.elf_entry: - raise QlErrorExecutionStop('Dynamic library .init() failed!') - self.ql.enable_lib_patch() - self.run_function_after_load() - self.ql.loader.skip_exit_check = False - self.ql.write_exit_trap() - - main_thread.set_start_address(self.ql.loader.elf_entry) - main_thread.exit_point = self.exit_point - - #thread_management.clean_world() - #thread_management.set_main_thread(main_thread) - thread_management.run() else: @@ -146,8 +119,8 @@ def run(self): except UcError: # TODO: this is bad We need a better approach for this - if self.ql.output != QL_OUTPUT.DEBUG: - return + #if self.ql.output != QL_OUTPUT.DEBUG: + # return self.emu_error() raise diff --git a/qiling/os/linux/map_syscall.py b/qiling/os/linux/map_syscall.py index 7e6e7ef67..90a9206cb 100644 --- a/qiling/os/linux/map_syscall.py +++ b/qiling/os/linux/map_syscall.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # Table from: https://github.com/zeropointdynamics/zelos/blob/master/src/zelos/ext/platforms/linux/syscalls/syscalls_table.py # cols = ("arm64", "arm", "x8664", "x32", "x86", "mips", "powerpc", "ia64") diff --git a/qiling/os/linux/syscall.py b/qiling/os/linux/syscall.py index 9bd21a65b..78010912b 100644 --- a/qiling/os/linux/syscall.py +++ b/qiling/os/linux/syscall.py @@ -1,43 +1,33 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -# -# import struct -# import sys -# import os -# import string -# import resource -# import socket -# import time -# import io -# import select +# from unicorn.arm_const import * from unicorn.x86_const import * from unicorn.mips_const import * + from qiling.arch.x86_const import * from qiling.const import * -import logging + def ql_syscall_set_thread_area(ql, u_info_addr, *args, **kw): if ql.archtype == QL_ARCH.X86: GDT_ENTRY_TLS_MIN = 12 GDT_ENTRY_TLS_MAX = 14 - logging.info("set_thread_area(u_info_addr= 0x%x)" % u_info_addr) - u_info = ql.mem.read(u_info_addr, 4 * 4) index = ql.unpack32s(u_info[0 : 4]) base = ql.unpack32(u_info[4 : 8]) limit = ql.unpack32(u_info[8 : 12]) - logging.debug("[+] set_thread_area base : 0x%x limit is : 0x%x" % (base, limit)) + ql.log.debug("set_thread_area base : 0x%x limit is : 0x%x" % (base, limit)) if index == -1: index = ql.os.gdtm.get_free_idx(12) if index == -1 or index < 12 or index > 14: + ql.log.warning(f"Wrong index {index} from address {hex(u_info_addr)}") return -1 else: ql.os.gdtm.register_gdt_segment(index, base, limit, QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_3) @@ -50,7 +40,9 @@ def ql_syscall_set_thread_area(ql, u_info_addr, *args, **kw): ql.reg.cp0_userlocal = u_info_addr ql.reg.v0 = 0 ql.reg.a3 = 0 - logging.info ("set_thread_area(0x%x)" % u_info_addr) + ql.log.debug ("set_thread_area(0x%x)" % u_info_addr) + + return 0 def ql_syscall_set_tls(ql, address, *args, **kw): @@ -58,4 +50,4 @@ def ql_syscall_set_tls(ql, address, *args, **kw): ql.reg.c13_c0_3 = address ql.mem.write(ql.arch.arm_get_tls_addr + 12, ql.pack32(address)) ql.reg.r0 = address - logging.info("settls(0x%x)" % address) + ql.log.debug("settls(0x%x)" % address) diff --git a/qiling/os/linux/syscall_nums.py b/qiling/os/linux/syscall_nums.py index 2a2196dcc..52abeb958 100644 --- a/qiling/os/linux/syscall_nums.py +++ b/qiling/os/linux/syscall_nums.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # Linux syscall numbers diff --git a/qiling/os/linux/thread.py b/qiling/os/linux/thread.py index beb4a714a..5681519e6 100644 --- a/qiling/os/linux/thread.py +++ b/qiling/os/linux/thread.py @@ -1,15 +1,15 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import os, time, logging -from pathlib import Path +import gevent, os, time -from unicorn.unicorn import UcError -import gevent +from typing import Callable +from abc import ABC, abstractmethod +from pathlib import Path from gevent import Greenlet - +from unicorn.unicorn import UcError from unicorn.mips_const import * from unicorn.arm_const import * @@ -18,13 +18,11 @@ from qiling.arch.x86_const import * from qiling.const import * from qiling.os.const import * - - -from abc import ABC, abstractmethod - +from qiling.exception import * LINUX_THREAD_ID = 2000 + def new_thread_id(): global LINUX_THREAD_ID old = LINUX_THREAD_ID @@ -32,15 +30,15 @@ def new_thread_id(): return old class QlLinuxThread(QlThread): -# static member for generate unique thread id. - - - def __init__(self, ql, start_address = 0, context = None, set_child_tid_addr = None): + def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): super(QlLinuxThread, self).__init__(ql) - self._thread_id = new_thread_id() + if not thread_id: + self._thread_id = new_thread_id() + else: + self._thread_id = thread_id self._saved_context = context self._ql = ql - self._exit_point = self.ql.os.exit_point + self._exit_point = exit_point self._start_address = start_address self._status = THREAD_STATUS_RUNNING self._return_val = 0 @@ -48,17 +46,8 @@ def __init__(self, ql, start_address = 0, context = None, set_child_tid_addr = N self._log_file_fd = None self._sched_cb = None - if self.ql.log_split: - _logger = logging.getLogger(f"thread{self.id}") - _logger.propagate = False - if self.ql.log_dir is not None and self.ql.log_dir != "": - handler = logging.FileHandler(Path(self.ql.log_dir) / f"{self.ql.targetname + self.ql.append}_{self.id}.qlog") - handler.setFormatter(logging.Formatter(FMT_STR)) - _logger.addHandler(handler) - - self._log_file_fd = _logger - else: - self._log_file_fd = logging.getLogger() + # Compatibility + self._log_file_fd = ql.log # For each thread, the kernel maintains two attributes (addresses) # called set_child_tid and clear_child_tid. These two attributes @@ -209,7 +198,7 @@ def robust_list_head_len(self, l): self._robust_list_head_len = l @property - def sched_cb(self): + def sched_cb(self) -> Callable: return self._sched_cb @sched_cb.setter @@ -238,37 +227,50 @@ def _run(self): # In this context, we do: # - Call gevent functions to switch threads. # - Forward blocking syscalls to gevent. + self.ql.reg.arch_pc = self.start_address + if not self._saved_context: + self.save() + while self.status != THREAD_STATUS_TERMINATED: + # Rewrite our status and the current thread. + self.status = THREAD_STATUS_RUNNING + self.ql.os.thread_management.cur_thread = self + # Restore the context of the currently executing thread and set tls self.restore() + # Sanity check + if self.ql.reg.arch_pc == self.exit_point: + self.ql.log.warning(f"Nothing to do but still get scheduled!") + # Run and log the run event - self.start_address = self.ql.arch.get_pc() + start_address = self.ql.arch.get_pc() # For arm thumb. self.sched_cb = QlLinuxThread._default_sched_cb - logging.debug(f"[Thread {self.get_id()}] scheduled.") - self.status = THREAD_STATUS_RUNNING - self.ql.os.thread_management.cur_thread = self + self.ql.log.debug(f"Scheduled from {hex(start_address)}.") try: # Known issue for timeout: https://github.com/unicorn-engine/unicorn/issues/1355 - self.ql.emu_start(self.start_address, self.exit_point, count=3000) - except UcError: - print(self.ql._hook) + self.ql.emu_start(start_address, self.exit_point, count=30000) + except UcError as e: self.ql.os.emu_error() - raise - if self.ql.arch.get_pc() == self.exit_point: - self.stop() - break - + self.ql.log.exception("") + raise e + self.ql.log.debug(f"Suspended at {hex(self.ql.reg.arch_pc)}") self.save() + # Note that this callback may be set by UC callbacks. # Some thought on this design: # 1. Never give up control during a UC callback. # 2. emu_stop only sends a signal to unicorn which won't stop it immediately. # 3. According to 1, never call gevent functions in UC callbacks. - logging.debug(f"[Thread {self.get_id()}] calls sched_cb: {self.sched_cb}") + self.ql.log.debug(f"Call sched_cb: {self.sched_cb}") self.sched_cb(self) + if self.status == THREAD_STATUS_TERMINATED or self.ql.reg.arch_pc == self.exit_point: + break + + self._on_stop() + # Depreciated. def get_id(self): return self.id @@ -282,13 +284,29 @@ def restore(self): pass @abstractmethod - def clone_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr): pass + + @abstractmethod + def clone(self): + # This is a workaround to implement our thread based on gevent greenlet. + # Core idea: + # A gevent greenlet can't re-run if it has finished _run method but our framework requires threads to be resumed anytime. Therefore, a workaround is to + # use multiple greenlets to represent a single qiling thread. + # + # Of course we can make the greenlet run forever and wait for notifications to resume but that would make the design much more complicated. + # + # Caveat: + # Don't use thread id to identify the thread object. + new_thread = self.ql.os.thread_class.spawn(self._ql, self._start_address, self._exit_point, self._saved_context, set_child_tid_addr = None, thread_id = self._thread_id) + new_thread._current_path = self._current_path + new_thread._return_val = self._return_val + new_thread._robust_list_head_len = self._robust_list_head_len + new_thread._robust_list_head_ptr = self._robust_list_head_ptr + new_thread._set_child_tid_address = self._set_child_tid_address + new_thread._clear_child_tid_address = self._clear_child_tid_address + return new_thread - def suspend(self): - self.save() - - # TODO: Rename def save_context(self): self.saved_context = self.ql.arch.context_save() @@ -318,19 +336,18 @@ def _on_stop(self): # Source: Linux Man Page if self.clear_child_tid_address is not None: - logging.debug(f"[Thread {self.get_id()}] Perform CLONE_CHILD_CLEARTID at {hex(self.clear_child_tid_address)}") + self.ql.log.debug(f"Perform CLONE_CHILD_CLEARTID at {hex(self.clear_child_tid_address)}") self.ql.mem.write(self.clear_child_tid_address, self.ql.pack32(0)) wakes = self.ql.os.futexm.get_futex_wake_list(self.ql, self.clear_child_tid_address, 1) self.clear_child_tid_address = None # When the thread is to stop, we don't have chance for next sched_cb, so # we notify the thread directly. for t, e in wakes: - logging.debug(f"[Thread {self.get_id()}] Notify [Thread {t.get_id()}].") + self.ql.log.debug(f"Notify {t}.") e.set() # This function should called outside unicorn callback. def stop(self): - self._on_stop() self.status = THREAD_STATUS_TERMINATED def is_stop(self): @@ -349,11 +366,11 @@ def update_global_thread_id(self): class QlLinuxX86Thread(QlLinuxThread): """docstring for X86Thread""" - def __init__(self, ql, start_address = 0, context = None, set_child_tid_addr = None): - super(QlLinuxX86Thread, self).__init__(ql, start_address, context, set_child_tid_addr) + def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): + super(QlLinuxX86Thread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) self.tls = bytes(b'\x00' * (8 * 3)) - def clone_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr): old_tls = bytes(self.ql.os.gdtm.get_gdt_buf(12, 14 + 1)) self.ql.os.gdtm.set_gdt_buf(12, 14 + 1, self.tls) @@ -374,94 +391,135 @@ def clone_thread_tls(self, tls_addr): self.tls = bytes(self.ql.os.gdtm.get_gdt_buf(12, 14 + 1)) self.ql.os.gdtm.set_gdt_buf(12, 14 + 1, old_tls) + self.ql.log.debug(f"Set tls to index={hex(index)} base={hex(base)} limit={hex(limit)} fs={hex(self.ql.reg.fs)} gs={hex(self.ql.reg.gs)} gdt_buf={self.tls}") def save(self): self.save_context() self.tls = bytes(self.ql.os.gdtm.get_gdt_buf(12, 14 + 1)) + self.ql.log.debug(f"Saved context. fs={hex(self.ql.reg.fs)} gs={hex(self.ql.reg.gs)} gdt_buf={self.tls}") def restore(self): self.restore_context() self.ql.os.gdtm.set_gdt_buf(12, 14 + 1, self.tls) - self.ql.reg.gs = self.ql.reg.gs - self.ql.reg.fs = self.ql.reg.fs + self.ql.log.debug(f"Restored context. fs={hex(self.ql.reg.fs)} gs={hex(self.ql.reg.gs)} gdt_buf={self.tls}") + + def clone(self): + new_thread = super(QlLinuxX86Thread, self).clone() + new_thread.tls = self.tls + return new_thread class QlLinuxX8664Thread(QlLinuxThread): """docstring for X8664Thread""" - def __init__(self, ql, start_address = 0, context = None, set_child_tid_addr = None): - super(QlLinuxX8664Thread, self).__init__(ql,start_address, context, set_child_tid_addr) + def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): + super(QlLinuxX8664Thread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) self.tls = 0 - def clone_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr): self.tls = tls_addr + self.ql.reg.msr(FSMSR, self.tls) + self.ql.log.debug(f"Set fsbase to {hex(tls_addr)} for {str(self)}") + # Some notes: + # - https://wiki.osdev.org/SWAPGS + # - https://stackoverflow.com/questions/11497563/detail-about-msr-gs-base-in-linux-x86-64 def save(self): self.save_context() self.tls = self.ql.reg.msr(FSMSR) + self.ql.log.debug(f"Saved context: fs={hex(self.ql.reg.fsbase)} tls={hex(self.tls)}") def restore(self): self.restore_context() - self.ql.reg.msr(FSMSR, self.tls) + self.set_thread_tls(self.tls) + self.ql.log.debug(f"Restored context: fs={hex(self.ql.reg.fsbase)} tls={hex(self.tls)}") + + def clone(self): + new_thread = super(QlLinuxX8664Thread, self).clone() + new_thread.tls = self.tls + return new_thread class QlLinuxMIPS32Thread(QlLinuxThread): """docstring for QlLinuxMIPS32Thread""" - def __init__(self, ql, start_address = 0, context = None, set_child_tid_addr = None): - super(QlLinuxMIPS32Thread, self).__init__(ql, start_address, context, set_child_tid_addr) + def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): + super(QlLinuxMIPS32Thread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) self.tls = 0 - def clone_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr): self.tls = tls_addr - + CONFIG3_ULR = (1 << 13) + self.ql.reg.cp0_config3 = CONFIG3_ULR + self.ql.reg.cp0_userlocal = self.tls + self.ql.log.debug(f"Set cp0 to {hex(self.ql.reg.cp0_userlocal)}") def save(self): self.save_context() - self.tls = self.ql.reg.cp0_userlocal - + self.tls = self.ql.reg.cp0_userlocal + self.ql.log.debug(f"Saved context. cp0={hex(self.ql.reg.cp0_userlocal)}") def restore(self): self.restore_context() - CONFIG3_ULR = (1 << 13) - self.ql.reg.cp0_config3 = CONFIG3_ULR - self.ql.reg.cp0_userlocal = self.tls + self.set_thread_tls(self.tls) + self.ql.log.debug(f"Restored context. cp0={hex(self.ql.reg.cp0_userlocal)}") + def clone(self): + new_thread = super(QlLinuxMIPS32Thread, self).clone() + new_thread.tls = self.tls + return new_thread class QlLinuxARMThread(QlLinuxThread): """docstring for QlLinuxARMThread""" - def __init__(self, ql, start_address = 0, context = None, set_child_tid_addr = None): - super(QlLinuxARMThread, self).__init__(ql, start_address, context, set_child_tid_addr) + def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): + super(QlLinuxARMThread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) self.tls = 0 - def clone_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr): self.tls = tls_addr - + self.ql.reg.c13_c0_3 = self.tls + self.ql.log.debug(f"Set c13_c0_3 to {hex(self.ql.reg.c13_c0_3)}") def save(self): self.save_context() self.tls = self.ql.reg.c13_c0_3 + self.ql.log.debug(f"Saved context. c13_c0_3={hex(self.ql.reg.c13_c0_3)}") def restore(self): self.restore_context() - self.ql.reg.c13_c0_3 = self.tls + self.set_thread_tls(self.tls) + self.ql.log.debug(f"Restored context. c13_c0_3={hex(self.ql.reg.c13_c0_3)}") + + def clone(self): + new_thread = super(QlLinuxARMThread, self).clone() + new_thread.tls = self.tls + return new_thread class QlLinuxARM64Thread(QlLinuxThread): """docstring for QlLinuxARM64Thread""" - def __init__(self, ql, start_address = 0, context = None, set_child_tid_addr = None): - super(QlLinuxARM64Thread, self).__init__(ql, start_address, context, set_child_tid_addr) + def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): + super(QlLinuxARM64Thread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) self.tls = 0 - def clone_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr): self.tls = tls_addr + self.ql.reg.tpidr_el0 = self.tls + self.ql.log.debug(f"Set tpidr_el0 to {hex(self.ql.reg.tpidr_el0)}") def save(self): self.save_context() self.tls = self.ql.reg.tpidr_el0 + self.ql.log.debug(f"Saved context. tpidr_el0={hex(self.ql.reg.tpidr_el0)}") def restore(self): self.restore_context() - self.ql.reg.tpidr_el0 = self.tls + self.set_thread_tls(self.tls) + self.ql.log.debug(f"Restored context. tpidr_el0={hex(self.ql.reg.tpidr_el0)}") + + def clone(self): + new_thread = super(QlLinuxARM64Thread, self).clone() + new_thread.tls = self.tls + return new_thread class QlLinuxThreadManagement: def __init__(self, ql): @@ -495,16 +553,49 @@ def stop_thread(self, t): # Exit the world. if t == self.main_thread: self.stop() + + def _clear_queued_msg(self): + try: + msg_before_main_thread = self.ql._msg_before_main_thread + for lvl, msg in msg_before_main_thread: + self.main_thread.log_file_fd.log(lvl, msg) + except AttributeError: + pass + def _prepare_lib_patch(self): + if self.ql.loader.elf_entry != self.ql.loader.entry_point: + self.main_thread = self.ql.os.thread_class.spawn(self.ql, self.ql.loader.entry_point, self.ql.loader.elf_entry) + self.cur_thread = self.main_thread + self._clear_queued_msg() + gevent.joinall([self.main_thread], raise_error=True) + if self.ql.reg.arch_pc != self.ql.loader.elf_entry: + self.ql.log.error(f"{self.cur_thread} Expect {hex(self.ql.loader.elf_entry)} but get {hex(self.ql.reg.arch_pc)} when running loader.") + raise QlErrorExecutionStop('Dynamic library .init() failed!') + self.ql.enable_lib_patch() + self.ql.os.run_function_after_load() + self.ql.loader.skip_exit_check = False + self.ql.write_exit_trap() + return self.main_thread + return None + # Stop the world, urge all threads to stop immediately. def stop(self): - logging.debug("[Thread Manager] Stop the world.") + self.ql.log.debug("[Thread Manager] Stop the world.") self.ql.emu_stop() for t in self.threads: gevent.kill(t) def run(self): + previous_thread = self._prepare_lib_patch() + if previous_thread is None: + self.main_thread = self.ql.os.thread_class.spawn(self.ql, self.ql.loader.elf_entry, self.ql.os.exit_point) + else: + self.main_thread = previous_thread.clone() + self.main_thread.start_address = self.ql.loader.elf_entry + self.main_thread.exit_point = self.ql.os.exit_point + self.cur_thread = self.main_thread + self._clear_queued_msg() # If we get exceptions from gevent here, it means a critical bug related to multithread. # Please fire an issue if you encounter an exception from gevent. - gevent.joinall([self.main_thread]) + gevent.joinall([self.main_thread], raise_error=True) diff --git a/qiling/os/linux/utils.py b/qiling/os/linux/utils.py index f7f2f14a1..a874a3350 100644 --- a/qiling/os/linux/utils.py +++ b/qiling/os/linux/utils.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + + -import logging from .const import * from qiling.const import * @@ -21,5 +22,5 @@ def ql_arm_init_get_tls(ql): # sc = ql.os.lsbmsb_convert(ql, sc) ql.mem.write(ql.arch.arm_get_tls_addr, sc) - logging.debug("[+] Set init_kernel_get_tls") + ql.log.debug("Set init_kernel_get_tls") \ No newline at end of file diff --git a/qiling/os/macos/__init__.py b/qiling/os/macos/__init__.py index 26c746542..16756da5d 100644 --- a/qiling/os/macos/__init__.py +++ b/qiling/os/macos/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # virtual addr : 0x00000000000 - 0x7FFFFFFFFFF # 64 bit case no aslr: diff --git a/qiling/os/macos/const.py b/qiling/os/macos/const.py index 15c51612a..46c5b9a40 100644 --- a/qiling/os/macos/const.py +++ b/qiling/os/macos/const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # basic values PAGE_SIZE = 0x1000 diff --git a/qiling/os/macos/events/macos.py b/qiling/os/macos/events/macos.py index dfec29792..3eac7729d 100644 --- a/qiling/os/macos/events/macos.py +++ b/qiling/os/macos/events/macos.py @@ -1,15 +1,20 @@ -import ctypes -import socket -import struct -import logging + +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +import ctypes, socket, struct + + from functools import wraps +from unicorn.x86_const import * +from unicorn import UcError, UC_ERR_READ_UNMAPPED, UC_ERR_FETCH_UNMAPPED from qiling.os.macos.structs import * from qiling.os.macos.utils import gen_stub_code from .macos_structs import * -from unicorn.x86_const import * -from unicorn import UcError, UC_ERR_READ_UNMAPPED, UC_ERR_FETCH_UNMAPPED class QlMacOSEv: def __init__(self, ql, ev_type, ev_name, ev_obj, ev_obj_idx=-1, protocol=None): @@ -29,14 +34,14 @@ def set_params(self, params): self.params = params[:] def dump(self): - logging.info("[*] Dumping object: %s with type %d" % (self.name, self.type.value)) + self.ql.log.info("[*] Dumping object: %s with type %d" % (self.name, self.type.value)) for field in self.event._fields_: if isinstance(getattr(self.event, field[0]), POINTER64): - logging.info("%s: 0x%x" % (field[0], getattr(self.event, field[0]).value)) + self.ql.log.info("%s: 0x%x" % (field[0], getattr(self.event, field[0]).value)) elif isinstance(getattr(self.event, field[0]), int): - logging.info("%s: %d" % (field[0], getattr(self.event, field[0]))) + self.ql.log.info("%s: %d" % (field[0], getattr(self.event, field[0]))) elif isinstance(getattr(self.event, field[0]), bytes): - logging.info("%s: %s" % (field[0], getattr(self.event, field[0]).decode())) + self.ql.log.info("%s: %s" % (field[0], getattr(self.event, field[0]).decode())) def init_ev_ctx(f): @wraps(f) @@ -109,7 +114,7 @@ def __init__(self, ql): def add_process(self, pid, name): for p in self.my_procs: if p.p_pid == pid: - logging.info("[!] Duplicated process") + self.ql.log.info("Duplicated process") return cur_proc_addr = self.ql.os.heap.alloc(ctypes.sizeof(proc_t)) @@ -203,7 +208,7 @@ def get_events_by_name(self, ev_name, keyword=b""): def emit(self, ev_name, ev_type, params): found = self.get_event_by_name_and_type(ev_name, ev_type) if found is None: - logging.info("[!] No callbacks found for (%s, %s)" % (ev_name, ev_type)) + self.ql.log.info("No callbacks found for (%s, %s)" % (ev_name, ev_type)) return found.set_params(params) diff --git a/qiling/os/macos/events/macos_policy.py b/qiling/os/macos/events/macos_policy.py index 5cba45600..1ef0fcb35 100644 --- a/qiling/os/macos/events/macos_policy.py +++ b/qiling/os/macos/events/macos_policy.py @@ -1,10 +1,15 @@ -import enum -import ctypes -import logging -from .macos_structs import label_t, ucred_t, POINTER64, fileglob_t, vnode_t -#from .macos import * +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +import ctypes, enum + from functools import wraps +from .macos_structs import label_t, ucred_t, POINTER64, fileglob_t, vnode_t + + def init_ctx(f): @wraps(f) def wrapper(self, *args, **kw): @@ -52,7 +57,7 @@ def __init__(self, ql, manager): @init_ctx def mpo_file_check_mmap(self, prot, flags, file_pos): if self.manager.vnode is None or self.manager.cred is None: - logging.info("[!] Invalid vnode or credential") + ql.log.info("Invalid vnode or credential") return fg_addr = self.ql.os.heap.alloc(ctypes.sizeof(fileglob_t)) fg = fileglob_t(self.ql, fg_addr) diff --git a/qiling/os/macos/events/macos_structs.py b/qiling/os/macos/events/macos_structs.py index 91615b076..718d3c2fd 100644 --- a/qiling/os/macos/events/macos_structs.py +++ b/qiling/os/macos/events/macos_structs.py @@ -1,8 +1,13 @@ -import enum -import ctypes -import logging +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +import ctypes, enum + from qiling.os.macos.structs import POINTER64 + base_event_normal = 0 class AutoNumberNormalEvent(enum.Enum): def __new__(cls): @@ -1002,14 +1007,14 @@ def loadFromMem(self): return newObj def dump(self): - logging.info("[*] Dumping object: %s" % (self.sf_name)) + self.ql.log.info("[*] Dumping object: %s" % (self.sf_name)) for field in self._fields_: if isinstance(getattr(self, field[0]), POINTER64): - logging.info("%s: 0x%x" % (field[0], getattr(self, field[0]).value)) + self.ql.log.info("%s: 0x%x" % (field[0], getattr(self, field[0]).value)) elif isinstance(getattr(self, field[0]), int): - logging.info("%s: %d" % (field[0], getattr(self, field[0]))) + self.ql.log.info("%s: %d" % (field[0], getattr(self, field[0]))) elif isinstance(getattr(self, field[0]), bytes): - logging.info("%s: %s" % (field[0], getattr(self, field[0]).decode())) + self.ql.log.info("%s: %s" % (field[0], getattr(self, field[0]).decode())) # struct sockaddr_in { # __uint8_t sin_len; @@ -1119,11 +1124,11 @@ def loadFromMem(self): def dump(self): for field in self._fields_: if isinstance(getattr(self, field[0]), POINTER64): - logging.info("%s: 0x%x" % (field[0], getattr(self, field[0]).value)) + self.ql.log.info("%s: 0x%x" % (field[0], getattr(self, field[0]).value)) elif isinstance(getattr(self, field[0]), int): - logging.info("%s: %d" % (field[0], getattr(self, field[0]))) + self.ql.log.info("%s: %d" % (field[0], getattr(self, field[0]))) elif isinstance(getattr(self, field[0]), bytes): - logging.info("%s: %s" % (field[0], getattr(self, field[0]).decode())) + self.ql.log.info("%s: %s" % (field[0], getattr(self, field[0]).decode())) # struct ucred { # TAILQ_ENTRY(ucred) cr_link; /* never modify this without KAUTH_CRED_HASH_LOCK */ diff --git a/qiling/os/macos/fncc.py b/qiling/os/macos/fncc.py index 0001c6bc1..029f69873 100644 --- a/qiling/os/macos/fncc.py +++ b/qiling/os/macos/fncc.py @@ -1,11 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import struct +# + from unicorn.x86_const import * -from qiling.os.utils import * -from qiling.os.macos.utils import * DWORD = 1 UINT = 1 @@ -24,112 +22,11 @@ BOOLEAN = 8 BOOL = 8 - -def _get_param_by_index(ql, index): - return get_params_by_index(ql, index) - - -def get_params_by_index(ql, index): - reg_list = [UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_RCX, UC_X86_REG_R8, UC_X86_REG_R9] - if index < 6: - return ql.uc.reg_read(reg_list[index]) - - index -= 6 - # skip ret_addr - return ql.stack_read((index + 1) * 8) - - -def _x8664_get_args(ql, number): - reg_list = [UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_RCX, UC_X86_REG_R8, UC_X86_REG_R9] - arg_list = [] - reg_num = number - if reg_num > 6: - reg_num = 6 - - number -= reg_num - # get args in registers first - for i in reg_list[:reg_num]: - arg_list.append(ql.uc.reg_read(i)) - - # the rest args are from stack - for i in range(number): - # skip ret_addr and 32 byte home space - arg_list.append(ql.stack_read((i + 7) * 8)) - - if reg_num == 1: - return arg_list[0] - else: - return arg_list - - -def set_function_params(ql, in_params, out_params, index=0): - for each in in_params: - if in_params[each] in (DWORD, POINTER, ULONGLONG): - out_params[each] = _get_param_by_index(ql, index) - elif in_params[each] == STRING: - ptr = _get_param_by_index(ql, index) - if ptr == 0: - out_params[each] = 0 - else: - out_params[each] = macho_read_string(ql, ptr, 0x1000) - elif in_params[each] in (BOOLEAN, BOOL): - ptr = _get_param_by_index(ql, index) - if ptr == 1: - out_params[each] = "True" - else: - out_params[each] = "False" - - index += 1 - - return index - - -def get_function_param(ql, number): - return _x8664_get_args(ql, number) - - -def set_return_value(ql, ret): - ql.uc.reg_write(UC_X86_REG_RAX, ret) - - -def get_return_value(ql): - return ql.uc.reg_read(UC_X86_REG_RAX) - - -def __x86_cc(ql, passthru, param_num, params, func, args, kwargs): - # read params - if params is not None: - param_num = set_function_params(ql, params, args[2]) - - # call function - result = func(*args, **kwargs) - - # set return value - if result is not None: - set_return_value(ql, result) - - # print - print_function(ql, passthru, args[1], func.__name__, args[2], result) - return result, param_num - - -def x8664_fastcall(ql, passthru, param_num, params, func, args, kwargs): - result, param_num = __x86_cc(ql, passthru, param_num, params, func, args, kwargs) - - if not passthru and ql.os.RUN is True: - try: - ql.reg.arch_pc = ql.stack_pop() - except UcError: - raise - - return result - - def macos_kernel_api(param_num=None, params=None, passthru=False): def decorator(func): def wrapper(*args, **kwargs): ql = args[0] - return x8664_fastcall(ql, passthru, param_num, params, func, args, kwargs) + return ql.os.x8664_fastcall(param_num, params, func, args, kwargs, passthru) return wrapper return decorator diff --git a/qiling/os/macos/kernel_api/__init__.py b/qiling/os/macos/kernel_api/__init__.py index 72cfb9c22..b61104202 100644 --- a/qiling/os/macos/kernel_api/__init__.py +++ b/qiling/os/macos/kernel_api/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .kernel_api import * diff --git a/qiling/os/macos/kernel_api/hook.py b/qiling/os/macos/kernel_api/hook.py index e0df738ad..444ea524c 100644 --- a/qiling/os/macos/kernel_api/hook.py +++ b/qiling/os/macos/kernel_api/hook.py @@ -1,17 +1,19 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import types -import logging from qiling.os.macos.kernel_api import * -from unicorn import * +from qiling.exception import * + # hook MacOS kernel API def hook_kernel_api(ql, address, size): # call kernel api - if address in ql.import_symbols: - api_name = ql.import_symbols[address].decode() + if address in ql.loader.import_symbols: + api_name = ql.loader.import_symbols[address].decode() # print("OK, found hook for %s at 0x%x" % (api_name, address)) api_func = None @@ -30,12 +32,12 @@ def hook_kernel_api(ql, address, size): except UcError: raise except Exception: - logging.exception("") - logging.debug("[!] %s Exception Found" % api_name) - raise QlErrorSyscallError("[!] MacOS kernel API Implementation Error") + ql.log.exception("") + ql.log.debug("%s Exception Found" % api_name) + raise QlErrorSyscallError("MacOS kernel API Implementation Error") else: - logging.info("[!] %s is not implemented\n" % api_name) + ql.log.info("%s is not implemented\n" % api_name) if ql.debug_stop: - raise QlErrorSyscallNotFound("[!] MacOS kernel API Implementation Not Found") + raise QlErrorSyscallNotFound("MacOS kernel API Implementation Not Found") diff --git a/qiling/os/macos/kernel_api/kernel_api.py b/qiling/os/macos/kernel_api/kernel_api.py index 543d00aa4..5f2f33d79 100644 --- a/qiling/os/macos/kernel_api/kernel_api.py +++ b/qiling/os/macos/kernel_api/kernel_api.py @@ -1,14 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import ctypes, os, struct -import ctypes -import os -import struct -import logging from time import time + from qiling.os.macos.fncc import * from qiling.os.macos.structs import * from qiling.os.macos.utils import gen_stub_code @@ -951,7 +950,7 @@ def hook__sock_getsockname(ql, address, params): "cmd": STRING, }) def hook__KUNCExecute(ql, address, params): - # logging.debug("[+] Starting userspace process: %s" % params["cmd"]) + # ql.log.debug("Starting userspace process: %s" % params["cmd"]) return 0 ######################## Custom events / callbacks ######################## @@ -995,7 +994,7 @@ def hook__sysctl_register_oid(ql, address, params): true_name = oid_parent.lstrip(b"_") + b"." + oid_name.encode() + b"\x00" if oidp.oid_handler.value != 0: - logging.debug("[+] New sysctl callback has been registered: %s" % true_name) + ql.log.debug("New sysctl callback has been registered: %s" % true_name) ql.os.ev_manager.register(oidp.oid_handler.value, true_name, MacOSEventType.EV_SYSCTL, ev_obj=oidp, idx=0) return @@ -1017,7 +1016,7 @@ def hook__sysctl_unregister_oid(ql, address, params): break true_name = oid_parent.lstrip(b"_") + b"." + oid_name.encode() + b"\x00" - logging.debug("[+] A sysctl event has been deregistered: %s" % true_name) + ql.log.debug("A sysctl event has been deregistered: %s" % true_name) ql.os.ev_manager.deregister(true_name) @macos_kernel_api(params={ @@ -1038,7 +1037,7 @@ def hook__sysctl_root(ql, address, params): obj = event.event ql.os.ev_manager.emit(ev_name, ev_type, [obj.oid_arg1.value, obj.oid_arg2, params["req"]]) else: - logging.debug("[!] Event not found (%s, %s)" % (ev_name, ev_type.name)) + ql.log.debug("Event not found (%s, %s)" % (ev_name, ev_type.name)) return 0 @macos_kernel_api(params={ @@ -1076,7 +1075,7 @@ def hook__ctl_register(ql, address, params): ql.os.ev_manager.register(userctl.ctl_bind.value, ctl_name, MacOSEventType.EV_CTL_BIND_FUNC, ev_obj=userctl, idx=0) - logging.debug("[+] New ctl callbacks has been registered: %s (%d/%d/%d/%d/%d/%d/%d/%d)" % ( + ql.log.debug("New ctl callbacks has been registered: %s (%d/%d/%d/%d/%d/%d/%d/%d)" % ( ctl_name, (flag & (1 << 0)) != 0, (flag & (1 << 1)) != 0, @@ -1097,7 +1096,7 @@ def hook__ctl_deregister(ql, address, params): userctl = kern_ctl_reg_t(ql, params["userctl"]) userctl = userctl.loadFromMem() ctl_name = ql.mem.string(params["userctl"]).encode() - logging.debug("[+] A ctl event has been deregistered: %s" % ctl_name) + ql.log.debug("A ctl event has been deregistered: %s" % ctl_name) ql.os.ev_manager.deregister(ctl_name) @macos_kernel_api(params={ @@ -1171,7 +1170,7 @@ def hook__sflt_register(ql, address, params): flag |= 1 << 14 ql.os.ev_manager.register(sf.sf_ioctl.value, true_name, MacOSEventType.EV_SFLT_IOCTL, protocol=params["protocol"]) - logging.debug("[+] New sflt callbacks has been registered: %s (%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d)" % ( + ql.log.debug("New sflt callbacks has been registered: %s (%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d)" % ( true_name, (flag & (1 << 0)) != 0, (flag & (1 << 1)) != 0, @@ -1203,7 +1202,7 @@ def hook__sflt_unregister(ql, address, params): break ql.os.ev_manager.deregister(b"", keyword=handle) - logging.debug("[+] A sflt event has been deregistered: %s" % (handle)) + ql.log.debug("A sflt event has been deregistered: %s" % (handle)) return 0 @macos_kernel_api(params={ @@ -1222,7 +1221,7 @@ def hook__mac_policy_register(ql, address, params): func, = struct.unpack(" -# reference to 《Mac OS X and IOS Internals: To the Apple's Core》 -import logging from struct import pack, unpack + from qiling.const import * # define in kernel osfmk/mach/message.h @@ -82,7 +83,7 @@ def read_msg_header(self, addr, size): return header def read_msg_content(self, addr, size): - logging.debug("0x{:X}, {}".format(addr, size)) + self.ql.log.debug("0x{:X}, {}".format(addr, size)) return self.ql.mem.read(addr, size) @@ -125,10 +126,10 @@ def deal_with_msg(self, msg, addr): out_msg = self.ql.os.macho_task_server.get_special_port(msg.header, msg.content) out_msg.write_msg_to_mem(addr) else: - logging.info("Error Mach Msgid {} can not handled".format(msg.header.msgh_id)) + self.ql.log.info("Error Mach Msgid {} can not handled".format(msg.header.msgh_id)) raise Exception("Mach Msgid Not Found") - logging.debug("Reply-> Header: {}, Content: {}".format(out_msg.header, out_msg.content)) + self.ql.log.debug("Reply-> Header: {}, Content: {}".format(out_msg.header, out_msg.content)) def get_thread_port(self, MachoThread): return MachoThread.port.name diff --git a/qiling/os/macos/macos.py b/qiling/os/macos/macos.py index 2e6b69e1f..531d1b5bb 100644 --- a/qiling/os/macos/macos.py +++ b/qiling/os/macos/macos.py @@ -1,22 +1,20 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import traceback, logging +import traceback from unicorn import * from unicorn.x86_const import * from unicorn.arm64_const import * -from qiling.arch.x86 import * +from qiling.arch.x86 import * from qiling.const import * from qiling.os.const import * from qiling.os.posix.posix import QlOsPosix - from .const import * - from qiling.os.macos import macos from qiling.os.macos.structs import * from qiling.os.macos.events.macos_structs import * @@ -36,6 +34,7 @@ def __init__(self, ql): self.pid = self.profile.getint("KERNEL","pid") self.load() + # load MacOS driver def load_kext(self): from qiling.os.macos.events.macos_structs import mac_policy_list_t @@ -60,16 +59,16 @@ def load_kext(self): self.savedrip=0xffffff8000a163bd self.ql.run(begin=self.ql.loader.kext_alloc) self.kext_object = self.ql.reg.rax - logging.debug("[+] Created kext object at 0x%x" % self.kext_object) + self.ql.log.debug("Created kext object at 0x%x" % self.kext_object) self.ql.reg.rdi = self.kext_object self.ql.reg.rsi = 0 # NULL option self.savedrip=0xffffff8000a16020 self.ql.run(begin=self.ql.loader.kext_init) if self.ql.reg.rax == 0: - logging.debug("[!] Failed to initialize kext object") + self.ql.log.debug("Failed to initialize kext object") return - logging.debug("[+] Initialized kext object") + self.ql.log.debug("Initialized kext object") self.ql.reg.rdi = self.kext_object # FIXME Determine provider for kext @@ -77,9 +76,9 @@ def load_kext(self): self.savedrip=0xffffff8000a16102 self.ql.run(begin=self.ql.loader.kext_attach) if self.ql.reg.rax == 0: - logging.debug("[!] Failed to attach kext object") + self.ql.log.debug("Failed to attach kext object") return - logging.debug("[+] Attached kext object 1st time") + self.ql.log.debug("Attached kext object 1st time") self.ql.reg.rdi = self.kext_object self.ql.reg.rdi = 0 @@ -90,14 +89,14 @@ def load_kext(self): self.savedrip=0xffffff8000a16184 self.ql.run(begin=self.ql.loader.kext_probe) self.heap.free(tmp) - logging.debug("[+] Probed kext object") + self.ql.log.debug("Probed kext object") self.ql.reg.rdi = self.kext_object # FIXME Determine provider for kext self.ql.reg.rsi = 0 # ? self.savedrip=0xffffff8000a16198 self.ql.run(begin=self.ql.loader.kext_detach) - logging.debug("[+] Detached kext object") + self.ql.log.debug("Detached kext object") self.ql.reg.rdi = self.kext_object # FIXME Determine provider for kext @@ -105,9 +104,9 @@ def load_kext(self): self.savedrip=0xffffff8000a168a3 self.ql.run(begin=self.ql.loader.kext_attach) if self.ql.reg.rax == 0: - logging.debug("[!] Failed to attach kext object") + self.ql.log.debug("Failed to attach kext object") return - logging.debug("[+] Attached kext object 2nd time") + self.ql.log.debug("Attached kext object 2nd time") self.ql.reg.rdi = self.kext_object # FIXME Determine provider for kext @@ -117,7 +116,7 @@ def load_kext(self): else: from qiling.os.macos.structs import kmod_info_t, POINTER64 kmod_info_addr = self.heap.alloc(ctypes.sizeof(kmod_info_t)) - logging.debug("[+] Created fake kmod_info at 0x%x" % kmod_info_addr) + self.ql.log.debug("Created fake kmod_info at 0x%x" % kmod_info_addr) kmod_info = kmod_info_t(self.ql, kmod_info_addr) # OSKext.cpp:562 @@ -135,15 +134,16 @@ def load_kext(self): kmod_info.stop = POINTER64(self.ql.loader.kext_stop) kmod_info.updateToMem() - logging.debug("[+] Initialized kmod_info") + self.ql.log.debug("Initialized kmod_info") self.ql.reg.rdi = kmod_info_addr self.ql.reg.rsi = 0 self.savedrip=0xffffff80009c2c16 self.ql.run(begin=self.ql.loader.kext_start) + def load(self): - if self.ql.shellcoder: + if self.ql.code: return if self.ql.archtype== QL_ARCH.ARM64: @@ -161,11 +161,13 @@ def load(self): def hook_syscall(self, intno= None, int = None): return self.load_syscall() + def hook_sigtrap(self, intno= None, int = None): - logging.info("[!] Trap Found") + self.ql.log.info("Trap Found") self.emu_error() exit(1) + def run(self): #save initial stack pointer, so we can see if stack is balanced when #this function return at the end @@ -197,8 +199,8 @@ def callback_ret(ql): self.ql.loader.entry_point = self.ql.entry_point try: - if self.ql.shellcoder: - self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.shellcoder)), self.ql.timeout, self.ql.count) + if self.ql.code: + self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.code)), self.ql.timeout, self.ql.count) else: self.ql.emu_start(self.ql.loader.entry_point, self.exit_point, self.ql.timeout, self.ql.count) diff --git a/qiling/os/macos/map_syscall.py b/qiling/os/macos/map_syscall.py index 668906535..828bdd1d1 100644 --- a/qiling/os/macos/map_syscall.py +++ b/qiling/os/macos/map_syscall.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # cols = ("arm64", "x8664") diff --git a/qiling/os/macos/structs.py b/qiling/os/macos/structs.py index caa5595c3..402ec8ddb 100644 --- a/qiling/os/macos/structs.py +++ b/qiling/os/macos/structs.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import ctypes, struct from enum import IntEnum -import ctypes -import struct + class POINTER64(ctypes.Structure): _fields_ = [("value", ctypes.c_uint64)] diff --git a/qiling/os/macos/subsystems.py b/qiling/os/macos/subsystems.py index 6385bbdaf..e7969c2cd 100644 --- a/qiling/os/macos/subsystems.py +++ b/qiling/os/macos/subsystems.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # Subsystems which program may communicate to # generated by MIG @@ -10,10 +10,9 @@ # TODO: finish the servers # find Release source code in https://github.com/doadam/xnu-4570.1.46/master/BUILD/obj/RELEASE_X86_64/osfmk/RELEASE/mach/mach_host_server.c -import logging from struct import pack, unpack -from qiling.const import * +from qiling.const import * from .mach_port import * from .const import * @@ -31,7 +30,7 @@ def host_info(self, in_header, in_content): # parse request out_msg = MachMsg(self.ql) if len(in_content) < 16: - logging.debug("Error in Host info SubSystem -hostinfo()") + self.ql.log.debug("Error in Host info SubSystem -hostinfo()") raise ndr = unpack(" bufferSize: - logging.debug("Length error") + ql.log.debug("Length error") return 1 else: @@ -72,7 +67,6 @@ def ql_syscall_fgetattrlist(ql, fd, alist, attributeBuffer, bufferSize, options, def ql_syscall_poll(ql, target, address, size, *args, **kw): - logging.info("pool()") return KERN_SUCCESS @@ -82,24 +76,24 @@ def ql_syscall_poll(ql, target, address, size, *args, **kw): # 0xa def ql_syscall_kernelrpc_mach_vm_allocate_trap(ql, port, addr, size, flags, *args, **kw): - logging.debug("[+] [mach] mach vm allocate trap(port: 0x%x, addr: 0x%x, size: 0x%x, flags: 0x%x" % (port, addr, size, flags)) + ql.log.debug("[mach] mach vm allocate trap(port: 0x%x, addr: 0x%x, size: 0x%x, flags: 0x%x" % (port, addr, size, flags)) mmap_address = ql.os.macho_task.min_offset mmap_end = page_align_end(mmap_address + size, PAGE_SIZE) ql.mem.map(mmap_address, mmap_end - mmap_address) ql.mem.write(mmap_address, b'\x00'*(mmap_end - mmap_address)) ql.os.macho_task.min_offset = mmap_end - logging.debug("[+] vm alloc form 0x%x to 0x%0x" % (mmap_address, mmap_end)) + ql.log.debug("vm alloc form 0x%x to 0x%0x" % (mmap_address, mmap_end)) ql.mem.write(addr, struct.pack(" Header: %s, Content: %s" % (mach_msg.header, mach_msg.content)) + ql.log.debug("Recv-> Header: %s, Content: %s" % (mach_msg.header, mach_msg.content)) ql.os.macho_port_manager.deal_with_msg(mach_msg, args) return 0 @@ -174,9 +168,8 @@ def ql_syscall_mach_msg_trap(ql, args, opt, ssize, rsize, rname, timeout): # 0x21 def ql_syscall_access_macos(ql, path, flags, *args, **kw): - path_str = macho_read_string(ql, path, MAX_PATH_SIZE) - logging.info("access(%s, 0x%x)" % (path_str, flags)) - logging.debug("[+] access(path: %s, flags: 0x%x)" % (path_str, flags)) + path_str = ql.os.read_cstring(path) + ql.log.debug("access(path: %s, flags: 0x%x)" % (path_str, flags)) if not ql.os.macho_fs.isexists(path_str): return ENOENT else: @@ -184,7 +177,7 @@ def ql_syscall_access_macos(ql, path, flags, *args, **kw): # 0x30 def ql_syscall_sigprocmask(ql, how, mask, omask, *args, **kw): - logging.info("sigprocmask(how: 0x%x, mask: 0x%x, omask: 0x%x)" % (how, mask, omask)) + ql.log.debug("sigprocmask(how: 0x%x, mask: 0x%x, omask: 0x%x)" % (how, mask, omask)) # 0x5c def ql_syscall_fcntl64_macos(ql, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw): @@ -203,12 +196,12 @@ def ql_syscall_fcntl64_macos(ql, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw): else: regreturn = 0 - logging.info("fcntl64(fd: %d, cmd: %d, arg: 0x%x) = %d" % (fcntl_fd, fcntl_cmd, fcntl_arg, regreturn)) + ql.log.debug("fcntl64(fd: %d, cmd: %d, arg: 0x%x) = %d" % (fcntl_fd, fcntl_cmd, fcntl_arg, regreturn)) return regreturn # 0x99 def ql_syscall_pread(ql, fd, buf, nbyte, offset, *args, **kw): - logging.info("pread(fd: 0x%x, buf: 0x%x, nbyte: 0x%x, offset: 0x%x)" % ( + ql.log.debug("pread(fd: 0x%x, buf: 0x%x, nbyte: 0x%x, offset: 0x%x)" % ( fd, buf, nbyte, offset )) if fd >= 0 and fd <= MAX_FD_SIZE: @@ -222,18 +215,14 @@ def ql_syscall_pread(ql, fd, buf, nbyte, offset, *args, **kw): def ql_syscall_csops(ql, pid, ops, useraddr, usersize, *args, **kw): flag = struct.pack(" bufferSize: - logging.debug("Length error") + ql.log.debug("Length error") return 1 else: ql.mem.write(attributeBuffer, attr) @@ -276,14 +265,13 @@ def ql_syscall_getattrlist(ql, path, alist, attributeBuffer, bufferSize, options # rlim_t rlim_max; /* maximum value for rlim_cur */ uint64 # }; def ql_syscall_getrlimit(ql, which, rlp, *args, **kw): - logging.info("getrlimit(0x%x, 0x%x)" % (which, rlp)) - logging.debug("[+] getrlimit(which:0x%x, rlp:0x%x)" % (which, rlp)) + ql.log.debug("getrlimit(which:0x%x, rlp:0x%x)" % (which, rlp)) _RLIMIT_POSIX_FLAG = 0x1000 RLIM_NLIMITS = 9 which = which & _RLIMIT_POSIX_FLAG if which >= RLIM_NLIMITS: return EINVAL - else : + else: ql.mem.write(rlp, b'\x00\x13\x00\x00\x00\x00\x00\x00') # rlim_cur ql.mem.write(rlp, b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F') # rlim_max pass @@ -315,12 +303,12 @@ def ql_syscall_mmap2_macos(ql, mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags mmap_base = ql.loader.mmap_address ql.loader.mmap_address = mmap_base + ((mmap2_length + 0x1000 - 1) // 0x1000) * 0x1000 - logging.debug("[+] log mmap - mmap2(0x%x, %d, 0x%x, 0x%x, %d, %d)" % (mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap2_fd, mmap2_pgoffset)) - logging.debug("[+] log mmap - return addr : " + hex(mmap_base)) - logging.debug("[+] log mmap - addr range : " + hex(mmap_base) + ' - ' + hex(mmap_base + ((mmap2_length + 0x1000 - 1) // 0x1000) * 0x1000)) + ql.log.debug("mmap - mmap2(0x%x, %d, 0x%x, 0x%x, %d, %d)" % (mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap2_fd, mmap2_pgoffset)) + ql.log.debug("mmap - return addr : " + hex(mmap_base)) + ql.log.debug("mmap - addr range : " + hex(mmap_base) + ' - ' + hex(mmap_base + ((mmap2_length + 0x1000 - 1) // 0x1000) * 0x1000)) if need_mmap: - logging.debug("[+] log mmap - mapping needed") + ql.log.debug("mmap - mapping needed") try: ql.mem.map(mmap_base, ((mmap2_length + 0x1000 - 1) // 0x1000) * 0x1000) except: @@ -332,32 +320,29 @@ def ql_syscall_mmap2_macos(ql, mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags ql.os.fd[mmap2_fd].lseek(mmap2_pgoffset) data = ql.os.fd[mmap2_fd].read(mmap2_length) - logging.debug("[+] log mem wirte : " + hex(len(data))) - logging.debug("[+] log mem mmap : " + str(ql.os.fd[mmap2_fd].name)) + ql.log.debug("mem wirte : " + hex(len(data))) + ql.log.debug("mem mmap : " + str(ql.os.fd[mmap2_fd].name)) ql.mem.write(mmap_base, data) mem_info = ql.os.fd[mmap2_fd].name - logging.info("mmap2(0x%x, %d, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap2_fd, mmap2_pgoffset, mmap_base)) + ql.log.debug("mmap2(0x%x, %d, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap2_fd, mmap2_pgoffset, mmap_base)) regreturn = mmap_base - logging.debug("[+] mmap_base is 0x%x" % regreturn) + ql.log.debug("mmap_base is 0x%x" % regreturn) return regreturn # 0xca def ql_syscall_sysctl(ql, name, namelen, old, oldlenp, new_arg, newlen): - logging.info("sysctl(name: 0x%x, namelen: 0x%x, old: 0x%x, oldlenp: 0x%x, new: 0x%x, newlen: 0x%x)" % ( + ql.log.debug("sysctl(name: 0x%x, namelen: 0x%x, old: 0x%x, oldlenp: 0x%x, new: 0x%x, newlen: 0x%x)" % ( name, namelen, old, oldlenp, new_arg, newlen )) return KERN_SUCCESS # 0x112 def ql_syscall_sysctlbyname(ql, name, namelen, old, oldlenp, new_arg, newlen): - logging.info("sysctlbyname(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)" % ( - name, namelen, old, oldlenp, new_arg, newlen - )) - logging.debug("[+] sysctlbyname(name: 0x%x, namelen: 0x%x, old: 0x%x, oldlenp: 0x%x, new: 0x%x, newlen: 0x%x)" % ( + ql.log.debug("sysctlbyname(name: 0x%x, namelen: 0x%x, old: 0x%x, oldlenp: 0x%x, new: 0x%x, newlen: 0x%x)" % ( name, namelen, old, oldlenp, new_arg, newlen )) return KERN_SUCCESS @@ -365,17 +350,13 @@ def ql_syscall_sysctlbyname(ql, name, namelen, old, oldlenp, new_arg, newlen): # 0x126 # check shared region if avalible , return not ready every time def ql_syscall_shared_region_check_np(ql, p, uap, retvalp, *args, **kw): - logging.info("shared_region_check_np(0x%x, 0x%x, 0x%x) = 0x%x" % (p, uap, retvalp, EINVAL)) - logging.debug("[+] shared_region_check_np(p: 0x%x, uap: 0x%x, retvalp: 0x%x) = 0x%x" % (p, uap, retvalp, EINVAL)) + ql.log.debug("shared_region_check_np(p: 0x%x, uap: 0x%x, retvalp: 0x%x) = 0x%x" % (p, uap, retvalp, EINVAL)) return EINVAL # 0x150 def ql_syscall_proc_info(ql, callnum, pid, flavor, arg, buff, buffer_size): retval = struct.unpack("= 0 and regreturn != 2: - logging.debug("[+] File Found: %s" % relative_path) + ql.log.debug("File Found: %s" % relative_path) else: - logging.debug("[!] File Not Found %s" % relative_path) + ql.log.debug("File Not Found %s" % relative_path) return regreturn # 0x1b6 def ql_syscall_shared_region_map_and_slide_np(ql, fd, count, mappings_addr, slide, slide_start, slide_size): - logging.info("shared_region_map_and_slide_np(%d, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)" % ( - fd, count ,mappings_addr, slide, slide_start, slide_size - )) - logging.debug("[+] shared_region_map_and_slide_np(fd: %d, count: 0x%x, mappings: 0x%x, slide: 0x%x, slide_start: 0x%x, slide_size: 0x%x)" % ( + ql.log.debug("shared_region_map_and_slide_np(fd: %d, count: 0x%x, mappings: 0x%x, slide: 0x%x, slide_start: 0x%x, slide_size: 0x%x)" % ( fd, count ,mappings_addr, slide, slide_start, slide_size )) mapping_list = [] @@ -597,34 +572,26 @@ def ql_syscall_shared_region_map_and_slide_np(ql, fd, count, mappings_addr, slid # 0x1e3 def ql_syscall_csrctl(ql, op, useraddr, usersize, *args, **kw): - logging.info("csrctl(0x%x, 0x%x, 0x%x)" % (op, useraddr, usersize)) - logging.debug("csrctl(op: 0x%x, useraddr :0x%x, usersize: 0x%x)" % (op, useraddr, usersize)) + ql.log.debug("csrctl(op: 0x%x, useraddr :0x%x, usersize: 0x%x)" % (op, useraddr, usersize)) return 1 # 0x1f4 def ql_syscall_getentropy(ql, buffer, size, *args, **kw): - logging.info("getentropy(0x%x, 0x%x)" % (buffer, size)) - logging.debug("[+] getentropy(buffer: 0x%x, size: 0x%x)" % (buffer, size)) + ql.log.debug("getentropy(buffer: 0x%x, size: 0x%x)" % (buffer, size)) return KERN_SUCCESS # 0x208 def ql_syscall_terminate_with_payload(ql, pid, reason_namespace, reason_code, payload, payload_size, reason_string): - logging.info("terminate_with_payload(%d, 0x%x, 0x%x, 0x%x 0x%x, 0x%x)" % ( - pid, reason_namespace, reason_code,payload, payload_size, reason_string)) - - logging.debug("[+] terminate_with_payload(pid: %d, reason_namespace: 0x%x, reason_code: 0x%x, payload: 0x%x \ + ql.log.debug("terminate_with_payload(pid: %d, reason_namespace: 0x%x, reason_code: 0x%x, payload: 0x%x \ payload_size: 0x%x, reason_string: 0x%x)" % (pid, reason_namespace, reason_code, payload, payload_size, reason_string)) ql.emu_stop() - raise QlErrorSyscallError("[!] Exit with Error") + raise QlErrorSyscallError("Exit with Error") return KERN_SUCCESS # 0x209 def ql_syscall_abort_with_payload(ql, reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags): - logging.info("abort_with_payload(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)" % ( - reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags)) - - logging.debug("[+] abort_with_payload(reason_namespace: 0x%x, reason_code: 0x%x, payload: 0x%x, payload_size: 0x%x, reason_string: 0x%x,\ + ql.log.debug("abort_with_payload(reason_namespace: 0x%x, reason_code: 0x%x, payload: 0x%x, payload_size: 0x%x, reason_string: 0x%x,\ reason_flags: 0x%x)" % (reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags)) return KERN_SUCCESS @@ -637,6 +604,6 @@ def ql_syscall_abort_with_payload(ql, reason_namespace, reason_code, payload, pa # 0x3d # thread_set_tsd_base def ql_syscall_thread_fast_set_cthread_self64(ql, u_info_addr, *args, **kw): - logging.debug("[+] [mdep] thread fast set cthread self64(tsd_base:0x%x)" % (u_info_addr)) + ql.log.debug("[mdep] thread fast set cthread self64(tsd_base:0x%x)" % (u_info_addr)) ql.reg.msr(GSMSR, u_info_addr) return KERN_SUCCESS diff --git a/qiling/os/macos/task.py b/qiling/os/macos/task.py index 3269b1322..cca4b339e 100644 --- a/qiling/os/macos/task.py +++ b/qiling/os/macos/task.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # task class not finished # TODO: finished diff --git a/qiling/os/macos/thread.py b/qiling/os/macos/thread.py index 3bbc2aa9f..f86ec184e 100644 --- a/qiling/os/macos/thread.py +++ b/qiling/os/macos/thread.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os + from qiling.os.thread import * from qiling.os.macos.mach_port import * diff --git a/qiling/os/macos/utils.py b/qiling/os/macos/utils.py index dc3831ce9..34e8d3a62 100644 --- a/qiling/os/macos/utils.py +++ b/qiling/os/macos/utils.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import ctypes, os, struct, logging +import ctypes, os, struct from unicorn.x86_const import * from unicorn.arm_const import * @@ -17,43 +17,43 @@ def IOConnectCallMethod(ql, selector, output_array, output_cnt, output_struct, output_struct_size): if ql.os.IOKit is not True: - logging.info("[!] Must have a IOKit driver") + ql.log.info("Must have a IOKit driver") return output_array, output_struct args_addr = ql.os.heap.alloc(ctypes.sizeof(IOExternalMethodArguments)) - logging.debug("[+] Created IOExternalMethodArguments object at 0x%x" % args_addr) + ql.log.debug("Created IOExternalMethodArguments object at 0x%x" % args_addr) args_obj = IOExternalMethodArguments(ql, args_addr) if input_array is not None and input_cnt != 0: input_array_addr = ql.os.heap.alloc(input_cnt) ql.mem.write(input_array_addr, b''.join(struct.pack(" max_length: - break - return ret - - -def print_function(ql, passthru, address, function_name, params, ret): - function_name = function_name.replace('hook_', '') - if function_name in ("__stdio_common_vfprintf", "printf", "wsprintfW", "sprintf"): - return - log = '0x%0.2x: %s(' % (address, function_name) - for each in params: - value = params[each] - if type(value) == str or type(value) == bytearray: - log += '%s = "%s", ' % (each, value) - else: - log += '%s = 0x%x, ' % (each, value) - log = log.strip(", ") - log += ')' - if ret is not None: - log += ' = 0x%x' % ret - - if passthru: - log += ' (PASSTHRU)' - - if ql.output != QL_OUTPUT.DEBUG: - log = log.partition(" ")[-1] - logging.info(log) - else: - logging.debug(log) diff --git a/qiling/os/mapper.py b/qiling/os/mapper.py index 18da471af..73b93f9f1 100644 --- a/qiling/os/mapper.py +++ b/qiling/os/mapper.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import inspect + from .filestruct import ql_file from .utils import QlOsUtils -import inspect, logging + # All mapped objects should inherit this class. # Note this object is compatible with ql_file. @@ -94,7 +97,7 @@ def mapping_count(self): def open_ql_file(self, path, openflags, openmode): if self.has_mapping(path): - logging.info(f"mapping {path}") + self.ql.log.info(f"mapping {path}") return self._open_mapping_ql_file(path, openflags, openmode) else: real_path = self.ql.os.transform_to_real_path(path) @@ -102,7 +105,7 @@ def open_ql_file(self, path, openflags, openmode): def open(self, path, openmode): if self.has_mapping(path): - logging.info(f"mapping {path}") + self.ql.log.info(f"mapping {path}") return self._open_mapping(path, openmode) else: real_path = self.ql.os.transform_to_real_path(path) diff --git a/qiling/os/memory.py b/qiling/os/memory.py index c0941a941..60add9b8c 100644 --- a/qiling/os/memory.py +++ b/qiling/os/memory.py @@ -1,13 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import os, re, logging +import os, re from qiling.const import * from qiling.exception import * + from unicorn import ( UC_PROT_ALL, UC_PROT_EXEC, @@ -25,17 +26,21 @@ class QlMemoryManager: def __init__(self, ql): self.ql = ql self.map_info = [] - - if self.ql.archbit == 64: - max_addr = 0xFFFFFFFFFFFFFFFF - elif self.ql.archbit == 32: - max_addr = 0xFFFFFFFF - elif self.ql.archbit == 16: - # 20bit address line - max_addr = 0xFFFFF + bit_stuff = { + 64 : (0xFFFFFFFFFFFFFFFF,), + 32 : (0xFFFFFFFF,), + 16 : (0xFFFFF,) # 20bit address line + } + + if ql.archbit not in bit_stuff: + raise QlErrorStructConversion("Unsupported Qiling archtecture for memory manager") + + max_addr, = bit_stuff[ql.archbit] + + #self.read_ptr = read_ptr self.max_addr = max_addr - self.max_mem_addr = max_addr + self.max_mem_addr = max_addr def string(self, addr, value=None ,encoding='utf-8'): @@ -130,13 +135,13 @@ def _perms_mapping(ps): perms_sym.append("-") return "".join(perms_sym) - logging.info("[+] Start End Perm. Path") + self.ql.log.info("[+] Start End Perm. Path") for start, end, perm, info in self.map_info: _perm = _perms_mapping(perm) image = self.ql.os.find_containing_image(start) if image: info += f" ({image.path})" - logging.info("[+] %08x - %08x - %s %s" % (start, end, _perm, info)) + self.ql.log.info("[+] %08x - %08x - %s %s" % (start, end, _perm, info)) def get_lib_base(self, filename): @@ -170,21 +175,38 @@ def restore(self, mem_dict): info = value[3] mem_read = bytes(value[4]) - logging.debug("restore key: %i 0x%x 0x%x %s" % (key, start, end, info)) + self.ql.log.debug("restore key: %i 0x%x 0x%x %s" % (key, start, end, info)) if self.is_mapped(start, end-start) == False: - logging.debug("mapping 0x%x 0x%x mapsize 0x%x" % (start, end, end-start)) + self.ql.log.debug("mapping 0x%x 0x%x mapsize 0x%x" % (start, end, end-start)) self.map(start, end-start, perms=perm, info=info) - logging.debug("writing 0x%x size 0x%x write_size 0x%x " % (start, end-start, len(mem_read))) + self.ql.log.debug("writing 0x%x size 0x%x write_size 0x%x " % (start, end-start, len(mem_read))) self.write(start, mem_read) def read(self, addr: int, size: int) -> bytearray: return self.ql.uc.mem_read(addr, size) + def read_ptr(self, addr: int, size: int=None): + if not size: + size = self.ql.archbit // 8 - def write(self, addr: int, data: bytes) -> None: - return self.ql.uc.mem_write(addr, data) + if size == 2: + return self.ql.unpack16(self.read(addr, 2)) + elif size == 4: + return self.ql.unpack32(self.read(addr, 4)) + elif size == 8: + return self.ql.unpack64(self.read(addr, 8)) + else: + raise QlErrorStructConversion(f"Unsupported pointer size: {size}") + def write(self, addr: int, data: bytes) -> None: + try: + self.ql.uc.mem_write(addr, data) + except: + self.show_mapinfo() + self.ql.log.debug("addresss write length: " + str(len(data))) + self.ql.log.error("addresss write error: " + hex(addr)) + raise def search(self, needle: bytes, begin= None, end= None): """ @@ -318,7 +340,7 @@ def find_free_space( # address is free if addr + size < max_gap_addr and self.is_mapped(addr, size) == False: return addr - raise QlOutOfMemory("[!] Out Of Memory") + raise QlOutOfMemory("Out Of Memory") def map_anywhere( @@ -389,13 +411,13 @@ def map(self, addr, size, perms=UC_PROT_ALL, info=None, ptr=None): self.ql.uc.mem_map(addr, size, perms) self.add_mapinfo(addr, addr + size, perms, info if info else "[mapped]") else: - raise QlMemoryMappedError("[!] Memory Mapped") + raise QlMemoryMappedError("Memory Mapped") else: self.ql.uc.mem_map_ptr(addr, size, perms, ptr) def get_mapped(self): for idx, val in enumerate(self.ql.uc.mem_regions()): - logging.info(idx, list(map(hex, val))) + self.ql.log.info(idx, list(map(hex, val))) # A Simple Heap Implementation class Chunk(): @@ -444,12 +466,6 @@ def restore(self, saved_state): self.mem_alloc = saved_state['mem_alloc'] def alloc(self, size): - - if self.ql.archbit == 32: - size = self.ql.mem.align(size, 4) - elif self.ql.archbit == 64: - size = self.ql.mem.align(size, 8) - # Find the heap chunks that best matches size self.chunks.sort(key=Chunk.compare) for chunk in self.chunks: @@ -476,7 +492,7 @@ def alloc(self, size): self.chunks.append(chunk) chunk.inuse = True - #logging.debug("heap.alloc addresss: " + hex(chunk.address)) + #ql.log.debug("heap.alloc addresss: " + hex(chunk.address)) return chunk.address def size(self, addr): diff --git a/qiling/os/os.py b/qiling/os/os.py index f9552a4b3..e899ed1e2 100644 --- a/qiling/os/os.py +++ b/qiling/os/os.py @@ -1,22 +1,28 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import os, sys, types, logging +import sys + +from typing import Callable, Sequence, Mapping, MutableMapping, Any +from unicorn.x86_const import * -from .utils import QlOsUtils from .const import * from .filestruct import ql_file from .mapper import QlFsMapper +from .utils import QlOsUtils -from qiling.const import * +from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT, QL_OS_POSIX +from qiling.exception import QlErrorArch +from qiling.os.fncc import QlOsFncc -from unicorn.x86_const import * -class QlOs(QlOsUtils): +class QlOs(QlOsUtils, QlOsFncc): def __init__(self, ql): - super(QlOs, self).__init__(ql) + #super(QlOs, self).__init__(ql) + QlOsUtils.__init__(self, ql) + QlOsFncc.__init__(self, ql) self.ql = ql self.fs_mapper = QlFsMapper(ql) self.child_processes = False @@ -27,22 +33,14 @@ def __init__(self, ql): self.services = {} self.elf_mem_start = 0x0 - if "fileno" not in dir(sys.stdin) or "fileno" not in dir(sys.stdout) or "fileno" not in dir(sys.stderr): + if not hasattr(sys.stdin, "fileno") or not hasattr(sys.stdout, "fileno") or not hasattr(sys.stderr, "fileno"): # IDAPython has some hack on standard io streams and thus they don't have corresponding fds. - if "buffer" in dir(sys.stdin): - self.stdin = sys.stdin.buffer - else: - self.stdin = sys.stdin - if "buffer" in dir(sys.stdout): - self.stdout = sys.stdout.buffer - else: - self.stdout = sys.stdout - if "buffer" in dir(sys.stderr): - self.stderr = sys.stderr.buffer - else: - self.stderr = sys.stderr + + self.stdin = sys.stdin.buffer if hasattr(sys.stdin, "buffer") else sys.stdin + self.stdout = sys.stdout.buffer if hasattr(sys.stdout, "buffer") else sys.stdout + self.stderr = sys.stderr.buffer if hasattr(sys.stderr, "buffer") else sys.stderr else: - self.stdin = ql_file('stdin', sys.stdin.fileno()) + self.stdin = ql_file('stdin', sys.stdin.fileno()) self.stdout = ql_file('stdout', sys.stdout.fileno()) self.stderr = ql_file('stderr', sys.stderr.fileno()) @@ -55,23 +53,18 @@ def __init__(self, ql): if self.ql.stderr != 0: self.stderr = self.ql.stderr - if self.ql.archbit == 32: - EMU_END = 0x8fffffff - elif self.ql.archbit == 64: - EMU_END = 0xffffffffffffffff - - elif self.ql.archbit == 16: - # 20bit address lane - EMU_END = 0xfffff - # defult exit point - self.exit_point = EMU_END - - if self.ql.shellcoder: - self.shellcoder_ram_size = int(self.profile.get("SHELLCODER", "ram_size"), 16) + self.exit_point = { + 16: 0xfffff, # 20bit address lane + 32: 0x8fffffff, + 64: 0xffffffffffffffff + }.get(self.ql.archbit, None) + + if self.ql.code: + self.code_ram_size = int(self.profile.get("CODE", "ram_size"), 16) # this shellcode entrypoint does not work for windows # windows shellcode entry point will comes from pe loader - self.entry_point = int(self.profile.get("SHELLCODER", "entry_point"), 16) + self.entry_point = int(self.profile.get("CODE", "entry_point"), 16) # We can save every syscall called self.syscalls = {} @@ -85,6 +78,54 @@ def save(self): def restore(self, saved_state): pass + def set_syscall(self, target_syscall, intercept_function, intercept): + if intercept == QL_INTERCEPT.ENTER: + if isinstance(target_syscall, int): + self.dict_posix_onEnter_syscall_by_num[target_syscall] = intercept_function + else: + syscall_name = "ql_syscall_" + str(target_syscall) + self.dict_posix_onEnter_syscall[syscall_name] = intercept_function + + elif intercept == QL_INTERCEPT.EXIT: + if self.ql.ostype in (QL_OS_POSIX): + if isinstance(target_syscall, int): + self.dict_posix_onExit_syscall_by_num[target_syscall] = intercept_function + else: + syscall_name = "ql_syscall_" + str(target_syscall) + self.dict_posix_onExit_syscall[syscall_name] = intercept_function + + else: + if self.ql.ostype in (QL_OS_POSIX): + if isinstance(target_syscall, int): + self.dict_posix_syscall_by_num[target_syscall] = intercept_function + else: + syscall_name = "ql_syscall_" + str(target_syscall) + self.dict_posix_syscall[syscall_name] = intercept_function + + elif self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + self.set_api(target_syscall, intercept_function) + + def set_api(self, api_name, intercept_function, intercept): + if self.ql.ostype == QL_OS.UEFI: + api_name = "hook_" + str(api_name) + + if intercept == QL_INTERCEPT.ENTER: + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + self.user_defined_api_onenter[api_name] = intercept_function + else: + self.add_function_hook(api_name, intercept_function, intercept) + + elif intercept == QL_INTERCEPT.EXIT: + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + self.user_defined_api_onexit[api_name] = intercept_function + else: + self.add_function_hook(api_name, intercept_function, intercept) + + else: + if self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI): + self.user_defined_api[api_name] = intercept_function + else: + self.add_function_hook(api_name, intercept_function) def find_containing_image(self, pc): for image in self.ql.loader.images: @@ -92,198 +133,49 @@ def find_containing_image(self, pc): return image def emu_error(self): - logging.error("\n") + self.ql.log.error("\n") for reg in self.ql.reg.register_mapping: if isinstance(reg, str): REG_NAME = reg REG_VAL = self.ql.reg.read(reg) - logging.error("%s\t:\t 0x%x" % (REG_NAME, REG_VAL)) + self.ql.log.error("%s\t:\t 0x%x" % (REG_NAME, REG_VAL)) - logging.error("\n") - logging.error("PC = 0x%x" % (self.ql.reg.arch_pc)) + self.ql.log.error("\n") + self.ql.log.error("PC = 0x%x" % (self.ql.reg.arch_pc)) containing_image = self.find_containing_image(self.ql.reg.arch_pc) if containing_image: offset = self.ql.reg.arch_pc - containing_image.base - logging.error(" (%s+0x%x)" % (containing_image.path, offset)) + self.ql.log.error(" (%s+0x%x)" % (containing_image.path, offset)) else: - logging.info("\n") + self.ql.log.info("\n") self.ql.mem.show_mapinfo() try: buf = self.ql.mem.read(self.ql.reg.arch_pc, 8) - logging.error("%r" % ([hex(_) for _ in buf])) + self.ql.log.error("%r" % ([hex(_) for _ in buf])) - logging.info("\n") + self.ql.log.info("\n") self.disassembler(self.ql, self.ql.reg.arch_pc, 64) except: - logging.error("Error: PC(0x%x) Unreachable" % self.ql.reg.arch_pc) - - - def _x86_set_args(self, args): - for i in range(len(args)): - # skip ret_addr - self.ql.stack_write((i + 1) * 4, args[i]) - - - def _x8664_set_args(self, args): - reg_list=None - if self.ql.ostype == QL_OS.LINUX: - reg_list = [UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_R10, UC_X86_REG_R8, UC_X86_REG_R9] - elif self.ql.ostype == QL_OS.WINDOWS: - reg_list = [UC_X86_REG_RCX, UC_X86_REG_RDX, UC_X86_REG_R8, UC_X86_REG_R9] - if reg_list!=None: - for i in range(len(args)): - self.ql.uc.reg_write(reg_list[i], args[i]) + self.ql.log.error("Error: PC(0x%x) Unreachable" % self.ql.reg.arch_pc) + def set_function_args(self, args: Sequence[int]) -> None: + """Set function call arguments. + """ - def set_function_args(self, args): - if self.ql.archtype == QL_ARCH.X86: # 32bit - self._x86_set_args(args) - else: # 64bit - self._x8664_set_args(args) - - - def _x86_get_params_by_index(self, index): - # index starts from 0 - # skip ret_addr - return self.ql.stack_read((index + 1) * 4) - - - def _x8664_get_params_by_index(self, index): - reg_list = ["rcx", "rdx", "r8", "r9"] - if index < 4: - return self.ql.reg.read(reg_list[index]) - - index -= 4 - # skip ret_addr - return self.ql.stack_read((index + 5) * 8) - - - def get_param_by_index(self, index): - if self.ql.archtype == QL_ARCH.X86: - return self._x86_get_params_by_index(index) - elif self.ql.archtype == QL_ARCH.X8664: - return self._x8664_get_params_by_index(index) - - - def _x86_get_args(self, number): - arg_list = [] - for i in range(number): - # skip ret_addr - arg_list.append(self.ql.stack_read((i + 1) * 4)) - if number == 1: - return arg_list[0] - else: - return arg_list - - - def _x8664_get_args(self, number): - reg_list = ["rcx", "rdx", "r8", "r9"] - arg_list = [] - reg_num = number - if reg_num > 4: - reg_num = 4 - number -= reg_num - for i in reg_list[:reg_num]: - arg_list.append(self.ql.reg.read(i)) - for i in range(number): - # skip ret_addr and 32 byte home space - arg_list.append(self.ql.stack_read((i + 5) * 8)) - if reg_num == 1: - return arg_list[0] - else: - return arg_list - - - def set_function_params(self, in_params, out_params): - index = 0 - for each in in_params: - if in_params[each] == DWORD or in_params[each] == POINTER: - out_params[each] = self.get_param_by_index(index) - elif in_params[each] == ULONGLONG: - if self.ql.archtype == QL_ARCH.X86: - low = self.get_param_by_index(index) - index += 1 - high = self.get_param_by_index(index) - out_params[each] = high << 32 + low - else: - out_params[each] = self.get_param_by_index(index) - elif in_params[each] == STRING: - ptr = self.get_param_by_index(index) - if ptr == 0: - out_params[each] = 0 - else: - content = self.read_cstring(ptr) - out_params[each] = content - elif in_params[each] == WSTRING: - ptr = self.get_param_by_index(index) - if ptr == 0: - out_params[each] = 0 - else: - content = self.read_wstring(ptr) - out_params[each] = content - elif in_params[each] == GUID: - ptr = self.get_param_by_index(index) - if ptr == 0: - out_params[each] = 0 - else: - out_params[each] = str(self.read_guid(ptr)) - index += 1 - return index - - - def get_function_param(self, number): - if self.ql.archtype == QL_ARCH.X86: - return self._x86_get_args(number) - elif self.ql.archtype == QL_ARCH.X8664: - return self._x8664_get_args(number) - - - def set_return_value(self, ret): - if self.ql.archtype == QL_ARCH.X86: - self.ql.reg.eax = ret - elif self.ql.archtype == QL_ARCH.X8664: - self.ql.reg.rax = ret - - - def get_return_value(self): - if self.ql.archtype == QL_ARCH.X86: - return self.ql.reg.eax - elif self.ql.archtype == QL_ARCH.X8664: - return self.ql.reg.rax - - # - # stdcall cdecl fastcall cc - # - - def __x86_cc(self, param_num, params, func, args, kwargs, passthru=False): - # read params - if params is not None: - param_num = self.set_function_params(params, args[2]) - - if isinstance(self.winapi_func_onenter, types.FunctionType): - address, params = self.winapi_func_onenter(*args, **kwargs) - args = (self.ql, address, params) - onEnter = True - else: - onEnter = False - - # call function - result = func(*args, **kwargs) - - if isinstance(self.winapi_func_onexit, types.FunctionType): - self.winapi_func_onexit(*args, **kwargs) - - # set return value - if result is not None: - self.set_return_value(result) - # print - self.print_function(args[1], func.__name__, args[2], result, passthru) - - return result, param_num + for i, (reg, arg) in enumerate(zip(self._cc_args, args)): + # should arg be written to a reg or the stack? + if reg is None: + # get matching stack item + si = i - self._cc_args.index(None) + # skip return address and shadow space + self.ql.stack_write((1 + self._shadow + si) * self._asize, arg) + else: + self.ql.uc.reg_write(reg, arg) + def clear_syscalls(self): self.syscalls = {} self.syscalls_counter = 0 @@ -292,11 +184,14 @@ def clear_syscalls(self): def _call_api(self, name, params, result, address, return_address): params_with_values = {} + if name.startswith("hook_"): - name = name.split("hook_", 1)[1] + name = name[5:] + # printfs are shit if params is not None: self.set_function_params(params, params_with_values) + self.syscalls.setdefault(name, []).append({ "params": params_with_values, "result": result, @@ -305,48 +200,4 @@ def _call_api(self, name, params, result, address, return_address): "position": self.syscalls_counter }) - self.syscalls_counter += 1 - - - def x86_stdcall(self, param_num, params, func, args, kwargs, passthru=False): - # if we check ret_addr before the call, we can't modify the ret_addr from inside the hook - result, param_num = self.__x86_cc(param_num, params, func, args, kwargs, passthru) - - # get ret addr - ret_addr = self.ql.stack_read(0) - - # append syscall to list - self._call_api(func.__name__, params, result, self.ql.reg.arch_pc, ret_addr) - - if not passthru and self.PE_RUN: - # update stack pointer - self.ql.reg.arch_sp = self.ql.reg.arch_sp + ((param_num + 1) * 4) - - self.ql.reg.arch_pc = ret_addr - - return result - - - def x86_cdecl(self, param_num, params, func, args, kwargs, passthru=False): - result, param_num = self.__x86_cc(param_num, params, func, args, kwargs) - old_pc = self.ql.reg.arch_pc - # append syscall to list - self._call_api(func.__name__, params, result, old_pc, self.ql.stack_read(0)) - - if not passthru and self.PE_RUN: - self.ql.reg.arch_pc = self.ql.stack_pop() - - return result - - - def x8664_fastcall(self, param_num, params, func, args, kwargs, passthru=False): - result, param_num = self.__x86_cc(param_num, params, func, args, kwargs) - - old_pc = self.ql.reg.arch_pc - # append syscall to list - self._call_api(func.__name__, params, result, old_pc, self.ql.stack_read(0)) - - if not passthru and self.PE_RUN: - self.ql.reg.arch_pc = self.ql.stack_pop() - - return result + self.syscalls_counter += 1 \ No newline at end of file diff --git a/qiling/os/posix/const.py b/qiling/os/posix/const.py index af03b41ec..70e52016c 100644 --- a/qiling/os/posix/const.py +++ b/qiling/os/posix/const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.const import * @@ -246,3 +246,137 @@ 'O_NOFOLLOW' : 0x20000, 'O_SYNC' : 0x101000, } + +ECHILD = 10 + +errors = { + 1: 'EPERM', + 2: 'ENOENT', + 3: 'ESRCH', + 4: 'EINTR', + 5: 'EIO', + 6: 'ENXIO', + 7: 'E2BIG', + 8: 'ENOEXEC', + 9: 'EBADF', + 10: 'ECHILD', + 11: 'EAGAIN/EWOULDBLOCK', + 12: 'ENOMEM', + 13: 'EACCES', + 14: 'EFAULT', + 15: 'ENOTBLK', + 16: 'EBUSY', + 17: 'EEXIST', + 18: 'EXDEV', + 19: 'ENODEV', + 20: 'ENOTDIR', + 21: 'EISDIR', + 22: 'EINVAL', + 23: 'ENFILE', + 24: 'EMFILE', + 25: 'ENOTTY', + 26: 'ETXTBSY', + 27: 'EFBIG', + 28: 'ENOSPC', + 29: 'ESPIPE', + 30: 'EROFS', + 31: 'EMLINK', + 32: 'EPIPE', + 33: 'EDOM', + 34: 'ERANGE', + 35: 'EDEADLK', + 36: 'ENAMETOOLONG', + 37: 'ENOLCK', + 38: 'ENOSYS', + 39: 'ENOTEMPTY', + 40: 'ELOOP', + 42: 'ENOMSG', + 43: 'EIDRM', + 44: 'ECHRNG', + 45: 'EL2NSYNC', + 46: 'EL3HLT', + 47: 'EL3RST', + 48: 'ELNRNG', + 49: 'EUNATCH', + 50: 'ENOCSI', + 51: 'EL2HLT', + 52: 'EBADE', + 53: 'EBADR', + 54: 'EXFULL', + 55: 'ENOANO', + 56: 'EBADRQC', + 57: 'EBADSLT', + 59: 'EBFONT', + 60: 'ENOSTR', + 61: 'ENODATA', + 62: 'ETIME', + 63: 'ENOSR', + 64: 'ENONET', + 65: 'ENOPKG', + 66: 'EREMOTE', + 67: 'ENOLINK', + 68: 'EADV', + 69: 'ESRMNT', + 70: 'ECOMM', + 71: 'EPROTO', + 72: 'EMULTIHOP', + 73: 'EDOTDOT', + 74: 'EBADMSG', + 75: 'EOVERFLOW', + 76: 'ENOTUNIQ', + 77: 'EBADFD', + 78: 'EREMCHG', + 79: 'ELIBACC', + 80: 'ELIBBAD', + 81: 'ELIBSCN', + 82: 'ELIBMAX', + 83: 'ELIBEXEC', + 84: 'EILSEQ', + 85: 'ERESTART', + 86: 'ESTRPIPE', + 87: 'EUSERS', + 88: 'ENOTSOCK', + 89: 'EDESTADDRREQ', + 90: 'EMSGSIZE', + 91: 'EPROTOTYPE', + 92: 'ENOPROTOOPT', + 93: 'EPROTONOSUPPORT', + 94: 'ESOCKTNOSUPPORT', + 95: 'EOPNOTSUPP', + 96: 'EPFNOSUPPORT', + 97: 'EAFNOSUPPORT', + 98: 'EADDRINUSE', + 99: 'EADDRNOTAVAIL', + 100: 'ENETDOWN', + 101: 'ENETUNREACH', + 102: 'ENETRESET', + 103: 'ECONNABORTED', + 104: 'ECONNRESET', + 105: 'ENOBUFS', + 106: 'EISCONN', + 107: 'ENOTCONN', + 108: 'ESHUTDOWN', + 109: 'ETOOMANYREFS', + 110: 'ETIMEDOUT', + 111: 'ECONNREFUSED', + 112: 'EHOSTDOWN', + 113: 'EHOSTUNREACH', + 114: 'EALREADY', + 115: 'EINPROGRESS', + 116: 'ESTALE', + 117: 'EUCLEAN', + 118: 'ENOTNAM', + 119: 'ENAVAIL', + 120: 'EISNAM', + 121: 'EREMOTEIO', + 122: 'EDQUOT', + 123: 'ENOMEDIUM', + 124: 'EMEDIUMTYPE', + 125: 'ECANCELED', + 126: 'ENOKEY', + 127: 'EKEYEXPIRED', + 128: 'EKEYREVOKED', + 129: 'EKEYREJECTED', + 130: 'EOWNERDEAD', + 131: 'ENOTRECOVERABLE', +} diff --git a/qiling/os/posix/const_mapping.py b/qiling/os/posix/const_mapping.py index a23de1634..aad93ab01 100644 --- a/qiling/os/posix/const_mapping.py +++ b/qiling/os/posix/const_mapping.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .const import * from qiling.const import * diff --git a/qiling/os/posix/filestruct.py b/qiling/os/posix/filestruct.py index a30bc3f4d..76d5cac2c 100644 --- a/qiling/os/posix/filestruct.py +++ b/qiling/os/posix/filestruct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os from qiling.exception import * diff --git a/qiling/os/posix/posix.py b/qiling/os/posix/posix.py index 6ded974c7..b9517380b 100644 --- a/qiling/os/posix/posix.py +++ b/qiling/os/posix/posix.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -# For syscall_num -import logging +from inspect import signature from unicorn.arm64_const import * from unicorn.arm_const import * @@ -24,6 +23,19 @@ from qiling.os.linux.function_hook import ARMFunctionArg, MIPS32FunctionArg, ARM64FunctionArg, X86FunctionArg, X64FunctionArg +def getNameFromErrorCode(ret): + """ + Return the hex representation of a return value and if possible + add the corresponding error name to it. + :param ret: Return value of a syscall. + :return: The string representation of the error. + """ + if -ret in errors: + return hex(ret) + "(" + errors[-ret] + ")" + else: + return hex(ret) + + class QlOsPosix(QlOs): def __init__(self, ql): super(QlOsPosix, self).__init__(ql) @@ -116,16 +128,12 @@ def load_syscall(self, intno=None): self.syscall_name = self.syscall_map.__name__ else: self.syscall_name = map_syscall(self.ql, self.syscall) - - import qiling.os.posix.syscall - import qiling.os.linux.syscall - import qiling.os.macos.syscall - import qiling.os.freebsd.syscall - - if self.syscall_name not in dir(qiling.os.posix.syscall) \ - and self.syscall_name not in dir(qiling.os.linux.syscall) \ - and self.syscall_name not in dir(qiling.os.macos.syscall) \ - and self.syscall_name not in dir(qiling.os.freebsd.syscall): + _ostype_str = ostype_convert_str(self.ql.ostype) + _posix_syscall = ql_get_module_function(f"qiling.os.posix", "syscall") + _os_syscall = ql_get_module_function(f"qiling.os.{_ostype_str.lower()}", "syscall") + + if self.syscall_name not in dir(_posix_syscall) \ + and self.syscall_name not in dir(_os_syscall): syscall_name_str = self.syscall_name self.syscall_map = None @@ -168,9 +176,24 @@ def load_syscall(self, intno=None): ret = self.syscall_onEnter(self.ql, self.get_func_arg()[0], self.get_func_arg()[1], self.get_func_arg()[2], self.get_func_arg()[3], self.get_func_arg()[4], self.get_func_arg()[5]) if isinstance(ret, int) == False or ret & QL_CALL_BLOCK == 0: + args = [] + for n, argname in enumerate(signature(self.syscall_map).parameters.values()): + argname = str(argname) + if not n or argname.startswith("*"): + # first arg for syscalls is ql + continue + else: + # cut the first part of the arg if it is of form fstatat64_fd + argname = argname if "_" not in argname else "".join(argname.split("_")[1:]) + args.append(f"{argname}={hex(self.get_func_arg()[n-1])}") + args = ", ".join(args) + self.ql.log.info("0x%x: %s(%s)" % (self.ql.reg.arch_pc, self.syscall_map.__name__[11:], args)) ret = self.syscall_map(self.ql, self.get_func_arg()[0], self.get_func_arg()[1], self.get_func_arg()[2], self.get_func_arg()[3], self.get_func_arg()[4], self.get_func_arg()[5]) if ret is not None and isinstance(ret, int): - self.set_syscall_return(ret) + # each name has a list of calls, we want the last one and we want to update the return value + self.syscalls[self.syscall_name][-1]["result"] = ret + ret = self.set_syscall_return(ret) + self.ql.log.debug("%s() = %s" % (self.syscall_map.__name__[11:], getNameFromErrorCode(ret))) if self.syscall_onExit is not None: self.syscall_onExit(self.ql, self.get_func_arg()[0], self.get_func_arg()[1], self.get_func_arg()[2], self.get_func_arg()[3], self.get_func_arg()[4], self.get_func_arg()[5]) @@ -178,14 +201,14 @@ def load_syscall(self, intno=None): except KeyboardInterrupt: raise except Exception as e: - logging.exception("") - logging.info("[!] Syscall ERROR: %s DEBUG: %s" % (self.syscall_name, e)) + self.ql.log.exception("") + self.ql.log.info("Syscall ERROR: %s DEBUG: %s" % (self.syscall_name, e)) raise e else: - logging.warning( - "[!] 0x%x: syscall %s number = 0x%x(%d) not implemented" % (self.ql.reg.arch_pc, syscall_name_str, self.syscall, self.syscall)) + self.ql.log.warning( + "0x%x: syscall %s number = 0x%x(%d) not implemented" % (self.ql.reg.arch_pc, syscall_name_str, self.syscall, self.syscall)) if self.ql.debug_stop: - raise QlErrorSyscallNotFound("[!] Syscall Not Found") + raise QlErrorSyscallNotFound("Syscall Not Found") # get syscall def get_syscall(self): @@ -206,8 +229,6 @@ def get_syscall(self): return self.ql.reg.read(syscall_num) def set_syscall_return(self, regreturn): - # each name has a list of calls, we want the last one and we want to update the return value - self.syscalls[self.syscall_name][-1]["result"] = regreturn if self.ql.archtype == QL_ARCH.ARM: # ARM self.ql.reg.r0 = regreturn @@ -229,7 +250,7 @@ def set_syscall_return(self, regreturn): self.ql.reg.v0 = regreturn self.ql.reg.a3 = a3return - + return regreturn # get syscall def get_func_arg(self): diff --git a/qiling/os/posix/syscall/__init__.py b/qiling/os/posix/syscall/__init__.py index 6ec9a17da..d543080ca 100644 --- a/qiling/os/posix/syscall/__init__.py +++ b/qiling/os/posix/syscall/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .fcntl import * from .futex import * from .ioctl import * diff --git a/qiling/os/posix/syscall/fcntl.py b/qiling/os/posix/syscall/fcntl.py index 58515d76d..709e6e9ae 100644 --- a/qiling/os/posix/syscall/fcntl.py +++ b/qiling/os/posix/syscall/fcntl.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -41,13 +41,12 @@ def ql_syscall_open(ql, filename, flags, mode, *args, **kw): except QlSyscallError as e: regreturn = - e.errno - logging.info("open(%s, 0x%x, 0o%o) = %d" % (relative_path, flags, mode, regreturn)) - logging.debug("[+] open(%s, %s, 0o%o) = %d" % (relative_path, open_flags_mapping(flags, ql.archtype), mode, regreturn)) + ql.log.debug("open(%s, %s, 0o%o) = %d" % (relative_path, open_flags_mapping(flags, ql.archtype), mode, regreturn)) if regreturn >= 0 and regreturn != 2: - logging.debug("[+] File Found: %s" % real_path) + ql.log.debug("File Found: %s" % real_path) else: - logging.debug("[!] File Not Found %s" % real_path) + ql.log.debug("File Not Found %s" % real_path) return regreturn @@ -81,14 +80,13 @@ def ql_syscall_openat(ql, openat_fd, openat_path, openat_flags, openat_mode, *ar except QlSyscallError: regreturn = -1 - logging.info("openat(%d, %s, 0x%x, 0o%o) = %d" % (openat_fd, relative_path, openat_flags, openat_mode, regreturn)) - logging.debug("[+] openat(%d, %s, %s, 0o%o) = %d" % ( + ql.log.debug("openat(%d, %s, %s, 0o%o) = %d" % ( openat_fd, relative_path, open_flags_mapping(openat_flags, ql.archtype), openat_mode, regreturn)) if regreturn >= 0 and regreturn != 2: - logging.debug("[+] File Found: %s" % real_path) + ql.log.debug("File Found: %s" % real_path) else: - logging.debug("[!] File Not Found %s" % real_path) + ql.log.debug("File Not Found %s" % real_path) return regreturn @@ -104,7 +102,6 @@ def ql_syscall_fcntl(ql, fcntl_fd, fcntl_cmd, *args, **kw): elif fcntl_cmd == F_SETFL: regreturn = 0 - logging.info("fcntl(%d, %d) = %d" % (fcntl_fd, fcntl_cmd, regreturn)) return regreturn @@ -118,7 +115,8 @@ def ql_syscall_fcntl64(ql, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw): if fcntl_cmd == F_GETFL: regreturn = 2 elif fcntl_cmd == F_SETFL: - ql.os.fd[fcntl_fd].fcntl(fcntl_cmd, fcntl_arg) + if isinstance(ql.os.fd[fcntl_fd], ql_socket): + ql.os.fd[fcntl_fd].fcntl(fcntl_cmd, fcntl_arg) regreturn = 0 elif fcntl_cmd == F_GETFD: regreturn = 2 @@ -127,12 +125,10 @@ def ql_syscall_fcntl64(ql, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw): else: regreturn = 0 - logging.info("fcntl64(%d, %d, %d) = %d" % (fcntl_fd, fcntl_cmd, fcntl_arg, regreturn)) return regreturn def ql_syscall_flock(ql, flock_fd, flock_operation, *args, **kw): # Should always return 0, we don't need a actual file lock regreturn = 0 - logging.info("flock(%d) = %d" % (flock_operation, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/futex.py b/qiling/os/posix/syscall/futex.py index cc96382f8..28bf55232 100644 --- a/qiling/os/posix/syscall/futex.py +++ b/qiling/os/posix/syscall/futex.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -17,7 +17,6 @@ def ql_syscall_set_robust_list(ql, set_robust_list_head_ptr, set_robust_list_hea ql.os.thread_management.cur_thread.robust_list_head_ptr = set_robust_list_head_ptr ql.os.thread_management.cur_thread.robust_list_head_len = set_robust_list_head_len regreturn = 0 - logging.info("set_robust_list(%x, %x) = %d"%(set_robust_list_head_ptr, set_robust_list_head_len, regreturn)) return regreturn @@ -47,13 +46,13 @@ def ql_syscall_futex(ql, futex_uaddr, futex_op, futex_val, futex_timeout, futex_ regreturn = ql.os.futexm.futex_wait(ql, futex_uaddr, ql.os.thread_management.cur_thread, futex_val) - logging.info("futex(%x, %d, %d, %x) = %d" % (futex_uaddr, futex_op, futex_val, futex_timeout, regreturn)) + ql.log.debug("futex(%x, %d, %d, %x) = %d" % (futex_uaddr, futex_op, futex_val, futex_timeout, regreturn)) elif futex_op & (FUTEX_PRIVATE_FLAG - 1) == FUTEX_WAIT_BITSET: regreturn = ql.os.futexm.futex_wait(ql, futex_uaddr, ql.os.thread_management.cur_thread, futex_val, futex_val3) - logging.info("futex(%x, %d, %d, %x, %x, %x) = %d" % (futex_uaddr, + ql.log.debug("futex(%x, %d, %d, %x, %x, %x) = %d" % (futex_uaddr, futex_op, futex_val, futex_timeout, futex_uaddr2, @@ -61,12 +60,12 @@ def ql_syscall_futex(ql, futex_uaddr, futex_op, futex_val, futex_timeout, futex_ regreturn)) elif futex_op & (FUTEX_PRIVATE_FLAG - 1) == FUTEX_WAKE: regreturn = ql.os.futexm.futex_wake(ql, futex_uaddr,ql.os.thread_management.cur_thread, futex_val) - logging.info("futex(%x, %d, %d) = %d" % (futex_uaddr, futex_op, futex_val, regreturn)) + ql.log.debug("futex(%x, %d, %d) = %d" % (futex_uaddr, futex_op, futex_val, regreturn)) elif futex_op & (FUTEX_PRIVATE_FLAG - 1) == FUTEX_WAKE_BITSET: regreturn = ql.os.futexm.futex_wake(ql, futex_uaddr,ql.os.thread_management.cur_thread, futex_val, futex_val3) - logging.info("futex(%x, %d, %d) = %d" % (futex_uaddr, futex_op, futex_val, regreturn)) + ql.log.debug("futex(%x, %d, %d) = %d" % (futex_uaddr, futex_op, futex_val, regreturn)) else: - logging.info("futex(%x, %d, %d) = ?" % (futex_uaddr, futex_op, futex_val)) + ql.log.debug("futex(%x, %d, %d) = ?" % (futex_uaddr, futex_op, futex_val)) ql.emu_stop() #ql.os.thread_management.cur_thread.stop() #ql.os.thread_management.cur_thread.stop_event = THREAD_EVENT_EXIT_GROUP_EVENT diff --git a/qiling/os/posix/syscall/ioctl.py b/qiling/os/posix/syscall/ioctl.py index b320fd51b..deef44245 100644 --- a/qiling/os/posix/syscall/ioctl.py +++ b/qiling/os/posix/syscall/ioctl.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -81,7 +81,7 @@ def ioctl(fd, cmd, arg): if isinstance(ql.os.fd[ioctl_fd], ql_socket) and (ioctl_cmd == SIOCGIFADDR or ioctl_cmd == SIOCGIFNETMASK): try: tmp_arg = ql.mem.read(ioctl_arg, 64) - logging.debug("[+] query network card : %s" % tmp_arg) + ql.log.debug("query network card : %s" % tmp_arg) data = ql.os.fd[ioctl_fd].ioctl(ioctl_cmd, bytes(tmp_arg)) ql.mem.write(ioctl_arg, data) regreturn = 0 @@ -102,5 +102,5 @@ def ioctl(fd, cmd, arg): except : regreturn = -1 - logging.info("ioctl(0x%x, 0x%x, 0x%x) = %d" % (ioctl_fd, ioctl_cmd, ioctl_arg, regreturn)) + ql.log.debug("ioctl(0x%x, 0x%x, 0x%x) = %d" % (ioctl_fd, ioctl_cmd, ioctl_arg, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/mman.py b/qiling/os/posix/syscall/mman.py index 1c9ad9e50..b46c9bd3a 100644 --- a/qiling/os/posix/syscall/mman.py +++ b/qiling/os/posix/syscall/mman.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from unicorn import ( UC_PROT_ALL, UC_PROT_EXEC, @@ -38,21 +38,17 @@ def ql_syscall_munmap(ql, munmap_addr , munmap_len, *args, **kw): munmap_len = ((munmap_len + 0x1000 - 1) // 0x1000) * 0x1000 ql.mem.unmap(munmap_addr, munmap_len) regreturn = 0 - - logging.info("munmap(0x%x, 0x%x) = %d" % (munmap_addr , munmap_len, regreturn)) return regreturn def ql_syscall_madvise(ql, *args, **kw): regreturn = 0 - logging.info("madvise() = %d" % regreturn) return regreturn def ql_syscall_mprotect(ql, mprotect_start, mprotect_len, mprotect_prot, *args, **kw): regreturn = 0 - logging.info("mprotect(0x%x, 0x%x, 0x%x) = %d" % (mprotect_start, mprotect_len, mprotect_prot, regreturn)) - logging.debug("[+] mprotect(0x%x, 0x%x, %s) = %d" % ( + ql.log.debug("mprotect(0x%x, 0x%x, %s) = %d" % ( mprotect_start, mprotect_len, mmap_prot_mapping(mprotect_prot), regreturn)) return regreturn @@ -66,9 +62,9 @@ def ql_syscall_old_mmap(ql, struct_mmap_args, *args, **kw): _struct.append(int.from_bytes(data, 'little')) mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, mmap_offset = _struct - logging.debug("[+] log old_mmap - old_mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d)" % ( + ql.log.debug("log old_mmap - old_mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d)" % ( mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, mmap_offset)) - logging.debug("[+] log old_mmap - old_mmap(0x%x, 0x%x, %s, %s, %d, %d)" % ( + ql.log.debug("log old_mmap - old_mmap(0x%x, 0x%x, %s, %s, %d, %d)" % ( mmap_addr, mmap_length, mmap_prot_mapping(mmap_prot), mmap_flag_mapping(mmap_flags), mmap_fd, mmap_offset)) # FIXME @@ -97,13 +93,13 @@ def ql_syscall_old_mmap(ql, struct_mmap_args, *args, **kw): elif ql.mem.is_mapped(mmap_addr, mmap_length): need_mmap = False - logging.debug("[+] log old_mmap - return addr : " + hex(mmap_base)) - logging.debug("[+] log old_mmap - addr range : " + hex(mmap_base) + ' - ' + hex( + ql.log.debug("old_mmap - return addr : " + hex(mmap_base)) + ql.log.debug("old_mmap - addr range : " + hex(mmap_base) + ' - ' + hex( mmap_base + ((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000)) # initialized mapping if need_mmap: - logging.debug("[+] log old_mmap - mapping needed") + ql.log.debug("old_mmap - mapping needed") try: ql.mem.map(mmap_base, ((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000, info="[syscall_old_mmap]") except: @@ -119,23 +115,23 @@ def ql_syscall_old_mmap(ql, struct_mmap_args, *args, **kw): ql.os.fd[mmap_fd]._is_map_shared = True ql.os.fd[mmap_fd]._mapped_offset = mmap_offset - logging.debug("[+] log mem wirte : " + hex(len(data))) - logging.debug("[+] log mem mmap : " + mem_info) + ql.log.debug("mem wirte : " + hex(len(data))) + ql.log.debug("mem mmap : " + mem_info) ql.mem.add_mapinfo(mmap_base, mmap_base + (((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000), mem_p = UC_PROT_ALL, mem_info = "[old_mmap] " + mem_info) ql.mem.write(mmap_base, data) - logging.info("old_mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, mmap_offset, mmap_base)) + ql.log.debug("old_mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, mmap_offset, mmap_base)) regreturn = mmap_base - logging.debug("[+] mmap_base is 0x%x" % regreturn) + ql.log.debug("mmap_base is 0x%x" % regreturn) return regreturn def ql_syscall_mmap(ql, mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, mmap_pgoffset): - logging.debug("[+] log mmap - mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d)" % ( + ql.log.debug("mmap - mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d)" % ( mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, mmap_pgoffset)) - logging.debug("[+] log mmap - mmap(0x%x, 0x%x, %s, %s, %d, %d)" % ( + ql.log.debug("mmap - mmap(0x%x, 0x%x, %s, %s, %d, %d)" % ( mmap_addr, mmap_length, mmap_prot_mapping(mmap_prot), mmap_flag_mapping(mmap_flags), mmap_fd, mmap_pgoffset)) # FIXME @@ -164,19 +160,19 @@ def ql_syscall_mmap(ql, mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, elif ql.mem.is_mapped(mmap_addr, mmap_length): need_mmap = False - logging.debug("[+] log mmap - return addr : " + hex(mmap_base)) - logging.debug("[+] log mmap - addr range : " + hex(mmap_base) + ' - ' + hex( + ql.log.debug("mmap - return addr : " + hex(mmap_base)) + ql.log.debug("mmap - addr range : " + hex(mmap_base) + ' - ' + hex( mmap_base + ((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000)) # initialized mapping if need_mmap: - logging.debug("[+] log mmap - mapping needed") + ql.log.debug("mmap - mapping needed") try: ql.mem.map(mmap_base, ((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000, info="[syscall_mmap]") except: - raise QlMemoryMappedError("[!] mapping needed but fail") + raise QlMemoryMappedError("mapping needed but fail") - logging.debug("[+] mmap_base 0x%x length 0x%x" %(mmap_base, (((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000))) + ql.log.debug("mmap_base 0x%x length 0x%x" %(mmap_base, (((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000))) # FIXME: MIPS32 Big Endian try: @@ -191,16 +187,16 @@ def ql_syscall_mmap(ql, mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, ql.os.fd[mmap_fd]._is_map_shared = True ql.os.fd[mmap_fd]._mapped_offset = mmap_pgoffset - logging.debug("[+] log mem wirte : " + hex(len(data))) - logging.debug("[+] log mem mmap : " + mem_info) + ql.log.debug("mem wirte : " + hex(len(data))) + ql.log.debug("mem mmap : " + mem_info) ql.mem.add_mapinfo(mmap_base, mmap_base + (((mmap_length + 0x1000 - 1) // 0x1000) * 0x1000), mem_p = UC_PROT_ALL, mem_info = "[mmap] " + mem_info) ql.mem.write(mmap_base, data) - logging.info("mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap_addr, mmap_length, mmap_prot, mmap_flags, + ql.log.debug("mmap(0x%x, 0x%x, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap_addr, mmap_length, mmap_prot, mmap_flags, mmap_fd, mmap_pgoffset, mmap_base)) regreturn = mmap_base - logging.debug("[+] mmap_base is 0x%x" % regreturn) + ql.log.debug("mmap_base is 0x%x" % regreturn) return regreturn @@ -231,16 +227,16 @@ def ql_syscall_mmap2(ql, mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap elif mmap2_addr !=0 and ql.mem.is_mapped(mmap2_addr, mmap2_length): need_mmap = False - logging.debug("[+] log mmap2 - mmap2(0x%x, 0x%x, 0x%x, 0x%x, %d, %d)" % ( + ql.log.debug("mmap2 - mmap2(0x%x, 0x%x, 0x%x, 0x%x, %d, %d)" % ( mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap2_fd, mmap2_pgoffset)) - logging.debug("[+] log mmap2 - mmap2(0x%x, 0x%x, %s, %s, %d, %d)" % ( + ql.log.debug("mmap2 - mmap2(0x%x, 0x%x, %s, %s, %d, %d)" % ( mmap2_addr, mmap2_length, mmap_prot_mapping(mmap2_prot), mmap_flag_mapping(mmap2_flags), mmap2_fd, mmap2_pgoffset)) - logging.debug("[+] log mmap2 - return addr : " + hex(mmap_base)) - logging.debug("[+] log mmap2 - addr range : " + hex(mmap_base) + ' - ' + hex( + ql.log.debug("mmap2 - return addr : " + hex(mmap_base)) + ql.log.debug("mmap2 - addr range : " + hex(mmap_base) + ' - ' + hex( mmap_base + ((mmap2_length + 0x1000 - 1) // 0x1000) * 0x1000)) if need_mmap: - logging.debug("[+] log mmap2 - mapping needed") + ql.log.debug("mmap2 - mapping needed") try: ql.mem.map(mmap_base, ((mmap2_length + 0x1000 - 1) // 0x1000) * 0x1000, info="[syscall_mmap2]") except: @@ -256,14 +252,14 @@ def ql_syscall_mmap2(ql, mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap ql.os.fd[mmap2_fd]._is_map_shared = True ql.os.fd[mmap2_fd]._mapped_offset = mmap2_pgoffset - logging.debug("[+] log mem write : " + hex(len(data))) - logging.debug("[+] log mem mmap2 : " + mem_info) + ql.log.debug("mem write : " + hex(len(data))) + ql.log.debug("mem mmap2 : " + mem_info) ql.mem.add_mapinfo(mmap_base, mmap_base + (((mmap2_length + 0x1000 - 1) // 0x1000) * 0x1000), mem_p = UC_PROT_ALL, mem_info = "[mmap2] " + mem_info) ql.mem.write(mmap_base, data) - logging.info("mmap2(0x%x, 0x%x, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap2_fd, mmap2_pgoffset, mmap_base)) + ql.log.debug("mmap2(0x%x, 0x%x, 0x%x, 0x%x, %d, %d) = 0x%x" % (mmap2_addr, mmap2_length, mmap2_prot, mmap2_flags, mmap2_fd, mmap2_pgoffset, mmap_base)) regreturn = mmap_base - logging.debug("[+] mmap2_base is 0x%x" % regreturn) + ql.log.debug("mmap2_base is 0x%x" % regreturn) return regreturn diff --git a/qiling/os/posix/syscall/net.py b/qiling/os/posix/syscall/net.py index c4c3de79f..7e356d7e4 100644 --- a/qiling/os/posix/syscall/net.py +++ b/qiling/os/posix/syscall/net.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -36,8 +36,6 @@ def ql_syscall_socketcall(ql, socketcall_call, socketcall_args, *args, **kw): SOCKETCALL_SYS_RECVMMSG = 19 SOCKETCALL_SYS_SENDMMSG = 20 - logging.info("socketcall(%d, %x)" % (socketcall_call, socketcall_args)) - if socketcall_call == SOCKETCALL_SYS_SOCKET: socketcall_domain = ql.unpack(ql.mem.read(socketcall_args, ql.pointersize)) socketcall_type = ql.unpack(ql.mem.read(socketcall_args + ql.pointersize, ql.pointersize)) @@ -103,6 +101,6 @@ def ql_syscall_socketcall(ql, socketcall_call, socketcall_args, *args, **kw): return ql_syscall_setsockopt(ql) else: - logging.debug("[!] error call %d" % socketcall_call) + ql.log.debug("error call %d" % socketcall_call) ql.os.stop(stop_event=THREAD_EVENT_UNEXECPT_EVENT) raise diff --git a/qiling/os/posix/syscall/prctl.py b/qiling/os/posix/syscall/prctl.py index cdaba2b59..981ba871f 100644 --- a/qiling/os/posix/syscall/prctl.py +++ b/qiling/os/posix/syscall/prctl.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -16,11 +16,9 @@ def ql_syscall_arch_prctl(ql, ARCHX, ARCH_SET_FS, *args, **kw): FSMSR = 0xC0000100 ql.reg.msr(FSMSR, ARCH_SET_FS) regreturn = 0 - logging.info("arch_prctl(0x%x) = %d" % (ARCH_SET_FS, regreturn)) return regreturn def ql_syscall_prctl(ql, *args, **kw): regreturn = 0 - logging.info("prctl() = %d" % (regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/ptrace.py b/qiling/os/posix/syscall/ptrace.py index 503ae48a7..d3ff3d9a4 100644 --- a/qiling/os/posix/syscall/ptrace.py +++ b/qiling/os/posix/syscall/ptrace.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -14,5 +14,4 @@ def ql_syscall_ptrace(ql, request, pid, addr, data, *args, **kw): regreturn = 0 - logging.info("ptrace(0x%x, 0x%x, 0x%x, 0x%x) = %d" % (request, pid, addr, data, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/random.py b/qiling/os/posix/syscall/random.py index 06288a5b1..5bb361ea9 100644 --- a/qiling/os/posix/syscall/random.py +++ b/qiling/os/posix/syscall/random.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import os -import os, logging from qiling.const import * @@ -17,10 +18,7 @@ def ql_syscall_getrandom(ql, buf, buflen, flags,*args, **kw): except: regreturn = -1 - logging.info("getrandom(0x%x, 0x%x, 0x%x) = %d" % - (buf, buflen, flags, regreturn)) - if data: - logging.debug("[+] getrandom() CONTENT:") - logging.debug(str(data)) + ql.log.debug("getrandom() CONTENT:") + ql.log.debug(str(data)) return regreturn diff --git a/qiling/os/posix/syscall/resource.py b/qiling/os/posix/syscall/resource.py index 4159e47a9..ee883cdf0 100644 --- a/qiling/os/posix/syscall/resource.py +++ b/qiling/os/posix/syscall/resource.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# try: import resource @@ -14,7 +14,7 @@ def setrlimit(self, resource, rlim): pass resource = DummyResource() -import logging + from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -27,7 +27,6 @@ def ql_syscall_ugetrlimit(ql, ugetrlimit_resource, ugetrlimit_rlim, *args, **kw) rlim = resource.getrlimit(ugetrlimit_resource) ql.mem.write(ugetrlimit_rlim, ql.pack32s(rlim[0]) + ql.pack32s(rlim[1])) regreturn = 0 - logging.info("ugetrlimit(%d, 0x%x) = %d" % (ugetrlimit_resource, ugetrlimit_rlim, regreturn)) return regreturn @@ -35,9 +34,7 @@ def ql_syscall_setrlimit(ql, setrlimit_resource, setrlimit_rlim, *args, **kw): # maybe we can nop the setrlimit tmp_rlim = (ql.unpack32s(ql.mem.read(setrlimit_rlim, 4)), ql.unpack32s(ql.mem.read(setrlimit_rlim + 4, 4))) resource.setrlimit(setrlimit_resource, tmp_rlim) - regreturn = 0 - logging.info("setrlimit(%d, 0x%x) = %d" % (setrlimit_resource, setrlimit_rlim, regreturn)) return regreturn @@ -50,11 +47,9 @@ def ql_syscall_prlimit64(ql, prlimit64_pid, prlimit64_resource, prlimit64_new_li else: # set other process which pid != 0 regreturn = -1 - logging.info("prlimit64(%d, %d, 0x%x, 0x%x) = %d" % (prlimit64_pid, prlimit64_resource, prlimit64_new_limit, prlimit64_old_limit, regreturn)) return regreturn def ql_syscall_getpriority(ql, getpriority_which, getpriority_who, null1, null2, null3, null4): base = os.getpriority(getpriority_which, getpriority_who) - logging.info("getpriority(0x%x, 0x%x) = %d" % (getpriority_which, getpriority_who, base)) return base diff --git a/qiling/os/posix/syscall/sched.py b/qiling/os/posix/syscall/sched.py index 138256110..480acd1d4 100644 --- a/qiling/os/posix/syscall/sched.py +++ b/qiling/os/posix/syscall/sched.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import types, logging +import types from multiprocessing import Process + from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -72,10 +73,9 @@ def ql_syscall_clone(ql, clone_flags, clone_child_stack, clone_parent_tidptr, cl f_th.update_global_thread_id() f_th.new_thread_id() - f_th.set_thread_log_file(ql.log_dir) if clone_flags & CLONE_SETTLS == CLONE_SETTLS: - f_th.clone_thread_tls(clone_newtls) + f_th.set_thread_tls(clone_newtls) if clone_flags & CLONE_CHILD_CLEARTID == CLONE_CHILD_CLEARTID: f_th.set_clear_child_tid_addr(clone_child_tidptr) @@ -83,10 +83,10 @@ def ql_syscall_clone(ql, clone_flags, clone_child_stack, clone_parent_tidptr, cl if clone_child_stack != 0: ql.arch.set_sp(clone_child_stack) regreturn = 0 - logging.info("clone(new_stack = %x, flags = %x, tls = %x, ptidptr = %x, ctidptr = %x) = %d" % (clone_child_stack, clone_flags, clone_newtls, clone_parent_tidptr, clone_child_tidptr, regreturn)) else: regreturn = pid - logging.info("clone(new_stack = %x, flags = %x, tls = %x, ptidptr = %x, ctidptr = %x) = %d" % (clone_child_stack, clone_flags, clone_newtls, clone_parent_tidptr, clone_child_tidptr, regreturn)) + + ql.log.debug("clone(new_stack = %x, flags = %x, tls = %x, ptidptr = %x, ctidptr = %x) = %d" % (clone_child_stack, clone_flags, clone_newtls, clone_parent_tidptr, clone_child_tidptr, regreturn)) ql.emu_stop() return regreturn @@ -94,16 +94,18 @@ def ql_syscall_clone(ql, clone_flags, clone_child_stack, clone_parent_tidptr, cl if clone_flags & CLONE_CHILD_SETTID == CLONE_CHILD_SETTID: set_child_tid_addr = clone_child_tidptr - th = ql.os.thread_class.spawn(ql, set_child_tid_addr = set_child_tid_addr) + th = ql.os.thread_class.spawn(ql, ql.reg.arch_pc + 2, ql.os.exit_point, set_child_tid_addr = set_child_tid_addr) th.current_path = f_th.current_path - logging.debug(f"[thread {th.get_id()}] created.") + ql.log.debug(f"{str(th)} created.") if clone_flags & CLONE_PARENT_SETTID == CLONE_PARENT_SETTID: ql.mem.write(clone_parent_tidptr, ql.pack32(th.id)) + ctx = ql.save(reg=True, mem=False) # Whether to set a new tls if clone_flags & CLONE_SETTLS == CLONE_SETTLS: - th.clone_thread_tls(clone_newtls) + ql.log.debug(f"new_tls={hex(clone_newtls)}") + th.set_thread_tls(clone_newtls) if clone_flags & CLONE_CHILD_CLEARTID == CLONE_CHILD_CLEARTID: th.set_clear_child_tid_addr(clone_child_tidptr) @@ -111,21 +113,24 @@ def ql_syscall_clone(ql, clone_flags, clone_child_stack, clone_parent_tidptr, cl # Set the stack and return value of the new thread # (the return value of the child thread is 0, and the return value of the parent thread is the tid of the child thread) # and save the current context. - f_sp = ql.arch.get_sp() - regreturn = 0 - ql.arch.set_sp(clone_child_stack) - th.save_context() + ql.reg.arch_sp = clone_child_stack + + # We have to find next pc manually for some archs since the pc is current instruction (like `syscall`). + if ql.archtype in (QL_ARCH.X8664, ): + ql.reg.arch_pc += list(ql.disassembler.disasm_lite(bytes(ql.mem.read(ql.reg.arch_pc, 4)), ql.reg.arch_pc))[0][1] + ql.log.debug(f"Fix pc for child thread to {hex(ql.reg.arch_pc)}") + ql.os.set_syscall_return(0) + th.save() if th is None or f_th is None: raise Exception() - ql.os.thread_management.cur_thread = th - logging.debug("[+] Currently running pid is: %d; tid is: %d " % ( + ql.log.debug("Currently running pid is: %d; tid is: %d " % ( os.getpid(), ql.os.thread_management.cur_thread.id)) - logging.info("clone(new_stack = %x, flags = %x, tls = %x, ptidptr = %x, ctidptr = %x) = %d" % ( + ql.log.debug("clone(new_stack = %x, flags = %x, tls = %x, ptidptr = %x, ctidptr = %x) = %d" % ( clone_child_stack, clone_flags, clone_newtls, clone_parent_tidptr, clone_child_tidptr, regreturn)) # Restore the stack and return value of the parent process - ql.arch.set_sp(f_sp) + ql.restore(ctx) regreturn = th.id # Break the parent process and enter the add new thread event @@ -133,10 +138,9 @@ def ql_syscall_clone(ql, clone_flags, clone_child_stack, clone_parent_tidptr, cl f_th.stop_event = THREAD_EVENT_CREATE_THREAD f_th.stop_return_val = th - ql.os.thread_management.cur_thread = f_th - logging.debug("[+] Currently running pid is: %d; tid is: %d " % ( + ql.log.debug("Currently running pid is: %d; tid is: %d " % ( os.getpid(), ql.os.thread_management.cur_thread.id)) - logging.info("clone(new_stack = %x, flags = %x, tls = %x, ptidptr = %x, ctidptr = %x) = %d" % ( + ql.log.debug("clone(new_stack = %x, flags = %x, tls = %x, ptidptr = %x, ctidptr = %x) = %d" % ( clone_child_stack, clone_flags, clone_newtls, clone_parent_tidptr, clone_child_tidptr, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/select.py b/qiling/os/posix/syscall/select.py index 364335149..35eb6cda7 100644 --- a/qiling/os/posix/syscall/select.py +++ b/qiling/os/posix/syscall/select.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import select -import select, logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -61,7 +62,7 @@ def set_fd_set(buf, idx): if _newselect_readfds != 0: tmp_buf = b'\x00' * (_newselect_nfds // 8 + 1) for i in ans[0]: - logging.debug("debug : " + str(tmp_r_map[i])) + ql.log.debug("debug : " + str(tmp_r_map[i])) tmp_buf = set_fd_set(tmp_buf, tmp_r_map[i]) ql.mem.write(_newselect_readfds, tmp_buf) @@ -81,5 +82,4 @@ def set_fd_set(buf, idx): except: if ql.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP): raise - logging.info("_newselect(%d, %x, %x, %x, %x) = %d" % (_newselect_nfds, _newselect_readfds, _newselect_writefds, _newselect_exceptfds, _newselect_timeout, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/sendfile.py b/qiling/os/posix/syscall/sendfile.py index e0ca89310..76f1bbc68 100644 --- a/qiling/os/posix/syscall/sendfile.py +++ b/qiling/os/posix/syscall/sendfile.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -19,6 +19,4 @@ def ql_syscall_sendfile64(ql, sendfile64_out_fd, sendfile64_in_fd, sendfile64_of regreturn = ql.os.fd[sendfile64_out_fd].write(buf) else: regreturn = -1 - - logging.info("sendfile64(%d, %d, %x, %d) = %d" % (sendfile64_out_fd, sendfile64_in_fd, sendfile64_offest, sendfile64_count, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/signal.py b/qiling/os/posix/syscall/signal.py index 1b1297af0..dba137da2 100644 --- a/qiling/os/posix/syscall/signal.py +++ b/qiling/os/posix/syscall/signal.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -29,7 +29,6 @@ def ql_syscall_rt_sigaction(ql, rt_sigaction_signum, rt_sigaction_act, rt_sigact ql.os.sigaction_act[rt_sigaction_signum] = data regreturn = 0 - logging.info("rt_sigaction(0x%x, 0x%x, = 0x%x) = %d" % (rt_sigaction_signum, rt_sigaction_act, rt_sigaction_oldact, regreturn)) return regreturn @@ -41,11 +40,9 @@ def ql_syscall_rt_sigprocmask(ql, rt_sigprocmask_how, rt_sigprocmask_nset, rt_si pass regreturn = 0 - logging.info("rt_sigprocmask(0x%x, 0x%x, 0x%x, 0x%x) = %d" % (rt_sigprocmask_how, rt_sigprocmask_nset, rt_sigprocmask_oset, rt_sigprocmask_sigsetsize, regreturn)) return regreturn def ql_syscall_signal(ql, sig, __sighandler_t, *args, **kw): regreturn = 0 - logging.info("signal(%d, 0x%x) = %d" % (sig, __sighandler_t,regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/socket.py b/qiling/os/posix/syscall/socket.py index 829c296c9..777a7214f 100644 --- a/qiling/os/posix/syscall/socket.py +++ b/qiling/os/posix/syscall/socket.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import struct, ipaddress -import struct, ipaddress, logging from qiling.const import * from qiling.os.linux.thread import * @@ -42,11 +43,9 @@ def ql_syscall_socket(ql, socket_domain, socket_type, socket_protocol, *args, ** except: regreturn = -1 - logging.info("socket(%d, %d, %d) = %d" % (socket_domain, socket_type, socket_protocol, regreturn)) - socket_type = socket_type_mapping(socket_type, ql.archtype) socket_domain = socket_domain_mapping(socket_domain, ql.archtype) - logging.debug("[+] socket(%s, %s, %s) = %d" % (socket_domain, socket_type, socket_protocol, regreturn)) + ql.log.debug("socket(%s, %s, %s) = %d" % (socket_domain, socket_type, socket_protocol, regreturn)) return regreturn @@ -80,17 +79,16 @@ def ql_syscall_connect(ql, connect_sockfd, connect_addr, connect_addrlen, *args, regreturn = -1 if s.family == AF_UNIX: - logging.info("connect(%s) = %d" % (sun_path, regreturn)) + ql.log.debug("connect(%s) = %d" % (sun_path, regreturn)) elif s.family == AF_INET: - logging.info("connect(%s, %d) = %d" % (ip, port, regreturn)) + ql.log.debug("connect(%s, %d) = %d" % (ip, port, regreturn)) else: - logging.info("connect() = %d" % (regreturn)) + ql.log.debug("connect() = %d" % (regreturn)) return regreturn def ql_syscall_setsockopt(ql, *args, **kw): regreturn = 0 - logging.info("setsockopt() = %d" % (regreturn)) return regreturn @@ -101,7 +99,6 @@ def ql_syscall_shutdown(ql, shutdown_fd, shutdown_how, *args, **kw): regreturn = 0 except: regreturn = -1 - logging.info("shutdown(%d, %d) = %d" % (shutdown_fd, shutdown_how, regreturn)) return regreturn @@ -123,7 +120,7 @@ def ql_syscall_bind(ql, bind_fd, bind_addr, bind_addrlen, *args, **kw): if sin_family == 1: path = data[2 : ].split(b'\x00')[0] path = ql.os.transform_to_real_path(path.decode()) - logging.info(path) + ql.log.info(path) ql.os.fd[bind_fd].bind(path) # need a proper fix, for now ipv4 comes first @@ -142,14 +139,14 @@ def ql_syscall_bind(ql, bind_fd, bind_addr, bind_addrlen, *args, **kw): else: regreturn = -1 - if ql.shellcoder: + if ql.code: regreturn = 0 if sin_family == 1: - logging.info("bind(%d, %s, %d) = %d" % (bind_fd, path, bind_addrlen, regreturn)) + ql.log.debug("bind(%d, %s, %d) = %d" % (bind_fd, path, bind_addrlen, regreturn)) else: - logging.info("bind(%d,%s:%d,%d) = %d" % (bind_fd, host, port, bind_addrlen,regreturn)) - logging.debug("[+] syscall bind host: %s and port: %i sin_family: %i" % (ql_bin_to_ip(host), port, sin_family)) + ql.log.debug("bind(%d,%s:%d,%d) = %d" % (bind_fd, host, port, bind_addrlen,regreturn)) + ql.log.debug("syscall bind host: %s and port: %i sin_family: %i" % (ql_bin_to_ip(host), port, sin_family)) return regreturn @@ -168,7 +165,7 @@ def ql_syscall_getsockname(ql, sockfd, addr, addrlenptr, *args, **kw): else: regreturn = -1 - logging.info("getsockname(%d, 0x%x, 0x%x) = %d" % (sockfd, addr, addrlenptr, regreturn)) + ql.log.debug("getsockname(%d, 0x%x, 0x%x) = %d" % (sockfd, addr, addrlenptr, regreturn)) return regreturn @@ -186,7 +183,7 @@ def ql_syscall_getpeername(ql, sockfd, addr, addrlenptr, *args, **kw): else: regreturn = -1 - logging.info("getpeername(%d, 0x%x, 0x%x) = %d" % (sockfd, addr, addrlenptr, regreturn)) + ql.log.debug("getpeername(%d, 0x%x, 0x%x) = %d" % (sockfd, addr, addrlenptr, regreturn)) return regreturn @@ -201,8 +198,6 @@ def ql_syscall_listen(ql, listen_sockfd, listen_backlog, *args, **kw): regreturn = -1 else: regreturn = -1 - - logging.info("listen(%d, %d) = %d" % (listen_sockfd, listen_backlog, regreturn)) return regreturn @@ -231,7 +226,7 @@ def inet_addr(ip): ql.os.fd[idx] = conn regreturn = idx - if ql.shellcoder == None and accept_addr !=0 and accept_addrlen != 0: + if ql.code == None and accept_addr !=0 and accept_addrlen != 0: tmp_buf = ql.pack16(conn.family) tmp_buf += ql.pack16(address[1]) tmp_buf += inet_addr(address[0]) @@ -243,7 +238,6 @@ def inet_addr(ip): raise regreturn = -1 - logging.info("accept(%d, %x, %x) = %d" %(accept_sockfd, accept_addr, accept_addrlen, regreturn)) return regreturn @@ -251,14 +245,12 @@ def ql_syscall_recv(ql, recv_sockfd, recv_buf, recv_len, recv_flags, *args, **kw if recv_sockfd < 256 and ql.os.fd[recv_sockfd] != 0: tmp_buf = ql.os.fd[recv_sockfd].recv(recv_len, recv_flags) if tmp_buf: - logging.debug("[+] recv() CONTENT:") - logging.debug("%s" % tmp_buf) + ql.log.debug("recv() CONTENT:") + ql.log.debug("%s" % tmp_buf) ql.mem.write(recv_buf, tmp_buf) regreturn = len(tmp_buf) else: regreturn = -1 - - logging.info("recv(%d, %x, %d, %x) = %d" % (recv_sockfd, recv_buf, recv_len, recv_flags, regreturn)) return regreturn @@ -266,23 +258,21 @@ def ql_syscall_send(ql, send_sockfd, send_buf, send_len, send_flags, *args, **kw regreturn = 0 if send_sockfd < 256 and ql.os.fd[send_sockfd] != 0: try: - logging.debug("[+] debug send() start") + ql.log.debug("debug send() start") tmp_buf = ql.mem.read(send_buf, send_len) - logging.debug("[+] fd is " + str(send_sockfd)) - logging.debug("[+] send() CONTENT:") - logging.debug("%s" % str(tmp_buf)) - logging.debug("[+] send() flag is " + str(send_flags)) - logging.debug("[+] send() len is " + str(send_len)) + ql.log.debug("fd is " + str(send_sockfd)) + ql.log.debug("send() CONTENT:") + ql.log.debug("%s" % str(tmp_buf)) + ql.log.debug("send() flag is " + str(send_flags)) + ql.log.debug("send() len is " + str(send_len)) regreturn = ql.os.fd[send_sockfd].send(bytes(tmp_buf), send_flags) - logging.debug("[+] debug send end") + ql.log.debug("debug send end") except: - logging.info(sys.exc_info()[0]) + ql.log.info(sys.exc_info()[0]) if ql.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP): raise else: regreturn = -1 - - logging.info("send(%d, %x, %d, %x) = %d" % (send_sockfd, send_buf, send_len, send_flags, regreturn)) return regreturn @@ -295,16 +285,16 @@ def ql_syscall_recvfrom(ql, recvfrom_sockfd, recvfrom_buf, recvfrom_len, recvfro if recvfrom_sockfd < 256 and ql.os.fd[recvfrom_sockfd] != 0: tmp_buf, tmp_addr = ql.os.fd[recvfrom_sockfd].recvfrom(recvfrom_len, recvfrom_flags) if tmp_buf: - logging.debug("[+] recvfrom() CONTENT:") - logging.debug("%s" % tmp_buf) + ql.log.debug("recvfrom() CONTENT:") + ql.log.debug("%s" % tmp_buf) sin_family = int(ql.os.fd[recvfrom_sockfd].family) data = struct.pack("H", tmp_addr[1]) data += ipaddress.ip_address(tmp_addr[0]).packed addrlen = ql.unpack(ql.mem.read(recvfrom_addrlen, ql.pointersize)) @@ -316,7 +306,6 @@ def ql_syscall_recvfrom(ql, recvfrom_sockfd, recvfrom_buf, recvfrom_len, recvfro else: regreturn = -1 - logging.info("recvfrom(%d, %#x, %d, %#x, %#x, %#x) = %d" % (recvfrom_sockfd, recvfrom_buf, recvfrom_len, recvfrom_flags, recvfrom_addr, recvfrom_addrlen, regreturn)) return regreturn @@ -329,7 +318,7 @@ def ql_syscall_sendto(ql, sendto_sockfd, sendto_buf, sendto_len, sendto_flags, s regreturn = 0 if sendto_sockfd < 256 and ql.os.fd[sendto_sockfd] != 0: try: - logging.debug("[+] debug sendto() start") + ql.log.debug("debug sendto() start") tmp_buf = ql.mem.read(sendto_buf, sendto_len) if ql.archtype== QL_ARCH.X8664: @@ -345,24 +334,23 @@ def ql_syscall_sendto(ql, sendto_sockfd, sendto_buf, sendto_len, sendto_flags, s path = data[2 : ].split(b'\x00')[0] path = ql.os.transform_to_real_path(path.decode()) - logging.debug("[+] fd is " + str(sendto_sockfd)) - logging.debug("[+] sendto() CONTENT:") - logging.debug("%s" % tmp_buf) - logging.debug("[+] sendto() flag is " + str(sendto_flags)) - logging.debug("[+] sendto() len is " + str(sendto_len)) + ql.log.debug("fd is " + str(sendto_sockfd)) + ql.log.debug("sendto() CONTENT:") + ql.log.debug("%s" % tmp_buf) + ql.log.debug("sendto() flag is " + str(sendto_flags)) + ql.log.debug("sendto() len is " + str(sendto_len)) if sin_family == 1: - logging.debug("[+] sendto() path is " + str(path)) + ql.log.debug("sendto() path is " + str(path)) regreturn = ql.os.fd[sendto_sockfd].sendto(bytes(tmp_buf), sendto_flags, path) else: - logging.debug("[+] sendto() addr is %s:%d" % (host, port)) + ql.log.debug("sendto() addr is %s:%d" % (host, port)) regreturn = ql.os.fd[sendto_sockfd].sendto(bytes(tmp_buf), sendto_flags, (host, port)) - logging.debug("[+] debug sendto end") + ql.log.debug("debug sendto end") except: - logging.info(sys.exc_info()[0]) + ql.log.info(sys.exc_info()[0]) if ql.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP): raise else: regreturn = -1 - logging.info("sendto(%d, %#x, %d, %#x, %#x, %#x) = %d" % (sendto_sockfd, sendto_buf, sendto_len, sendto_flags, sendto_addr, sendto_addrlen, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/stat.py b/qiling/os/posix/syscall/stat.py index 153fa5f66..c8e913c4b 100644 --- a/qiling/os/posix/syscall/stat.py +++ b/qiling/os/posix/syscall/stat.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -13,10 +13,243 @@ from qiling.exception import * from qiling.os.stat import * + +def create_stat_struct(ql, info): + if ql.archtype == QL_ARCH.MIPS: + # pack fstatinfo + stat_buf = ql.pack32(info.st_dev) + stat_buf += ql.pack32(0) * 3 + stat_buf += ql.pack32(info.st_ino) + stat_buf += ql.pack32(info.st_mode) + stat_buf += ql.pack32(info.st_nlink) + stat_buf += ql.pack32(info.st_uid) + stat_buf += ql.pack32(info.st_gid) + stat_buf += ql.pack32(info.st_rdev) + stat_buf += ql.pack32(0) * 2 + stat_buf += ql.pack32(info.st_size) + stat_buf += ql.pack32(0) + stat_buf += ql.pack32(int(info.st_atime)) + stat_buf += ql.pack32(0) + stat_buf += ql.pack32(int(info.st_mtime)) + stat_buf += ql.pack32(0) + stat_buf += ql.pack32(int(info.st_ctime)) + stat_buf += ql.pack32(0) + stat_buf += ql.pack32(info.st_blksize) + stat_buf += ql.pack32(info.st_blocks) + stat_buf = stat_buf.ljust(0x90, b'\x00') + elif ql.archtype == QL_ARCH.X8664: + if ql.platform == QL_OS.MACOS: + stat_buf = ql.pack64s(info.st_dev) + else: + stat_buf = ql.pack64(info.st_dev) + stat_buf += ql.pack(info.st_ino) + stat_buf += ql.pack64(info.st_nlink) + stat_buf += ql.pack32(info.st_mode) + stat_buf += ql.pack32(info.st_uid) + stat_buf += ql.pack32(info.st_gid) + stat_buf += ql.pack32(0) + stat_buf += ql.pack64(info.st_rdev) + stat_buf += ql.pack64(info.st_size) + stat_buf += ql.pack64(info.st_blksize) + stat_buf += ql.pack64(info.st_blocks) + stat_buf += ql.pack64(int(info.st_atime)) + stat_buf += ql.pack64(0) + stat_buf += ql.pack64(int(info.st_mtime)) + stat_buf += ql.pack64(0) + stat_buf += ql.pack64(int(info.st_ctime)) + stat_buf += ql.pack64(0) + elif ql.archtype == QL_ARCH.ARM64: + # struct stat is : 80 addr is : 0x4000811bc8 + # buf.st_dev offest 0 8 0 + # buf.st_ino offest 8 8 0 + # buf.st_mode offest 10 4 0 + # buf.st_nlink offest 14 4 0 + # buf.st_uid offest 18 4 0 + # buf.st_gid offest 1c 4 0 + # buf.st_rdev offest 20 8 0 + # buf.st_size offest 30 8 274886889936 + # buf.st_blksize offest 38 4 8461328 + # buf.st_blocks offest 40 8 274877909532 + # buf.st_atime offest 48 8 274886368336 + # buf.st_mtime offest 58 8 274877909472 + # buf.st_ctime offest 68 8 274886368336 + # buf.__glibc_reserved offest 78 8 + if ql.platform == QL_OS.MACOS: + stat_buf = ql.pack64s(info.st_dev) + else: + stat_buf = ql.pack64(info.st_dev) + stat_buf += ql.pack64(info.st_ino) + stat_buf += ql.pack32(info.st_mode) + stat_buf += ql.pack32(info.st_nlink) + stat_buf += ql.pack32(ql.os.uid) + stat_buf += ql.pack32(ql.os.gid) + stat_buf += ql.pack64(info.st_rdev) + stat_buf += ql.pack64(0) + stat_buf += ql.pack64(info.st_size) + stat_buf += ql.pack32(info.st_blksize) + stat_buf += ql.pack32(0) + stat_buf += ql.pack64(info.st_blocks) + stat_buf += ql.pack64(int(info.st_atime)) + stat_buf += ql.pack64(0) + stat_buf += ql.pack64(int(info.st_mtime)) + stat_buf += ql.pack64(0) + stat_buf += ql.pack64(int(info.st_ctime)) + stat_buf += ql.pack64(0) + else: + # pack fstatinfo + stat_buf = ql.pack32(info.st_dev) + stat_buf += ql.pack(info.st_ino) + stat_buf += ql.pack32(info.st_mode) + stat_buf += ql.pack32(info.st_nlink) + stat_buf += ql.pack32(info.st_uid) + stat_buf += ql.pack32(info.st_gid) + stat_buf += ql.pack32(info.st_rdev) + stat_buf += ql.pack32(info.st_size) + stat_buf += ql.pack32(info.st_blksize) + stat_buf += ql.pack32(info.st_blocks) + stat_buf += ql.pack32(int(info.st_atime)) + stat_buf += ql.pack32(int(info.st_mtime)) + stat_buf += ql.pack32(int(info.st_ctime)) + return stat_buf + + +def create_stat64_struct(ql, info): + if ql.archtype == QL_ARCH.ARM64: + # struct stat is : 80 addr is : 0x4000811bc8 + # buf.st_dev offest 0 8 0 + # buf.st_ino offest 8 8 0 + # buf.st_mode offest 10 4 0 + # buf.st_nlink offest 14 4 0 + # buf.st_uid offest 18 4 0 + # buf.st_gid offest 1c 4 0 + # buf.st_rdev offest 20 8 0 + # buf.st_size offest 30 8 274886889936 + # buf.st_blksize offest 38 4 8461328 + # buf.st_blocks offest 40 8 274877909532 + # buf.st_atime offest 48 8 274886368336 + # buf.st_mtime offest 58 8 274877909472 + # buf.st_ctime offest 68 8 274886368336 + # buf.__glibc_reserved offest 78 8 + fstat64_buf = ql.pack64(info.st_dev) # 8 + fstat64_buf += ql.pack64(info.st_ino) # 16 + fstat64_buf += ql.pack32(info.st_mode) # 20 + fstat64_buf += ql.pack32(info.st_nlink) # 24 + fstat64_buf += ql.pack32(ql.os.uid) # 28 + fstat64_buf += ql.pack32(ql.os.gid) # 32 + fstat64_buf += ql.pack64(info.st_rdev) # 40 + fstat64_buf += ql.pack64(0) # 48 + fstat64_buf += ql.pack64(info.st_size) # 56 + fstat64_buf += ql.pack32(info.st_blksize) # 60 + fstat64_buf += ql.pack32(0) # 64 + fstat64_buf += ql.pack64(info.st_blocks) # 72 + fstat64_buf += ql.pack64(int(info.st_atime)) # 80 + fstat64_buf += ql.pack64(0) # 88 + fstat64_buf += ql.pack64(int(info.st_mtime)) # 96 + fstat64_buf += ql.pack64(0) # 104 + fstat64_buf += ql.pack64(int(info.st_ctime)) # 114 + fstat64_buf += ql.pack64(0) # 120 + elif ql.archtype == QL_ARCH.MIPS: + # struct stat is : a0 addr is : 0x7fffedc0 + # buf.st_dev offest 0 4 2049 + # buf.st_ino offest 10 8 2400362 + # buf.st_mode offest 18 4 16893 + # buf.st_nlink offest 1c 4 5 + # buf.st_uid offest 20 4 1000 + # buf.st_gid offest 24 4 1000 + # buf.st_rdev offest 28 4 0 + # buf.st_size offest 38 8 0 + # buf.st_blksize offest 58 4 4096 + # buf.st_blocks offest 60 8 136 + # buf.st_atime offest 40 4 1586616689 + # buf.st_mtime offest 48 4 1586616689 + # buf.st_ctime offest 50 4 1586616689 + if ql.platform == QL_OS.MACOS: + fstat64_buf = ql.pack32s(info.st_dev) + else: + fstat64_buf = ql.pack32(info.st_dev) # 4 + fstat64_buf += b'\x00' * 12 # 16 + fstat64_buf += ql.pack64(info.st_ino) # 24 + fstat64_buf += ql.pack32(info.st_mode) + fstat64_buf += ql.pack32(info.st_nlink) + fstat64_buf += ql.pack32(ql.os.uid) + fstat64_buf += ql.pack32(ql.os.gid) + fstat64_buf += ql.pack32(info.st_rdev) + fstat64_buf += b'\x00' * 12 + fstat64_buf += ql.pack64(info.st_size) + fstat64_buf += ql.pack64(int(info.st_atime)) + fstat64_buf += ql.pack64(0) + fstat64_buf += ql.pack64(int(info.st_mtime)) + fstat64_buf += ql.pack64(0) + fstat64_buf += ql.pack64(int(info.st_ctime)) + fstat64_buf += ql.pack64(0) + fstat64_buf += ql.pack32(info.st_blksize) + fstat64_buf += ql.pack32(0) + fstat64_buf += ql.pack64(info.st_blocks) + elif ql.archtype == QL_ARCH.ARM: + # pack fstatinfo + if ql.platform == QL_OS.MACOS: + fstat64_buf = ql.pack64s(info.st_dev) + else: + fstat64_buf = ql.pack64(info.st_dev) + fstat64_buf += ql.pack32(0) + fstat64_buf += ql.pack32(info.st_ino) + fstat64_buf += ql.pack32(info.st_mode) + fstat64_buf += ql.pack32(info.st_nlink) + fstat64_buf += ql.pack32(info.st_uid) + fstat64_buf += ql.pack32(info.st_gid) + fstat64_buf += ql.pack64(info.st_rdev) # ?? fstat_info.st_rdev + fstat64_buf += ql.pack64(0) + fstat64_buf += ql.pack64(info.st_size) + fstat64_buf += ql.pack64(info.st_blksize) # ?? fstat_info.st_blksize + fstat64_buf += ql.pack64(info.st_blocks) # ?? fstat_info.st_blocks + fstat64_buf += ql.pack64(int(info.st_atime)) + fstat64_buf += ql.pack64(int(info.st_mtime)) + fstat64_buf += ql.pack64(int(info.st_ctime)) + fstat64_buf += ql.pack64(info.st_ino) + + else: + # pack fstatinfo + if ql.platform == QL_OS.MACOS: + fstat64_buf = ql.pack64s(info.st_dev) + else: + fstat64_buf = ql.pack64(info.st_dev) + fstat64_buf += ql.pack64(0x0000000300c30000) + fstat64_buf += ql.pack32(info.st_mode) + fstat64_buf += ql.pack32(info.st_nlink) + fstat64_buf += ql.pack32(info.st_uid) + fstat64_buf += ql.pack32(info.st_gid) + fstat64_buf += ql.pack64(0x0000000000008800) # ?? fstat_info.st_rdev + fstat64_buf += ql.pack32(0xffffd257) + fstat64_buf += ql.pack64(info.st_size) + fstat64_buf += ql.pack32(0x00000400) # ?? fstat_info.st_blksize + fstat64_buf += ql.pack64(0x0000000000000000) # ?? fstat_info.st_blocks + fstat64_buf += ql.pack64(int(info.st_atime)) + fstat64_buf += ql.pack64(int(info.st_mtime)) + fstat64_buf += ql.pack64(int(info.st_ctime)) + fstat64_buf += ql.pack64(info.st_ino) + return fstat64_buf + + +def statFamily(ql, path, ptr, name, stat_func, struct_func): + file = (ql.mem.string(path)) + real_path = ql.os.transform_to_real_path(file) + regreturn = 0 + try: + info = stat_func(real_path) + except OSError as e: + ql.log.debug(f'{name}("{file}", {hex(ptr)}) read/write fail') + return -e.errno + else: + buf = struct_func(ql, info) + ql.mem.write(ptr, buf) + ql.log.debug(f'{name}("{file}", {hex(ptr)}) write completed') + return regreturn + + def ql_syscall_chmod(ql, filename, mode, null1, null2, null3, null4): regreturn = 0 filename = ql.mem.string(filename) - logging.info("chmod(%s,%d) = %d" % (filename, mode, regreturn)) + ql.log.debug("chmod(%s,%d) = %d" % (filename, mode, regreturn)) return regreturn @@ -63,15 +296,13 @@ def ql_syscall_fstatat64(ql, fstatat64_fd, fstatat64_fname, fstatat64_buf, fstat fstat64_buf += ql.pack64(0) fstat64_buf += ql.pack64(int(fstat64_info.st_ctime)) fstat64_buf += ql.pack64(0) - ql.mem.write(fstatat64_buf,fstat64_buf) + ql.mem.write(fstatat64_buf, fstat64_buf) regreturn = 0 - logging.info("fstatat64(0x%x, %s) = %d" % (fstatat64_fd, relative_path, regreturn)) - if regreturn == 0: - logging.debug("[+] Directory Found: %s" % relative_path) + ql.log.debug("Directory Found: %s" % relative_path) else: - logging.debug("[!] Directory Not Found: %s" % relative_path) + ql.log.debug("Directory Not Found: %s" % relative_path) return regreturn @@ -84,447 +315,59 @@ def ql_syscall_fstat64(ql, fstat64_fd, fstat64_add, *args, **kw): elif fstat64_fd < 256 and ql.os.fd[fstat64_fd] != 0: user_fileno = fstat64_fd fstat64_info = ql.os.fd[user_fileno].fstat() - - if ql.archtype== QL_ARCH.ARM64: - # struct stat is : 80 addr is : 0x4000811bc8 - # buf.st_dev offest 0 8 0 - # buf.st_ino offest 8 8 0 - # buf.st_mode offest 10 4 0 - # buf.st_nlink offest 14 4 0 - # buf.st_uid offest 18 4 0 - # buf.st_gid offest 1c 4 0 - # buf.st_rdev offest 20 8 0 - # buf.st_size offest 30 8 274886889936 - # buf.st_blksize offest 38 4 8461328 - # buf.st_blocks offest 40 8 274877909532 - # buf.st_atime offest 48 8 274886368336 - # buf.st_mtime offest 58 8 274877909472 - # buf.st_ctime offest 68 8 274886368336 - # buf.__glibc_reserved offest 78 8 - fstat64_buf = ql.pack64(fstat64_info.st_dev) - fstat64_buf += ql.pack64(fstat64_info.st_ino) - fstat64_buf += ql.pack32(fstat64_info.st_mode) - fstat64_buf += ql.pack32(fstat64_info.st_nlink) - fstat64_buf += ql.pack32(ql.os.uid) - fstat64_buf += ql.pack32(ql.os.gid) - fstat64_buf += ql.pack64(fstat64_info.st_rdev) - fstat64_buf += ql.pack64(0) - fstat64_buf += ql.pack64(fstat64_info.st_size) - fstat64_buf += ql.pack32(fstat64_info.st_blksize) - fstat64_buf += ql.pack32(0) - fstat64_buf += ql.pack64(fstat64_info.st_blocks) - fstat64_buf += ql.pack64(int(fstat64_info.st_atime)) - fstat64_buf += ql.pack64(0) - fstat64_buf += ql.pack64(int(fstat64_info.st_mtime)) - fstat64_buf += ql.pack64(0) - fstat64_buf += ql.pack64(int(fstat64_info.st_ctime)) - fstat64_buf += ql.pack64(0) - elif ql.archtype == QL_ARCH.MIPS: - # struct stat is : a0 addr is : 0x7fffedc0 - # buf.st_dev offest 0 4 2049 - # buf.st_ino offest 10 8 2400362 - # buf.st_mode offest 18 4 16893 - # buf.st_nlink offest 1c 4 5 - # buf.st_uid offest 20 4 1000 - # buf.st_gid offest 24 4 1000 - # buf.st_rdev offest 28 4 0 - # buf.st_size offest 38 8 0 - # buf.st_blksize offest 58 4 4096 - # buf.st_blocks offest 60 8 136 - # buf.st_atime offest 40 4 1586616689 - # buf.st_mtime offest 48 4 1586616689 - # buf.st_ctime offest 50 4 1586616689 - if ql.platform == QL_OS.MACOS: - fstat64_buf = ql.pack32s(fstat64_info.st_dev) - else: - fstat64_buf = ql.pack32(fstat64_info.st_dev) - fstat64_buf += b'\x00' * 12 - fstat64_buf += ql.pack64(fstat64_info.st_ino) - fstat64_buf += ql.pack32(fstat64_info.st_mode) - fstat64_buf += ql.pack32(fstat64_info.st_nlink) - fstat64_buf += ql.pack32(ql.os.uid) - fstat64_buf += ql.pack32(ql.os.gid) - fstat64_buf += ql.pack32(fstat64_info.st_rdev) - fstat64_buf += b'\x00' * 12 - fstat64_buf += ql.pack64(fstat64_info.st_size) - fstat64_buf += ql.pack64(int(fstat64_info.st_atime)) - fstat64_buf += ql.pack64(0) - fstat64_buf += ql.pack64(int(fstat64_info.st_mtime)) - fstat64_buf += ql.pack64(0) - fstat64_buf += ql.pack64(int(fstat64_info.st_ctime)) - fstat64_buf += ql.pack64(0) - fstat64_buf += ql.pack32(fstat64_info.st_blksize) - fstat64_buf += ql.pack32(0) - fstat64_buf += ql.pack64(fstat64_info.st_blocks) - elif ql.archtype == QL_ARCH.ARM: - # pack fstatinfo - if ql.platform == QL_OS.MACOS: - fstat64_buf = ql.pack64s(fstat64_info.st_dev) - else: - fstat64_buf = ql.pack64(fstat64_info.st_dev) - fstat64_buf += ql.pack32(0) - fstat64_buf += ql.pack32(fstat64_info.st_ino) - fstat64_buf += ql.pack32(fstat64_info.st_mode) - fstat64_buf += ql.pack32(fstat64_info.st_nlink) - fstat64_buf += ql.pack32(fstat64_info.st_uid) - fstat64_buf += ql.pack32(fstat64_info.st_gid) - fstat64_buf += ql.pack64(fstat64_info.st_rdev) #?? fstat_info.st_rdev - fstat64_buf += ql.pack64(0) - fstat64_buf += ql.pack64(fstat64_info.st_size) - fstat64_buf += ql.pack64(fstat64_info.st_blksize) #?? fstat_info.st_blksize - fstat64_buf += ql.pack64(fstat64_info.st_blocks) #?? fstat_info.st_blocks - fstat64_buf += ql.pack64(int(fstat64_info.st_atime)) - fstat64_buf += ql.pack64(int(fstat64_info.st_mtime)) - fstat64_buf += ql.pack64(int(fstat64_info.st_ctime)) - fstat64_buf += ql.pack64(fstat64_info.st_ino) - - else: - # pack fstatinfo - if ql.platform == QL_OS.MACOS: - fstat64_buf = ql.pack64s(fstat64_info.st_dev) - else: - fstat64_buf = ql.pack64(fstat64_info.st_dev) - fstat64_buf += ql.pack64(0x0000000300c30000) - fstat64_buf += ql.pack32(fstat64_info.st_mode) - fstat64_buf += ql.pack32(fstat64_info.st_nlink) - fstat64_buf += ql.pack32(fstat64_info.st_uid) - fstat64_buf += ql.pack32(fstat64_info.st_gid) - fstat64_buf += ql.pack64(0x0000000000008800) #?? fstat_info.st_rdev - fstat64_buf += ql.pack32(0xffffd257) - fstat64_buf += ql.pack64(fstat64_info.st_size) - fstat64_buf += ql.pack32(0x00000400) #?? fstat_info.st_blksize - fstat64_buf += ql.pack64(0x0000000000000000) #?? fstat_info.st_blocks - fstat64_buf += ql.pack64(int(fstat64_info.st_atime)) - fstat64_buf += ql.pack64(int(fstat64_info.st_mtime)) - fstat64_buf += ql.pack64(int(fstat64_info.st_ctime)) - fstat64_buf += ql.pack64(fstat64_info.st_ino) - + fstat64_buf = create_stat64_struct(ql, fstat64_info) ql.mem.write(fstat64_add, fstat64_buf) regreturn = 0 else: regreturn = -1 - logging.info("fstat64(%d, 0x%x) = %d" % (fstat64_fd, fstat64_add, regreturn)) if regreturn == 0: - logging.debug("[+] fstat64 write completed") + ql.log.debug("fstat64 write completed") else: - logging.debug("[!] fstat64 read/write fail") + ql.log.debug("fstat64 read/write fail") return regreturn def ql_syscall_fstat(ql, fstat_fd, fstat_add, *args, **kw): - if fstat_fd < 256 and ql.os.fd[fstat_fd] != 0 and hasattr(ql.os.fd[fstat_fd], "fstat"): user_fileno = fstat_fd fstat_info = ql.os.fd[user_fileno].fstat() - - if ql.archtype== QL_ARCH.MIPS: - # pack fstatinfo - fstat_buf = ql.pack32(fstat_info.st_dev) - fstat_buf += ql.pack32(0) * 3 - fstat_buf += ql.pack32(fstat_info.st_ino) - fstat_buf += ql.pack32(fstat_info.st_mode) - fstat_buf += ql.pack32(fstat_info.st_nlink) - fstat_buf += ql.pack32(fstat_info.st_uid) - fstat_buf += ql.pack32(fstat_info.st_gid) - fstat_buf += ql.pack32(fstat_info.st_rdev) - fstat_buf += ql.pack32(0) * 2 - fstat_buf += ql.pack32(fstat_info.st_size) - fstat_buf += ql.pack32(0) - fstat_buf += ql.pack32(int(fstat_info.st_atime)) - fstat_buf += ql.pack32(0) - fstat_buf += ql.pack32(int(fstat_info.st_mtime)) - fstat_buf += ql.pack32(0) - fstat_buf += ql.pack32(int(fstat_info.st_ctime)) - fstat_buf += ql.pack32(0) - fstat_buf += ql.pack32(fstat_info.st_blksize) - fstat_buf += ql.pack32(fstat_info.st_blocks) - fstat_buf = fstat_buf.ljust(0x90, b'\x00') - elif ql.archtype== QL_ARCH.X8664: - if ql.platform == QL_OS.MACOS: - fstat_buf = ql.pack64s(fstat_info.st_dev) - else: - fstat_buf = ql.pack64(fstat_info.st_dev) - fstat_buf += ql.pack(fstat_info.st_ino) - fstat_buf += ql.pack64(fstat_info.st_nlink) - fstat_buf += ql.pack32(fstat_info.st_mode) - fstat_buf += ql.pack32(fstat_info.st_uid) - fstat_buf += ql.pack32(fstat_info.st_gid) - fstat_buf += ql.pack32(0) - fstat_buf += ql.pack64(fstat_info.st_rdev) - fstat_buf += ql.pack64(fstat_info.st_size) - fstat_buf += ql.pack64(fstat_info.st_blksize) - fstat_buf += ql.pack64(fstat_info.st_blocks) - fstat_buf += ql.pack64(int(fstat_info.st_atime)) - fstat_buf += ql.pack64(0) - fstat_buf += ql.pack64(int(fstat_info.st_mtime)) - fstat_buf += ql.pack64(0) - fstat_buf += ql.pack64(int(fstat_info.st_ctime)) - fstat_buf += ql.pack64(0) - elif ql.archtype== QL_ARCH.ARM64: - # struct stat is : 80 addr is : 0x4000811bc8 - # buf.st_dev offest 0 8 0 - # buf.st_ino offest 8 8 0 - # buf.st_mode offest 10 4 0 - # buf.st_nlink offest 14 4 0 - # buf.st_uid offest 18 4 0 - # buf.st_gid offest 1c 4 0 - # buf.st_rdev offest 20 8 0 - # buf.st_size offest 30 8 274886889936 - # buf.st_blksize offest 38 4 8461328 - # buf.st_blocks offest 40 8 274877909532 - # buf.st_atime offest 48 8 274886368336 - # buf.st_mtime offest 58 8 274877909472 - # buf.st_ctime offest 68 8 274886368336 - # buf.__glibc_reserved offest 78 8 - if ql.platform == QL_OS.MACOS: - fstat_buf = ql.pack64s(fstat_info.st_dev) - else: - fstat_buf = ql.pack64(fstat_info.st_dev) - fstat_buf += ql.pack64(fstat_info.st_ino) - fstat_buf += ql.pack32(fstat_info.st_mode) - fstat_buf += ql.pack32(fstat_info.st_nlink) - fstat_buf += ql.pack32(ql.os.uid) - fstat_buf += ql.pack32(ql.os.gid) - fstat_buf += ql.pack64(fstat_info.st_rdev) - fstat_buf += ql.pack64(0) - fstat_buf += ql.pack64(fstat_info.st_size) - fstat_buf += ql.pack32(fstat_info.st_blksize) - fstat_buf += ql.pack32(0) - fstat_buf += ql.pack64(fstat_info.st_blocks) - fstat_buf += ql.pack64(int(fstat_info.st_atime)) - fstat_buf += ql.pack64(0) - fstat_buf += ql.pack64(int(fstat_info.st_mtime)) - fstat_buf += ql.pack64(0) - fstat_buf += ql.pack64(int(fstat_info.st_ctime)) - fstat_buf += ql.pack64(0) - else: - # pack fstatinfo - fstat_buf = ql.pack32(fstat_info.st_dev) - fstat_buf += ql.pack(fstat_info.st_ino) - fstat_buf += ql.pack32(fstat_info.st_mode) - fstat_buf += ql.pack32(fstat_info.st_nlink) - fstat_buf += ql.pack32(fstat_info.st_uid) - fstat_buf += ql.pack32(fstat_info.st_gid) - fstat_buf += ql.pack32(fstat_info.st_rdev) - fstat_buf += ql.pack32(fstat_info.st_size) - fstat_buf += ql.pack32(fstat_info.st_blksize) - fstat_buf += ql.pack32(fstat_info.st_blocks) - fstat_buf += ql.pack32(int(fstat_info.st_atime)) - fstat_buf += ql.pack32(int(fstat_info.st_mtime)) - fstat_buf += ql.pack32(int(fstat_info.st_ctime)) - + fstat_buf = create_stat_struct(ql, fstat_info) ql.mem.write(fstat_add, fstat_buf) regreturn = 0 else: regreturn = -1 - logging.info("fstat(%d, 0x%x) = %d" % (fstat_fd, fstat_add, regreturn)) - if regreturn == 0: - logging.debug("[+] fstat write completed") - else: - logging.debug("[!] fstat read/write fail") - return regreturn - - -# int stat64(const char *pathname, struct stat64 *buf); -def ql_syscall_stat64(ql, stat64_pathname, stat64_buf_ptr, *args, **kw): - stat64_file = (ql.mem.string(stat64_pathname)) - - real_path = ql.os.transform_to_real_path(stat64_file) - relative_path = ql.os.transform_to_relative_path(stat64_file) - if os.path.exists(real_path) == False: - regreturn = -1 - else: - stat64_info = Stat(real_path) - - if ql.archtype== QL_ARCH.MIPS: - # packfstatinfo - # name offset size - # struct stat is : a0 - # buf.st_dev offest 0 4 - # buf.st_ino offest 10 8 - # buf.st_mode offest 18 4 - # buf.st_nlink offest 1c 4 - # buf.st_uid offest 20 4 - # buf.st_gid offest 24 4 - # buf.st_rdev offest 28 4 - # buf.st_size offest 38 8 - # buf.st_blksize offest 58 4 - # buf.st_blocks offest 60 8 - # buf.st_atime offest 40 4 - # buf.st_mtime offest 48 4 - # buf.st_ctime offest 50 4 - stat64_buf = ql.pack32(stat64_info.st_dev) - stat64_buf += ql.pack32(0) * 3 - stat64_buf += ql.pack64(stat64_info.st_ino) - stat64_buf += ql.pack32(stat64_info.st_mode) - stat64_buf += ql.pack32(stat64_info.st_nlink) - stat64_buf += ql.pack32(1000) - stat64_buf += ql.pack32(1000) - stat64_buf += ql.pack32(stat64_info.st_rdev) - stat64_buf += ql.pack32(0) * 3 - stat64_buf += ql.pack64(stat64_info.st_size) - stat64_buf += ql.pack64(int(stat64_info.st_atime)) - stat64_buf += ql.pack64(int(stat64_info.st_mtime)) - stat64_buf += ql.pack64(int(stat64_info.st_ctime)) - stat64_buf += ql.pack32(stat64_info.st_blksize) - stat64_buf += ql.pack32(0) - stat64_buf += ql.pack64(stat64_info.st_blocks) - else: - # packfstatinfo - stat64_buf = ql.pack64(stat64_info.st_dev) - stat64_buf += ql.pack64(0x0000000300c30000) - stat64_buf += ql.pack32(stat64_info.st_mode) - stat64_buf += ql.pack32(stat64_info.st_nlink) - stat64_buf += ql.pack32(stat64_info.st_uid) - stat64_buf += ql.pack32(stat64_info.st_gid) - stat64_buf += ql.pack64(0x0000000000008800) #?? fstat_info.st_rdev - stat64_buf += ql.pack32(0xffffd257) - stat64_buf += ql.pack64(stat64_info.st_size) - stat64_buf += ql.pack32(0x00000400) #?? fstat_info.st_blksize - stat64_buf += ql.pack64(0x0000000000000000) #?? fstat_info.st_blocks - stat64_buf += ql.pack64(int(stat64_info.st_atime)) - stat64_buf += ql.pack64(int(stat64_info.st_mtime)) - stat64_buf += ql.pack64(int(stat64_info.st_ctime)) - stat64_buf += ql.pack64(stat64_info.st_ino) - - ql.mem.write(stat64_buf_ptr, stat64_buf) - regreturn = 0 - - logging.info("stat64(%s, 0x%x) = %d" % (relative_path, stat64_buf_ptr, regreturn)) if regreturn == 0: - logging.debug("[+] stat64 write completed") + ql.log.debug("fstat write completed") else: - logging.debug("[!] stat64 read/write fail") + ql.log.debug("fstat read/write fail") return regreturn # int stat(const char *path, struct stat *buf); def ql_syscall_stat(ql, stat_path, stat_buf_ptr, *args, **kw): - stat_file = (ql.mem.string(stat_path)) - - real_path = ql.os.transform_to_real_path(stat_file) - relative_path = ql.os.transform_to_relative_path(stat_file) - - if os.path.exists(real_path) == False: - regreturn = -1 - else: - stat_info = Stat(real_path) - - if ql.archtype== QL_ARCH.MIPS: - # pack fstatinfo - stat_buf = ql.pack32(stat_info.st_dev) - stat_buf += ql.pack32(0) * 3 - stat_buf += ql.pack32(stat_info.st_ino) - stat_buf += ql.pack32(stat_info.st_mode) - stat_buf += ql.pack32(stat_info.st_nlink) - stat_buf += ql.pack32(stat_info.st_uid) - stat_buf += ql.pack32(stat_info.st_gid) - stat_buf += ql.pack32(stat_info.st_rdev) - stat_buf += ql.pack32(0) * 2 - stat_buf += ql.pack32(stat_info.st_size) - stat_buf += ql.pack32(0) - stat_buf += ql.pack32(int(stat_info.st_atime)) - stat_buf += ql.pack32(0) - stat_buf += ql.pack32(int(stat_info.st_mtime)) - stat_buf += ql.pack32(0) - stat_buf += ql.pack32(int(stat_info.st_ctime)) - stat_buf += ql.pack32(0) - stat_buf += ql.pack32(stat_info.st_blksize) - stat_buf += ql.pack32(stat_info.st_blocks) - stat_buf = stat_buf.ljust(0x90, b'\x00') - else: - # pack statinfo - stat_buf = ql.pack32(stat_info.st_mode) - stat_buf += ql.pack32(stat_info.st_ino) - stat_buf += ql.pack32(stat_info.st_dev) - stat_buf += ql.pack32(stat_info.st_rdev) - stat_buf += ql.pack32(stat_info.st_nlink) - stat_buf += ql.pack32(stat_info.st_size) - stat_buf += ql.pack32(stat_info.st_size) - stat_buf += ql.pack32(stat_info.st_size) - stat_buf += ql.pack32(int(stat_info.st_atime)) - stat_buf += ql.pack32(int(stat_info.st_mtime)) - stat_buf += ql.pack32(int(stat_info.st_ctime)) - stat_buf += ql.pack32(stat_info.st_blksize) - stat_buf += ql.pack32(stat_info.st_blocks) + return statFamily(ql, stat_path, stat_buf_ptr, "stat", Stat, create_stat_struct) - regreturn = 0 - ql.mem.write(stat_buf_ptr, stat_buf) - logging.info("stat(%s, 0x%x) = %d" % (relative_path, stat_buf_ptr, regreturn)) - if regreturn == 0: - logging.debug("[+] stat() write completed") - else: - logging.debug("[!] stat() read/write fail") - return regreturn +# int stat64(const char *pathname, struct stat64 *buf); +def ql_syscall_stat64(ql, stat64_pathname, stat64_buf_ptr, *args, **kw): + return statFamily(ql, stat64_pathname, stat64_buf_ptr, "stat64", Stat, create_stat64_struct) def ql_syscall_lstat(ql, lstat_path, lstat_buf_ptr, *args, **kw): - lstat_file = (ql.mem.string(lstat_path)) + return statFamily(ql, lstat_path, lstat_buf_ptr, "lstat", Lstat, create_stat_struct) - real_path = ql.os.transform_to_real_path(lstat_file) - relative_path = ql.os.transform_to_relative_path(lstat_file) - if os.path.exists(real_path) == False: - regreturn = -1 - else: - lstat_info = Lstat(real_path) - - if ql.archtype== QL_ARCH.MIPS: - # pack fstatinfo - lstat_buf = ql.pack32(lstat_info.st_dev) - lstat_buf += ql.pack32(0) * 3 - lstat_buf += ql.pack32(lstat_info.st_ino) - lstat_buf += ql.pack32(lstat_info.st_mode) - lstat_buf += ql.pack32(lstat_info.st_nlink) - lstat_buf += ql.pack32(lstat_info.st_uid) - lstat_buf += ql.pack32(lstat_info.st_gid) - lstat_buf += ql.pack32(lstat_info.st_rdev) - lstat_buf += ql.pack32(0) * 2 - lstat_buf += ql.pack32(lstat_info.st_size) - lstat_buf += ql.pack32(0) - lstat_buf += ql.pack32(int(lstat_info.st_atime)) - lstat_buf += ql.pack32(0) - lstat_buf += ql.pack32(int(lstat_info.st_mtime)) - lstat_buf += ql.pack32(0) - lstat_buf += ql.pack32(int(lstat_info.st_ctime)) - lstat_buf += ql.pack32(0) - lstat_buf += ql.pack32(lstat_info.st_blksize) - lstat_buf += ql.pack32(lstat_info.st_blocks) - lstat_buf = lstat_buf.ljust(0x90, b'\x00') - else: - # pack statinfo - lstat_buf = ql.pack32(lstat_info.st_mode) - lstat_buf += ql.pack32(lstat_info.st_ino) - lstat_buf += ql.pack32(lstat_info.st_dev) - lstat_buf += ql.pack32(lstat_info.st_rdev) - lstat_buf += ql.pack32(lstat_info.st_nlink) - lstat_buf += ql.pack32(lstat_info.st_size) - lstat_buf += ql.pack32(lstat_info.st_size) - lstat_buf += ql.pack32(lstat_info.st_size) - lstat_buf += ql.pack32(int(lstat_info.st_atime)) - lstat_buf += ql.pack32(int(lstat_info.st_mtime)) - lstat_buf += ql.pack32(int(lstat_info.st_ctime)) - lstat_buf += ql.pack32(lstat_info.st_blksize) - lstat_buf += ql.pack32(lstat_info.st_blocks) +def ql_syscall_lstat64(ql, lstat64_path, lstat64_buf_ptr, *args, **kw): + return statFamily(ql, lstat64_path, lstat64_buf_ptr, "lstat64", Lstat, create_stat64_struct) - regreturn = 0 - ql.mem.write(lstat_buf_ptr, lstat_buf) - - logging.info("lstat(%s, 0x%x) = %d" % (relative_path, lstat_buf_ptr, regreturn)) - if regreturn == 0: - logging.debug("[+] lstat() write completed") - else: - logging.debug("[!] lstat() read/write fail") - return regreturn def ql_syscall_mknodat(ql, dirfd, pathname, mode, dev, *args, **kw): # fix me. dirfd(relative path) not implement. file_path = ql.mem.string(pathname) real_path = ql.os.transform_to_real_path(file_path) - logging.info("mknodat(%d, %s, 0%o, %d)" % (dirfd, real_path, mode, dev)) + ql.log.debug("mknodat(%d, %s, 0%o, %d)" % (dirfd, real_path, mode, dev)) try: os.mknod(real_path, mode, dev) regreturn = 0 @@ -536,7 +379,7 @@ def ql_syscall_mknodat(ql, dirfd, pathname, mode, dev, *args, **kw): def ql_syscall_mkdir(ql, pathname, mode, *args, **kw): file_path = ql.mem.string(pathname) real_path = ql.os.transform_to_real_path(file_path) - logging.info("mkdir(%s, 0%o)" % (real_path, mode)) + ql.log.debug("mkdir(%s, 0%o)" % (real_path, mode)) try: if not os.path.exists(real_path): os.mkdir(real_path, mode) @@ -548,6 +391,6 @@ def ql_syscall_mkdir(ql, pathname, mode, *args, **kw): def ql_syscall_umask(ql, mode, *args, **kw): oldmask = os.umask(mode) - logging.info("umask(0%o) return oldmask 0%o" % (mode, oldmask)) + ql.log.debug("umask(0%o) return oldmask 0%o" % (mode, oldmask)) regreturn = oldmask return regreturn diff --git a/qiling/os/posix/syscall/sysctl.py b/qiling/os/posix/syscall/sysctl.py index 8dc165232..41f566611 100644 --- a/qiling/os/posix/syscall/sysctl.py +++ b/qiling/os/posix/syscall/sysctl.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -15,5 +15,4 @@ def ql_syscall__sysctl(ql, sysctl_name, sysctl_namelen, sysctl_bytes_oldlenp, sysctl_size_oldlenp, sysctl_bytes_newlen, sysctl_size_newlen): # sysctl (name=0x7fffffffe3d8, namelen=2, oldp=0x7fffffffe3d4, oldlenp=0x7fffffffe3e0, newp=0x0, newlen=) regreturn = 0 - logging.info("_sysctl(0x%x) = %i" % (sysctl_name, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/sysinfo.py b/qiling/os/posix/syscall/sysinfo.py index f796c2b05..a643d8565 100644 --- a/qiling/os/posix/syscall/sysinfo.py +++ b/qiling/os/posix/syscall/sysinfo.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import struct -import struct, logging from qiling.const import * from qiling.os.linux.thread import * @@ -34,6 +35,5 @@ def ql_syscall_sysinfo(ql, sysinfo_info, *args, **kw): ) regreturn = 0 - logging.info("sysinfo(0x%x) = %d" % (sysinfo_info, regreturn)) #ql.mem.write(sysinfo_info, data) return regreturn diff --git a/qiling/os/posix/syscall/time.py b/qiling/os/posix/syscall/time.py index 964f2bb76..2bfe62fda 100644 --- a/qiling/os/posix/syscall/time.py +++ b/qiling/os/posix/syscall/time.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -14,7 +14,6 @@ def ql_syscall_time(ql, *args, **kw): regreturn = int(time.time()) - logging.info("time() = %d" % regreturn) return regreturn @@ -35,7 +34,6 @@ def _sched_sleep(cur_thread): th = ql.os.thread_management.cur_thread regreturn = 0 - logging.info("nanosleep(0x%x, 0x%x) = %d" % (nanosleep_req, nanosleep_rem, regreturn)) return regreturn @@ -44,7 +42,6 @@ def ql_syscall_setitimer(ql, setitimer_which, setitimer_new_value, setitimer_old # When any timer expires, a signal is sent to the process, and the timer (potentially) restarts. # But I haven’t figured out how to send a signal yet. regreturn = 0 - logging.info("setitimer(%d, %x, %x) = %d" % (setitimer_which, setitimer_new_value, setitimer_old_value, regreturn)) return regreturn @@ -58,7 +55,6 @@ def ql_syscall_times(ql, times_tbuf, *args, **kw): tmp_buf += ql.pack32(int(tmp_times.children_system * 1000)) ql.mem.write(times_tbuf, tmp_buf) regreturn = int(tmp_times.elapsed * 100) - logging.info('times(%x) = %d' % (times_tbuf, regreturn)) return regreturn @@ -72,5 +68,4 @@ def ql_syscall_gettimeofday(ql, gettimeofday_tv, gettimeofday_tz, *args, **kw): if gettimeofday_tz != 0: ql.mem.write(gettimeofday_tz, b'\x00' * 8) regreturn = 0 - logging.info("gettimeofday(%x, %x) = %d" % (gettimeofday_tv, gettimeofday_tz, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/types.py b/qiling/os/posix/syscall/types.py index 84bd10df0..25a77fab9 100644 --- a/qiling/os/posix/syscall/types.py +++ b/qiling/os/posix/syscall/types.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -21,5 +21,4 @@ def ql_syscall_gettid(ql, *args, **kw): # In single-threaded process, the thread ID is equal to the process ID # per Posix documentation. regreturn = ql.os.pid - logging.info("gettid() = %d" % regreturn) return regreturn diff --git a/qiling/os/posix/syscall/uio.py b/qiling/os/posix/syscall/uio.py index 111337296..b2165f29f 100644 --- a/qiling/os/posix/syscall/uio.py +++ b/qiling/os/posix/syscall/uio.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -16,11 +16,10 @@ def ql_syscall_writev(ql, writev_fd, writev_vec, writev_vien, *args, **kw): regreturn = 0 size_t_len = ql.archbit // 8 iov = ql.mem.read(writev_vec, writev_vien * size_t_len * 2) - logging.info("writev(0x%x, 0x%x, 0x%x)" % (writev_fd, writev_vec, writev_vien)) - logging.debug("[+] writev() CONTENT:") + ql.log.debug("writev() CONTENT:") for i in range(writev_vien): 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 - logging.debug("%s" % str(ql.mem.read(addr, l))) + ql.log.debug("%s" % str(ql.mem.read(addr, l))) return regreturn diff --git a/qiling/os/posix/syscall/unistd.py b/qiling/os/posix/syscall/unistd.py index 517eaddd0..7c9b1b682 100644 --- a/qiling/os/posix/syscall/unistd.py +++ b/qiling/os/posix/syscall/unistd.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import stat, logging, itertools, pathlib +import stat, itertools, pathlib from multiprocessing import Process + from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -17,15 +18,12 @@ from qiling.os.stat import * def ql_syscall_exit(ql, exit_code, *args, **kw): - logging.info("exit(%u) = %u" % (exit_code, exit_code)) - if ql.os.child_processes == True: os._exit(0) if ql.multithread: def _sched_cb_exit(cur_thread): - logging.debug(f"[Thread {cur_thread.get_id()}] Terminated.") - cur_thread.status = THREAD_STATUS_TERMINATED + ql.log.debug(f"[Thread {cur_thread.get_id()}] Terminated") cur_thread.stop() cur_thread.exit_code = exit_code td = ql.os.thread_management.cur_thread @@ -39,8 +37,6 @@ def _sched_cb_exit(cur_thread): def ql_syscall_exit_group(ql, exit_code, *args, **kw): ql.os.exit_code = exit_code - logging.info("exit_group(%u)" % ql.os.exit_code) - if ql.os.child_processes == True: os._exit(0) @@ -49,55 +45,47 @@ def ql_syscall_exit_group(ql, exit_code, *args, **kw): def ql_syscall_alarm(ql, alarm_seconds, *args, **kw): regreturn = 0 - logging.info("alarm(%d) = %d" % (alarm_seconds, regreturn)) return regreturn def ql_syscall_issetugid(ql, *args, **kw): UGID = ql.os.uid - logging.info("issetugid(%i)" % UGID) regreturn = UGID return regreturn def ql_syscall_getuid(ql, *args, **kw): UID = ql.os.uid - logging.info("getuid(%i)" % UID) regreturn = UID return regreturn def ql_syscall_getuid32(ql, *args, **kw): UID = ql.os.uid - logging.info("getuid32(%i)" % UID) regreturn = UID return regreturn def ql_syscall_getgid32(ql, *args, **kw): GID = ql.os.gid - logging.info("getgid32(%i)" % GID) regreturn = GID return regreturn def ql_syscall_geteuid(ql, *args, **kw): EUID = ql.os.uid - logging.info("geteuid(%i)" % EUID) regreturn = EUID return regreturn def ql_syscall_getegid(ql, *args, **kw): EGID = ql.os.gid - logging.info("getegid(%i)" % EGID) regreturn = EGID return regreturn def ql_syscall_getgid(ql, *args, **kw): GID = ql.os.gid - logging.info("getgid(%i)" % GID) regreturn = GID return regreturn @@ -105,27 +93,23 @@ def ql_syscall_getgid(ql, *args, **kw): def ql_syscall_setgroups(ql, gidsetsize, grouplist, *args, **kw): GID = ql.os.gid regreturn = GID - logging.info("setgroups(0x%x, 0x%x) = %d" % (gidsetsize, grouplist, regreturn)) return regreturn def ql_syscall_setgid(ql, *args, **kw): GID = ql.os.gid - logging.info("setgid(%i)" % GID) regreturn = GID return regreturn def ql_syscall_setgid32(ql, *args, **kw): GID = ql.os.gid - logging.info("setgid32(%i)" % GID) regreturn = GID return regreturn def ql_syscall_setuid(ql, *args, **kw): UID = ql.os.uid - logging.info("setuid(%i)" % UID) regreturn = UID return regreturn @@ -144,24 +128,21 @@ def ql_syscall_faccessat(ql, faccessat_dfd, faccessat_filename, faccessat_mode, else: regreturn = -1 - logging.info("facccessat (%d, 0x%x, 0x%x) = %d" %(faccessat_dfd, faccessat_filename, faccessat_mode, regreturn)) - if regreturn == -1: - logging.debug("[!] File Not Found or Skipped: %s" % access_path) + ql.log.debug("File Not Found or Skipped: %s" % access_path) else: - logging.debug("[+] File Found: %s" % access_path) + ql.log.debug("File Found: %s" % access_path) return regreturn def ql_syscall_lseek(ql, lseek_fd, lseek_ofset, lseek_origin, *args, **kw): lseek_ofset = ql.unpacks(ql.pack(lseek_ofset)) regreturn = 0 - logging.debug("lseek(%d, 0x%x, 0x%x) = %d" % (lseek_fd, lseek_ofset, lseek_origin, regreturn)) + ql.log.debug("lseek(%d, 0x%x, 0x%x) = %d" % (lseek_fd, lseek_ofset, lseek_origin, regreturn)) try: regreturn = ql.os.fd[lseek_fd].lseek(lseek_ofset, lseek_origin) except OSError: regreturn = -1 - logging.info("lseek(%d, 0x%x, 0x%x) = %d" % (lseek_fd, lseek_ofset, lseek_origin, regreturn)) return regreturn @@ -169,7 +150,6 @@ def ql_syscall__llseek(ql, fd, offset_high, offset_low, result, whence, *args, * offset = offset_high << 32 | offset_low origin = whence regreturn = 0 - logging.info("_llseek(%d, 0x%x, 0x%x, 0x%x) = %d" % (fd, offset_high, offset_low, origin, regreturn)) try: ret = ql.os.fd[fd].lseek(offset, origin) except OSError: @@ -178,7 +158,7 @@ def ql_syscall__llseek(ql, fd, offset_high, offset_low, result, whence, *args, * if regreturn == 0: ql.mem.write(result, ql.pack64(ret)) - logging.info("_llseek(%d, 0x%x, 0x%x, 0x%x) = %d" % (fd, offset_high, offset_low, origin, regreturn)) + ql.log.debug("_llseek(%d, 0x%x, 0x%x, 0x%x) = %d" % (fd, offset_high, offset_low, origin, regreturn)) return regreturn @@ -186,8 +166,6 @@ def ql_syscall_brk(ql, brk_input, *args, **kw): # current brk_address will be modified if brk_input is not NULL(zero) # otherwise, just return current brk_address - logging.info("brk(0x%x)" % brk_input) - if brk_input != 0: new_brk_addr = ((brk_input + 0xfff) // 0x1000) * 0x1000 @@ -201,7 +179,7 @@ def ql_syscall_brk(ql, brk_input, *args, **kw): regreturn = ql.loader.brk_address - logging.debug("[+] brk return(0x%x)" % regreturn) + ql.log.debug("brk return(0x%x)" % regreturn) return regreturn @@ -216,11 +194,11 @@ def ql_syscall_access(ql, access_path, access_mode, *args, **kw): else: regreturn = 0 - logging.info("access(%s, 0x%x) = %d " % (relative_path, access_mode, regreturn)) + ql.log.debug("access(%s, 0x%x) = %d " % (relative_path, access_mode, regreturn)) if regreturn == 0: - logging.debug("[+] File found: %s" % relative_path) + ql.log.debug("File found: %s" % relative_path) else: - logging.debug("[!] No such file or directory") + ql.log.debug("No such file or directory") return regreturn @@ -231,7 +209,6 @@ def ql_syscall_close(ql, close_fd, *args, **kw): ql.os.fd[close_fd].close() ql.os.fd[close_fd] = 0 regreturn = 0 - logging.info("close(%d) = %d" % (close_fd, regreturn)) return regreturn @@ -249,7 +226,6 @@ def ql_syscall_pread64(ql, read_fd, read_buf, read_len, read_offt, *args, **kw): regreturn = -1 else: regreturn = -1 - logging.info("pread(%d, 0x%x, 0x%x, 0x%x) = 0x%x" % (read_fd, read_buf, read_len, read_offt, regreturn)) return regreturn @@ -264,11 +240,10 @@ def ql_syscall_read(ql, read_fd, read_buf, read_len, *args, **kw): regreturn = -1 else: regreturn = -1 - logging.info("read(%d, 0x%x, 0x%x) = %d" % (read_fd, read_buf, read_len, regreturn)) if data: - logging.debug("[+] read() CONTENT:") - logging.debug("%s" % data) + ql.log.debug("read() CONTENT:") + ql.log.debug("%s" % data) return regreturn @@ -279,23 +254,22 @@ def ql_syscall_write(ql, write_fd, write_buf, write_count, *args, **kw): try: buf = ql.mem.read(write_buf, write_count) if buf: - logging.debug("[+] write() CONTENT:") - logging.debug("%s" % buf) + ql.log.debug("write() CONTENT:") + ql.log.debug("%s" % buf) if hasattr(ql.os.fd[write_fd], "write"): - logging.info("write(%d,%x,%i) = %d" % (write_fd, write_buf, write_count, regreturn)) + ql.os.fd[write_fd].write(buf) else: - logging.info("[!] write(%d,%x,%i) failed due to write_fd" % (write_fd, write_buf, write_count, regreturn)) + ql.log.warning("write(%d,%x,%i) failed due to write_fd" % (write_fd, write_buf, write_count, regreturn)) regreturn = write_count except: regreturn = -1 - logging.info("write(%d,%x,%i) = %d" % (write_fd, write_buf, write_count, regreturn)) if ql.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP): raise #if buf: - # logging.info(buf.decode(errors='ignore')) + # ql.log.info(buf.decode(errors='ignore')) return regreturn @@ -317,7 +291,7 @@ def ql_syscall_readlink(ql, path_name, path_buff, path_buffsize, *args, **kw): else: regreturn = 0x0 - logging.info("readlink(%s, 0x%x, 0x%x) = %d" % (relative_path, path_buff, path_buffsize, regreturn)) + ql.log.debug("readlink(%s, 0x%x, 0x%x) = %d" % (relative_path, path_buff, path_buffsize, regreturn)) return regreturn @@ -330,7 +304,7 @@ def ql_syscall_getcwd(ql, path_buff, path_buffsize, *args, **kw): pathname = (ql.mem.read(path_buff, 0x100).split(b'\x00'))[0] pathname = str(pathname, 'utf-8', errors="ignore") - logging.info("getcwd(%s, 0x%x) = %d" % (pathname, path_buffsize, regreturn)) + ql.log.debug("getcwd(%s, 0x%x) = %d" % (pathname, path_buffsize, regreturn)) return regreturn @@ -346,10 +320,10 @@ def ql_syscall_chdir(ql, path_name, *args, **kw): pass else: ql.os.current_path = relative_path - logging.info("chdir(%s) = %d"% (relative_path, regreturn)) + ql.log.debug("chdir(%s) = %d"% (relative_path, regreturn)) else: regreturn = -1 - logging.info("chdir(%s) = %d : Not Found" % (relative_path, regreturn)) + ql.log.warning("chdir(%s) = %d : Not Found" % (relative_path, regreturn)) return regreturn @@ -370,20 +344,16 @@ def ql_syscall_readlinkat(ql, readlinkat_dfd, readlinkat_path, readlinkat_buf, r regreturn = (len(localpath)-1) else: regreturn = 0x0 - - logging.info("readlinkat(0x%x, 0x%x, 0x%x, 0x%x) = %d" % (readlinkat_dfd, readlinkat_path, readlinkat_buf, readlinkat_bufsiz, regreturn)) return regreturn def ql_syscall_getpid(ql, *args, **kw): regreturn= 0x512 - logging.info("getpid() = %d" % (regreturn)) return regreturn def ql_syscall_getppid(ql, *args, **kw): regreturn= 0x1024 - logging.info("getpid() = %d" % (regreturn)) return regreturn @@ -399,30 +369,18 @@ def ql_syscall_vfork(ql, *args, **kw): if pid == 0: ql.os.child_processes = True - logging.debug("[+] vfork(): is this a child process: %r" % (ql.os.child_processes)) + ql.log.debug("vfork(): is this a child process: %r" % (ql.os.child_processes)) regreturn = 0 - if ql.os.thread_management != None: - ql.os.thread_management.cur_thread.set_thread_log_file(ql.log_dir) - else: - if ql.log_split: - _logger = ql.log_file_fd - _logger = ql_setup_logging_file(ql.output, ql.log_file , _logger) - _logger_name = str(len(logging.root.manager.loggerDict)) - _logger = ql_setup_logging_file(ql.output, '_'.join((ql.log_file, _logger_name))) - ql.log_file_fd = _logger else: regreturn = pid if ql.os.thread_management != None: ql.emu_stop() - - logging.info("vfork() = %d" % regreturn) return regreturn def ql_syscall_setsid(ql, *args, **kw): regreturn = os.getpid() - logging.info("setsid() = %d" % regreturn) return regreturn @@ -458,21 +416,22 @@ def ql_syscall_execve(ql, execve_pathname, execve_argv, execve_envp, *args, **kw ql.emu_stop() - logging.info("execve(%s, [%s], [%s])"% (pathname, ', '.join(argv), ', '.join([key + '=' + value for key, value in env.items()]))) - - if ql.shellcoder: - return - + ql.log.debug("execve(%s, [%s], [%s])"% (pathname, ', '.join(argv), ', '.join([key + '=' + value for key, value in env.items()]))) + ql.loader.argv = argv ql.loader.env = env ql._path = real_path + ql.mem.map_info = [] ql.clear_ql_hooks() - ql._uc = ql.arch.init_uc - ql.os.load() - ql.loader.run() - ql.run() + if ql.code: + return + + # ql._uc = ql.arch.init_uc + # ql.os.load() + # ql.loader.run() + # ql.run() def ql_syscall_dup(ql, dup_oldfd, *args, **kw): @@ -486,7 +445,6 @@ def ql_syscall_dup(ql, dup_oldfd, *args, **kw): regreturn = idx break - logging.info("dup(%d) = %d" % (dup_oldfd, regreturn)) return regreturn @@ -499,7 +457,6 @@ def ql_syscall_dup2(ql, dup2_oldfd, dup2_newfd, *args, **kw): regreturn = -1 else: regreturn = -1 - logging.info("dup2(%d, %d) = %d" % (dup2_oldfd, dup2_newfd, regreturn)) return regreturn @@ -512,7 +469,6 @@ def ql_syscall_dup3(ql, dup3_oldfd, dup3_newfd, dup3_flags, null2, null3, null4) regreturn = -1 else: regreturn = -1 - logging.info("dup3(%d, %d, %d) = %d" % (dup3_oldfd, dup3_newfd, dup3_flags, regreturn)) return regreturn def ql_syscall_set_tid_address(ql, set_tid_address_tidptr, *args, **kw): @@ -521,7 +477,6 @@ def ql_syscall_set_tid_address(ql, set_tid_address_tidptr, *args, **kw): else: ql.os.thread_management.cur_thread.set_clear_child_tid_addr(set_tid_address_tidptr) regreturn = ql.os.thread_management.cur_thread.id - logging.info("set_tid_address(%x) = %d" % (set_tid_address_tidptr, regreturn)) return regreturn @@ -553,13 +508,12 @@ def ql_syscall_pipe(ql, pipe_pipefd, *args, **kw): ql.mem.write(pipe_pipefd, ql.pack32(idx1) + ql.pack32(idx2)) regreturn = 0 - logging.info("pipe(%x, [%d, %d]) = %d" % (pipe_pipefd, idx1, idx2, regreturn)) + ql.log.debug("pipe(%x, [%d, %d]) = %d" % (pipe_pipefd, idx1, idx2, regreturn)) return regreturn def ql_syscall_nice(ql, nice_inc, *args, **kw): regreturn = 0 - logging.info("nice(%d) = %d" % (nice_inc, regreturn)) return regreturn @@ -581,7 +535,7 @@ def ql_syscall_truncate(ql, path, length, *args, **kw): except: regreturn = -1 - logging.info('truncate(%s, 0x%x) = %d' % (path, length, regreturn)) + ql.log.debug('truncate(%s, 0x%x) = %d' % (path, length, regreturn)) return regreturn @@ -602,7 +556,7 @@ def ql_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args, **kw): except: regreturn = -1 - logging.info('ftruncate(%d, 0x%x) = %d' % (ftrunc_fd, ftrunc_length, regreturn)) + ql.log.debug("ftruncate(%d, 0x%x) = %d" % (ftrunc_fd, ftrunc_length, regreturn)) return regreturn @@ -617,14 +571,14 @@ def ql_syscall_unlink(ql, unlink_pathname, *args, **kw): os.unlink(real_path) regreturn = 0 except FileNotFoundError: - logging.debug('[!] No such file or directory') + ql.log.debug('No such file or directory') regreturn = -1 except: regreturn = -1 else: regreturn = -1 - logging.info('unlink(%s) = %d' % (pathname, regreturn)) + ql.log.debug("unlink(%s) = %d" % (pathname, regreturn)) return regreturn @@ -632,12 +586,12 @@ def ql_syscall_unlinkat(ql, dirfd, pathname, flag, *args, **kw): #FIXME dirfd(relative path) not implement. file_path = ql.mem.string(pathname) real_path = ql.os.transform_to_real_path(file_path) - logging.info("unlinkat(%d, %s, 0%o)" % (dirfd, real_path, flag)) + ql.log.debug("unlinkat(%d, %s, 0%o)" % (dirfd, real_path, flag)) try: os.unlink(real_path) return 0 except FileNotFoundError: - logging.debug("[!] No such file or directory") + ql.log.debug("No such file or directory") return -1 except: return -1 @@ -689,7 +643,6 @@ def _type_mapping(ent): _ent_count = 0 regreturn = 0 - logging.info("getdents(%d, 0x%x, 0x%x) = %d" % (fd, dirp, count, regreturn)) - logging.debug("[+] getdents(%d, /* %d entries */, 0x%x) = %d" % (fd, _ent_count, count, regreturn)) + ql.log.debug("getdents(%d, /* %d entries */, 0x%x) = %d" % (fd, _ent_count, count, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/utsname.py b/qiling/os/posix/syscall/utsname.py index f9a89b2b2..4c2e3cffd 100644 --- a/qiling/os/posix/syscall/utsname.py +++ b/qiling/os/posix/syscall/utsname.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -22,5 +22,4 @@ def ql_syscall_uname(ql, address, *args, **kw): buf += b''.ljust(65, b'\x00') ql.mem.write(address, buf) regreturn = 0 - logging.info("uname(0x%x) = %d" % (address, regreturn)) return regreturn diff --git a/qiling/os/posix/syscall/wait.py b/qiling/os/posix/syscall/wait.py index 8601b8815..80171519d 100644 --- a/qiling/os/posix/syscall/wait.py +++ b/qiling/os/posix/syscall/wait.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.linux.thread import * from qiling.const import * @@ -12,11 +12,17 @@ from qiling.os.posix.const_mapping import * from qiling.exception import * from qiling.utils import * +from qiling.os.posix.const import ECHILD + def ql_syscall_wait4(ql, wait4_pid, wait4_wstatus, wait4_options, wait4_rusage, *args, **kw): - spid, status, rusage = os.wait4(wait4_pid, wait4_options) - if wait4_wstatus != 0: - ql.mem.write(wait4_wstatus, ql.pack32(status)) - regreturn = spid - logging.info("wait4(%d, %d) = %d"% (wait4_pid, wait4_options, regreturn)) + wait4_pid = ql.unpacks(ql.pack(wait4_pid)) # convert to signed + try: + spid, status, rusage = os.wait4(wait4_pid, wait4_options) + if wait4_wstatus != 0: + ql.mem.write(wait4_wstatus, ql.pack32(status)) + regreturn = spid + except ChildProcessError: + regreturn = -ECHILD return regreturn + diff --git a/qiling/os/stat.py b/qiling/os/stat.py index f162c14b0..c59a90f98 100644 --- a/qiling/os/stat.py +++ b/qiling/os/stat.py @@ -1,57 +1,26 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import os +# +import os class Stat(object): def __init__(self, path): super().__init__() self.path = path - - self.st_dev = 0 - self.st_blksize = 0 - self.st_blocks = 0 - self.st_gid = 0 - self.st_ino = 0 - self.st_mode = 0 - self.st_nlink = 0 - self.st_rdev = 0 - self.st_size = 0 - self.st_uid = 0 - self.st_atime = 0 - self.st_mtime = 0 - self.st_ctime = 0 - stat_buf = os.stat(self.path) for name in dir(stat_buf): - if 'st_' in name: + if name.startswith('st_'): setattr(self, name, getattr(stat_buf, name)) class Fstat(object): def __init__(self, fd): super().__init__() self.fd = fd - - self.st_atime = 0 - self.st_blksize = 0 - self.st_blocks = 0 - self.st_ctime = 0 - self.st_dev = 0 - self.st_gid = 0 - self.st_ino = 0 - self.st_mode = 0 - self.st_mtime = 0 - self.st_nlink = 0 - self.st_rdev = 0 - self.st_size = 0 - self.st_uid = 0 - - fstat_buf = os.fstat(self.fd) for name in dir(fstat_buf): - if 'st_' in name: + if name.startswith('st_'): setattr(self, name, getattr(fstat_buf, name)) @@ -59,25 +28,9 @@ class Lstat(object): def __init__(self, path): super().__init__() self.path = path - - self.st_atime = 0 - self.st_blksize = 0 - self.st_blocks = 0 - self.st_ctime = 0 - self.st_dev = 0 - self.st_gid = 0 - self.st_ino = 0 - self.st_mode = 0 - self.st_mtime = 0 - self.st_nlink = 0 - self.st_rdev = 0 - self.st_size = 0 - self.st_uid = 0 - - lstat_buf = os.lstat(self.path) for name in dir(lstat_buf): - if 'st_' in name: + if name.startswith('st_'): setattr(self, name, getattr(lstat_buf, name)) diff --git a/qiling/os/thread.py b/qiling/os/thread.py index 636174c37..28adec686 100644 --- a/qiling/os/thread.py +++ b/qiling/os/thread.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from abc import ABCMeta, abstractmethod from gevent import Greenlet diff --git a/qiling/os/uefi/PiMultiPhase.py b/qiling/os/uefi/PiMultiPhase.py new file mode 100644 index 000000000..798705a78 --- /dev/null +++ b/qiling/os/uefi/PiMultiPhase.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from .UefiBaseType import * +from .ProcessorBind import * + +EFI_SMRAM_OPEN = 0x00000001 +EFI_SMRAM_CLOSED = 0x00000002 +EFI_SMRAM_LOCKED = 0x00000004 +EFI_CACHEABLE = 0x00000008 +EFI_ALLOCATED = 0x00000010 +EFI_NEEDS_TESTING = 0x00000020 +EFI_NEEDS_ECC_INITIALIZATION = 0x00000040 + +class EFI_SMRAM_DESCRIPTOR(STRUCT): + _fields_ = [ + ('PhysicalStart', EFI_PHYSICAL_ADDRESS), + ('CpuStart', EFI_PHYSICAL_ADDRESS), + ('PhysicalSize', UINT64), + ('RegionState', UINT64) + ] + +__all__ = [ + 'EFI_SMRAM_DESCRIPTOR', + 'EFI_SMRAM_OPEN', + 'EFI_SMRAM_CLOSED', + 'EFI_SMRAM_LOCKED', + 'EFI_CACHEABLE', + 'EFI_ALLOCATED', + 'EFI_NEEDS_TESTING', + 'EFI_NEEDS_ECC_INITIALIZATION' +] diff --git a/qiling/os/uefi/ProcessorBind.py b/qiling/os/uefi/ProcessorBind.py index fde43515f..4dac84170 100644 --- a/qiling/os/uefi/ProcessorBind.py +++ b/qiling/os/uefi/ProcessorBind.py @@ -1,4 +1,10 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + import ctypes +from typing import Any, Dict bits = 64 psize = bits // 8 @@ -8,7 +14,7 @@ 64 : ctypes.c_uint64 }[bits] -_pointer_type_cache = {} +_pointer_type_cache: Dict[str, type] = {} def PTR(ptype): pname = 'c_void' if ptype is None else ptype.__name__ @@ -54,7 +60,7 @@ class STRUCT(ctypes.LittleEndianStructure): def __init__(self): pass - def saveTo(self, ql, address : int): + def saveTo(self, ql, address: int) -> None: """Store self contents to a specified memory address. """ @@ -62,11 +68,9 @@ def saveTo(self, ql, address : int): ql.mem.write(address, data) - return address + len(data) - @classmethod - def loadFrom(cls, ql, address : int): - """Construct an instance from saved contents. + def loadFrom(cls, ql, address: int) -> Any: + """Construct an instance of the structure from saved contents. """ data = bytes(ql.mem.read(address, cls.sizeof())) @@ -74,14 +78,14 @@ def loadFrom(cls, ql, address : int): return cls.from_buffer_copy(data) @classmethod - def sizeof(cls): + def sizeof(cls) -> int: """Get the C structure size in bytes. """ return ctypes.sizeof(cls) @classmethod - def offsetof(cls, fname): + def offsetof(cls, fname: str) -> int: """Get the offset of a field in the C structure. """ diff --git a/qiling/os/uefi/UefiBaseType.py b/qiling/os/uefi/UefiBaseType.py index 34f2fb1ed..65380bd1c 100644 --- a/qiling/os/uefi/UefiBaseType.py +++ b/qiling/os/uefi/UefiBaseType.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -from .ProcessorBind import * +from qiling.os.uefi.ProcessorBind import * class EFI_GUID(STRUCT): _fields_ = [ diff --git a/qiling/os/uefi/UefiMultiPhase.py b/qiling/os/uefi/UefiMultiPhase.py index 9c36ead0a..01bd0e8ad 100644 --- a/qiling/os/uefi/UefiMultiPhase.py +++ b/qiling/os/uefi/UefiMultiPhase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .ProcessorBind import * diff --git a/qiling/os/uefi/UefiSpec.py b/qiling/os/uefi/UefiSpec.py index 71d97f556..14816b88f 100644 --- a/qiling/os/uefi/UefiSpec.py +++ b/qiling/os/uefi/UefiSpec.py @@ -2,7 +2,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # @see: MdePkg\Include\Uefi\UefiSpec.h diff --git a/qiling/os/uefi/bs.py b/qiling/os/uefi/bs.py index 1ea490b90..43f967528 100644 --- a/qiling/os/uefi/bs.py +++ b/qiling/os/uefi/bs.py @@ -1,15 +1,17 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from binascii import crc32 from qiling.os.const import * -from .fncc import * -from .utils import * -from .ProcessorBind import * -from .UefiSpec import * +from qiling.os.uefi.const import * +from qiling.os.uefi.fncc import * +from qiling.os.uefi.utils import * +from qiling.os.uefi.ProcessorBind import * +from qiling.os.uefi.UefiSpec import * +from qiling.os.uefi.protocols import common # TODO: find a better solution than hardcoding this pointer_size = 8 @@ -130,14 +132,7 @@ def hook_SignalEvent(ql, address, params): event_id = params["Event"] if event_id in ql.loader.events: - event = ql.loader.events[event_id] - - if not event["Set"]: - event["Set"] = True - notify_func = event["NotifyFunction"] - notify_context = event["NotifyContext"] - ql.loader.notify_list.append((event_id, notify_func, notify_context)) - + signal_event(ql, event_id) return EFI_SUCCESS else: return EFI_INVALID_PARAMETER @@ -166,25 +161,13 @@ def hook_CheckEvent(ql, address, params): "Interface" : POINTER, # PTR(VOID) }) def hook_InstallProtocolInterface(ql, address, params): - handle = read_int64(ql, params["Handle"]) - - if handle == 0: - handle = ql.loader.dxe_context.heap.alloc(1) - - dic = ql.loader.dxe_context.protocols.get(handle, {}) - - dic[params["Protocol"]] = params["Interface"] - ql.loader.dxe_context.protocols[handle] = dic - check_and_notify_protocols(ql) - write_int64(ql, params["Handle"], handle) - - return EFI_SUCCESS + return common.InstallProtocolInterface(ql.loader.dxe_context, params) @dxeapi(params = { "Handle" : POINTER, # EFI_HANDLE "Protocol" : GUID, # PTR(EFI_GUID) "OldInterface" : POINTER, # PTR(VOID) - "NewInterface" : POINTER, # PTR(VOID) + "NewInterface" : POINTER # PTR(VOID) }) def hook_ReinstallProtocolInterface(ql, address, params): handle = params["Handle"] @@ -208,20 +191,7 @@ def hook_ReinstallProtocolInterface(ql, address, params): "Interface" : POINTER # PTR(VOID) }) def hook_UninstallProtocolInterface(ql, address, params): - handle = params["Handle"] - - if handle not in ql.loader.dxe_context.protocols: - return EFI_NOT_FOUND - - dic = ql.loader.dxe_context.protocols[handle] - protocol = params["Protocol"] - - if protocol not in dic: - return EFI_NOT_FOUND - - del dic[protocol] - - return EFI_SUCCESS + return common.UninstallProtocolInterface(ql.loader.dxe_context, params) @dxeapi(params = { "Handle" : POINTER, # EFI_HANDLE @@ -229,16 +199,7 @@ def hook_UninstallProtocolInterface(ql, address, params): "Interface" : POINTER # PTR(PTR(VOID)) }) def hook_HandleProtocol(ql, address, params): - handle = params["Handle"] - protocol = params["Protocol"] - interface = params['Interface'] - - hdict = ql.loader.dxe_context.protocols - - if handle in hdict and protocol in hdict[handle]: - write_int64(ql, interface, hdict[handle][protocol]) - - return EFI_NOT_FOUND + return common.HandleProtocol(ql.loader.dxe_context, params) @dxeapi(params = { "Protocol" : GUID, # PTR(EFI_GUID) @@ -265,25 +226,7 @@ def hook_RegisterProtocolNotify(ql, address, params): "Buffer" : POINTER # PTR(EFI_HANDLE) }) def hook_LocateHandle(ql, address, params): - buffer_size, handles = LocateHandles(ql.loader.dxe_context, params) - - if len(handles) == 0: - return EFI_NOT_FOUND - - ret = EFI_BUFFER_TOO_SMALL - - if read_int64(ql, params["BufferSize"]) >= buffer_size: - ptr = params["Buffer"] - - for handle in handles: - write_int64(ql, ptr, handle) - ptr += pointer_size - - ret = EFI_SUCCESS - - write_int64(ql, params["BufferSize"], buffer_size) - - return ret + return common.LocateHandle(ql.loader.dxe_context, params) @dxeapi(params = { "Protocol" : GUID, # PTR(EFI_GUID) @@ -298,10 +241,7 @@ def hook_LocateDevicePath(ql, address, params): "Table" : POINTER # PTR(VOID) }) def hook_InstallConfigurationTable(ql, address, params): - guid = params["Guid"] - table = params["Table"] - - return CoreInstallConfigurationTable(ql, guid, table) + return common.InstallConfigurationTable(ql.loader.dxe_context, params) @dxeapi(params = { "BootPolicy" : BOOL, # BOOLEAN @@ -400,7 +340,7 @@ def hook_DisconnectController(ql, address, params): "Attributes" : UINT # UINT32 }) def hook_OpenProtocol(ql, address, params): - return LocateProtocol(ql.loader.dxe_context, params) + return common.LocateProtocol(ql.loader.dxe_context, params) @dxeapi(params = { "Handle" : POINTER, # EFI_HANDLE @@ -436,7 +376,7 @@ def hook_ProtocolsPerHandle(ql, address, params): "Buffer" : POINTER # PTR(PTR(EFI_HANDLE)) }) def hook_LocateHandleBuffer(ql, address, params): - buffer_size, handles = LocateHandles(ql.loader.dxe_context, params) + buffer_size, handles = common.LocateHandles(ql.loader.dxe_context, params) write_int64(ql, params["NoHandles"], len(handles)) if len(handles) == 0: @@ -460,7 +400,7 @@ def hook_LocateHandleBuffer(ql, address, params): "Interface" : POINTER # PTR(PTR(VOID)) }) def hook_LocateProtocol(ql, address, params): - return LocateProtocol(ql.loader.dxe_context, params) + return common.LocateProtocol(ql.loader.dxe_context, params) @dxeapi(params = { "Handle" : POINTER # PTR(EFI_HANDLE) @@ -470,7 +410,7 @@ def hook_InstallMultipleProtocolInterfaces(ql, address, params): handle = read_int64(ql, params["Handle"]) if handle == 0: - handle = ql.loader.dxe_context.heap.alloc(1) + handle = ql.loader.dxe_context.heap.alloc(pointer_size) dic = ql.loader.dxe_context.protocols.get(handle, {}) @@ -478,11 +418,13 @@ def hook_InstallMultipleProtocolInterfaces(ql, address, params): index = 1 while ql.os.get_param_by_index(index) != 0: GUID_ptr = ql.os.get_param_by_index(index) - protocol_ptr = ql.os.get_param_by_index(index+1) + protocol_ptr = ql.os.get_param_by_index(index + 1) + GUID = str(ql.os.read_guid(GUID_ptr)) - logging.info(f' | {GUID} {protocol_ptr:x}') dic[GUID] = protocol_ptr - index +=2 + + ql.log.info(f' | {GUID} {protocol_ptr:#x}') + index += 2 ql.loader.dxe_context.protocols[handle] = dic check_and_notify_protocols(ql) @@ -506,15 +448,17 @@ def hook_UninstallMultipleProtocolInterfaces(ql, address, params): index = 1 while ql.os.get_param_by_index(index) != 0: GUID_ptr = ql.os.get_param_by_index(index) - protocol_ptr = ql.os.get_param_by_index(index+1) + protocol_ptr = ql.os.get_param_by_index(index + 1) + GUID = str(ql.os.read_guid(GUID_ptr)) - logging.info(f' | {GUID}, {protocol_ptr:x}') if GUID not in dic: return EFI_INVALID_PARAMETER del dic[GUID] - index +=2 + + ql.log.info(f' | {GUID}, {protocol_ptr:#x}') + index += 2 return EFI_SUCCESS @@ -545,7 +489,7 @@ def hook_CopyMem(ql, address, params): }) def hook_SetMem(ql, address, params): ptr = params["Buffer"] - value = struct.pack('B',params["Value"] & 0xff) + value = ql.pack8(params["Value"] & 0xff) # TODO: do this the Pythonic way for i in range(params["Size"]): diff --git a/qiling/os/uefi/const.py b/qiling/os/uefi/const.py index 6017d02e0..d96972489 100644 --- a/qiling/os/uefi/const.py +++ b/qiling/os/uefi/const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# EFI_MAX_BIT = (1 << 63) EFI_SUCCESS = 0 @@ -41,3 +41,6 @@ EFI_WARN_DELETE_FAILURE = EFI_MAX_BIT | 2 EFI_WARN_WRITE_FAILURE = EFI_MAX_BIT | 3 EFI_WARN_BUFFER_TOO_SMALL = EFI_MAX_BIT | 4 + +# @see: MdePkg\Include\Base.h +EFI_ERROR = lambda status: (status & EFI_MAX_BIT) != 0 diff --git a/qiling/os/uefi/context.py b/qiling/os/uefi/context.py index 7de8db2d6..ba741e930 100644 --- a/qiling/os/uefi/context.py +++ b/qiling/os/uefi/context.py @@ -1,13 +1,22 @@ -import logging +from abc import ABC + from qiling.os.memory import QlMemoryHeap -from qiling.os.uefi.utils import init_struct +from qiling.os.uefi.utils import init_struct, str_to_guid +from qiling.os.uefi.UefiSpec import EFI_CONFIGURATION_TABLE, EFI_SYSTEM_TABLE +from qiling.os.uefi.smst import EFI_SMM_SYSTEM_TABLE2 -class UefiContext: +class UefiContext(ABC): def __init__(self, ql): self.ql = ql self.heap = None self.protocols = {} + # These members must be initialized before attempting to install a configuration table. + self.conf_table_array = [] + self.conf_table_array_ptr = 0 + self.conf_table_data_ptr = 0 + self.conf_table_data_next_ptr = 0 + def init_heap(self, base, size): self.heap = QlMemoryHeap(self.ql, base, base + size) @@ -21,7 +30,7 @@ def install_protocol(self, proto_desc, handle, address=None): self.protocols[handle] = {} if guid in self.protocols[handle]: - logging.warning(f'a protocol with guid {guid} is already installed') + self.ql.log.warning(f'a protocol with guid {guid} is already installed') if address is None: struct_class = proto_desc['struct'] @@ -32,8 +41,46 @@ def install_protocol(self, proto_desc, handle, address=None): self.protocols[handle][guid] = address + def install_configuration_table(self, guid, table): + guid = guid.lower() + confs = self.conf_table_array + + # find configuration table entry by guid. if found, idx would be set to the entry index + # in the array. if not, idx would be set to one past end of array + if guid not in confs: + confs.append(guid) + + idx = confs.index(guid) + ptr = self.conf_table_array_ptr + (idx * EFI_CONFIGURATION_TABLE.sizeof()) + + instance = EFI_CONFIGURATION_TABLE() + instance.VendorGuid = str_to_guid(guid) + instance.VendorTable = table + instance.saveTo(self.ql, ptr) + +class DxeContext(UefiContext): + def install_configuration_table(self, guid, table): + super().install_configuration_table(guid, table) + # Update number of configuration table entries in the ST. + gST = EFI_SYSTEM_TABLE.loadFrom(self.ql, self.ql.loader.gST) + gST.NumberOfTableEntries = len(self.conf_table_array) + gST.saveTo(self.ql, self.ql.loader.gST) + class SmmContext(UefiContext): def __init__(self, ql): super(SmmContext, self).__init__(ql) + # assume tseg is inaccessible to non-smm + self.tseg_open = False + + # assume tseg is locked + self.tseg_locked = True + self.swsmi_handlers = [] + + def install_configuration_table(self, guid, table): + super().install_configuration_table(guid, table) + # Update number of configuration table entries in the SMST. + gSmst = EFI_SMM_SYSTEM_TABLE2.loadFrom(self.ql, self.ql.loader.gSmst) + gSmst.NumberOfTableEntries = len(self.conf_table_array) + gSmst.saveTo(self.ql, self.ql.loader.gSmst) diff --git a/qiling/os/uefi/ds.py b/qiling/os/uefi/ds.py index d2be705a1..d6004e1a0 100644 --- a/qiling/os/uefi/ds.py +++ b/qiling/os/uefi/ds.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.const import * from qiling.os.const import * diff --git a/qiling/os/uefi/fncc.py b/qiling/os/uefi/fncc.py index 1a5c56fbb..47e2e0e9e 100644 --- a/qiling/os/uefi/fncc.py +++ b/qiling/os/uefi/fncc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# def dxeapi(param_num=None, params=None): def decorator(func): diff --git a/qiling/os/uefi/hob.py b/qiling/os/uefi/hob.py new file mode 100644 index 000000000..a9bf79c73 --- /dev/null +++ b/qiling/os/uefi/hob.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from qiling.os.uefi.utils import GetEfiConfigurationTable +from qiling.os.uefi.UefiBaseType import STRUCT, EFI_GUID, UINT32, UINT16 +from qiling.os.uefi.UefiSpec import EFI_CONFIGURATION_TABLE + +EFI_HOB_TYPE_HANDOFF = 0x0001 +EFI_HOB_TYPE_GUID_EXTENSION = 0x0004 +EFI_HOB_TYPE_END_OF_HOB_LIST = 0xffff + +class EFI_HOB_GENERIC_HEADER(STRUCT): + _fields_ = [ + ('HobType', UINT16), + ('HobLength', UINT16), + ('Reserved', UINT32) + ] + +class EFI_HOB_GUID_TYPE(STRUCT): + _fields_ = [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('Name', EFI_GUID) + ] + +def GetHobList(ql) -> int: + """Get HOB list location in memory (ostensibly set by PEI). + """ + + conftable_guid = ql.os.profile['HOB_LIST']['Guid'] + conftable_ptr = GetEfiConfigurationTable(ql.loader.dxe_context, conftable_guid) + conftable = EFI_CONFIGURATION_TABLE.loadFrom(ql, conftable_ptr) + + return ql.unpack64(conftable.VendorTable) + +def CreateHob(ql, hob) -> int: + """Add a HOB to the end of the HOB list. + """ + + hoblist = GetHobList(ql) + + # look for the list end marker; uefi codebase assumes there is + # always one + while True: + header = EFI_HOB_GENERIC_HEADER.loadFrom(ql, hoblist) + + if header.HobType == EFI_HOB_TYPE_END_OF_HOB_LIST: + break + + hoblist += header.HobLength + + # overwrite end marker with the hob + pHob = hoblist + hob.saveTo(ql, pHob) + hoblist += hob.sizeof() + + # create a new end marker istead, following the hob + marker = EFI_HOB_GENERIC_HEADER() + marker.HobType = EFI_HOB_TYPE_END_OF_HOB_LIST + marker.HobLength = 0x0000 + marker.Reserved = 0x00000000 + marker.saveTo(ql, hoblist) + + # return the address the hob was written to; it might be useful + return pHob diff --git a/qiling/os/uefi/protocols/EfiLoadedImageProtocol.py b/qiling/os/uefi/protocols/EfiLoadedImageProtocol.py index 54d91ba25..d7114dde9 100644 --- a/qiling/os/uefi/protocols/EfiLoadedImageProtocol.py +++ b/qiling/os/uefi/protocols/EfiLoadedImageProtocol.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from ..ProcessorBind import * from ..UefiBaseType import * @@ -32,7 +32,7 @@ def make_descriptor(fields): "guid" : "5b1b31a1-9562-11d2-8e3f-00a0c969723b", "struct" : EFI_LOADED_IMAGE_PROTOCOL, "fields" : ( - ('Revision', fields['revision']), + ('Revision', 0x1000), ('ParentHandle', 0), ('SystemTable', fields['gST']), ('DeviceHandle', fields['image_base']), diff --git a/qiling/os/uefi/protocols/EfiSmmAccess2Protocol.py b/qiling/os/uefi/protocols/EfiSmmAccess2Protocol.py index cfc44db3e..15e4834ac 100644 --- a/qiling/os/uefi/protocols/EfiSmmAccess2Protocol.py +++ b/qiling/os/uefi/protocols/EfiSmmAccess2Protocol.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.const import * from qiling.os.uefi.const import * from ..fncc import * from ..ProcessorBind import * from ..UefiBaseType import * -from ..utils import write_int64 +from ..PiMultiPhase import * +from ..utils import write_int64, read_int64 # @see: MdePkg\Include\Pi\PiMultiPhase.h class EFI_MMRAM_DESCRIPTOR(STRUCT): @@ -37,30 +38,93 @@ class EFI_SMM_ACCESS2_PROTOCOL(STRUCT): "This" : POINTER }) def hook_Open(ql, address, params): - return EFI_UNSUPPORTED + ql.loader.smm_context.tseg_open = True + + return EFI_SUCCESS @dxeapi(params = { "This" : POINTER }) def hook_Close(ql, address, params): - return EFI_UNSUPPORTED + ql.loader.smm_context.tseg_open = False + + return EFI_SUCCESS @dxeapi(params = { "This" : POINTER }) def hook_Lock(ql, address, params): - return EFI_UNSUPPORTED + ql.loader.smm_context.tseg_locked = True + + return EFI_SUCCESS + +def _coalesce(seq): + """Coalesce adjacent ranges on list, as long as they share the + same attributes. + """ + + res = [] + curr = seq[0] + + for item in seq[1:]: + start, end, attr = item + + if start == curr[1] and attr == curr[2]: + curr[1] = end + else: + res.append(curr) + curr = item + + res.append(curr) + + return res @dxeapi(params = { - "This" : POINTER, - "MmramMapSize" : POINTER, - "MmramMap" : POINTER + "This" : POINTER, # PTR(EFI_SMM_ACCESS2_PROTOCOL) + "MmramMapSize" : POINTER, # IN OUT PTR(UINTN) + "MmramMap" : POINTER # OUT PTR(EFI_MMRAM_DESCRIPTOR) }) def hook_GetCapabilities(ql, address, params): - write_int64(ql, params["MmramMapSize"], 0) + heap = ql.loader.smm_context.heap + + # get a copy of smm heap chunks list sorted by starting address + chunks = sorted(heap.chunks, key=lambda c: c.address) + + # turn chunks objects into 3-item entries: [start, end, inuse] + chunks = [[ch.address, ch.address + ch.size, ch.inuse] for ch in chunks] + + # if first chunk does not start at heap start, add a dummy free chunk there + if chunks[0][0] != heap.start_address: + chunks.insert(0, [heap.start_address, chunks[0].address, False]) + + # if last chunk does not end at heap end, add a dummy free chunk there + if (chunks[-1][1]) != heap.end_address: + chunks.append([chunks[-1][1], heap.end_address, False]) + + # coalesce adjacent free / used chunks on the list + chunks = _coalesce(chunks) + + size = len(chunks) * EFI_SMRAM_DESCRIPTOR.sizeof() + MmramMapSize = params["MmramMapSize"] + + if read_int64(ql, MmramMapSize) < size: + write_int64(ql, MmramMapSize, size) + return EFI_BUFFER_TOO_SMALL + + MmramMap = params["MmramMap"] + + state = EFI_CACHEABLE + state |= EFI_SMRAM_OPEN if ql.loader.smm_context.tseg_open else EFI_SMRAM_CLOSED + state |= EFI_SMRAM_LOCKED if ql.loader.smm_context.tseg_locked else 0 + + for i, ch in enumerate(chunks): + desc = EFI_SMRAM_DESCRIPTOR() + desc.PhysicalStart = ch[0] + desc.CpuStart = ch[0] + desc.PhysicalSize = ch[1] - ch[0] + desc.RegionState = state | (EFI_ALLOCATED if ch[2] else 0) - if params['MmramMap'] != 0: - write_int64(ql, params["MmramMap"], 0) + desc.saveTo(ql, MmramMap + (i * desc.sizeof())) return EFI_SUCCESS diff --git a/qiling/os/uefi/protocols/EfiSmmBase2Protocol.py b/qiling/os/uefi/protocols/EfiSmmBase2Protocol.py index 280f919ab..88d4114ae 100644 --- a/qiling/os/uefi/protocols/EfiSmmBase2Protocol.py +++ b/qiling/os/uefi/protocols/EfiSmmBase2Protocol.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.const import * from qiling.os.const import * from ..fncc import * @@ -26,7 +26,7 @@ class EFI_SMM_BASE2_PROTOCOL(STRUCT): "InSmram" : POINTER }) def hook_InSmm(ql, address, params): - logging.info(f'InSmram = {ql.loader.in_smm}') + ql.log.info(f'InSmram = {ql.loader.in_smm}') write_int8(ql, params["InSmram"], int(ql.loader.in_smm)) diff --git a/qiling/os/uefi/protocols/EfiSmmCpuProtocol.py b/qiling/os/uefi/protocols/EfiSmmCpuProtocol.py index 8d2dcb88f..8f6fdd656 100644 --- a/qiling/os/uefi/protocols/EfiSmmCpuProtocol.py +++ b/qiling/os/uefi/protocols/EfiSmmCpuProtocol.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.const import * from ..fncc import * from ..ProcessorBind import * from ..UefiBaseType import * +from ..const import * # @see: MdePkg/Include/Protocol/MmCpu.h class EFI_SMM_SAVE_STATE_REGISTER(ENUM_UC): diff --git a/qiling/os/uefi/protocols/EfiSmmSwDispatch2Protocol.py b/qiling/os/uefi/protocols/EfiSmmSwDispatch2Protocol.py index 7b1c265c1..6884f0017 100644 --- a/qiling/os/uefi/protocols/EfiSmmSwDispatch2Protocol.py +++ b/qiling/os/uefi/protocols/EfiSmmSwDispatch2Protocol.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.const import * from ..const import * diff --git a/qiling/os/uefi/protocols/PcdProtocol.py b/qiling/os/uefi/protocols/PcdProtocol.py index 90f9b557f..f4237b11a 100644 --- a/qiling/os/uefi/protocols/PcdProtocol.py +++ b/qiling/os/uefi/protocols/PcdProtocol.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.const import * from ..fncc import * diff --git a/qiling/os/uefi/protocols/common.py b/qiling/os/uefi/protocols/common.py new file mode 100644 index 000000000..230255386 --- /dev/null +++ b/qiling/os/uefi/protocols/common.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from qiling.os.uefi.const import EFI_SUCCESS, EFI_NOT_FOUND, EFI_UNSUPPORTED, EFI_BUFFER_TOO_SMALL, EFI_INVALID_PARAMETER +from qiling.os.uefi.utils import read_int64, write_int64, check_and_notify_protocols, signal_event +from qiling.os.uefi.UefiSpec import EFI_LOCATE_SEARCH_TYPE + +# TODO: get rid of this +pointer_size = 8 + +def LocateHandles(context, params): + SearchType = params["SearchType"] + Protocol = params["Protocol"] + + # get all handles + if SearchType == EFI_LOCATE_SEARCH_TYPE.AllHandles: + handles = context.protocols.keys() + + # get all handles that support the specified protocol + elif SearchType == EFI_LOCATE_SEARCH_TYPE.ByProtocol: + handles = [handle for handle, guid_dic in context.protocols.items() if Protocol in guid_dic] + + else: + handles = [] + + return len(handles) * pointer_size, handles + +def InstallProtocolInterface(context, params): + handle = read_int64(context.ql, params["Handle"]) + + if handle == 0: + handle = context.heap.alloc(1) + + dic = context.protocols.get(handle, {}) + + dic[params["Protocol"]] = params["Interface"] + context.protocols[handle] = dic + + for (event_id, event_dic) in context.ql.loader.events.items(): + if event_dic['Guid'] == params['Protocol']: + # The event was previously registered by 'RegisterProtocolNotify'. + signal_event(context.ql, event_id) + + check_and_notify_protocols(context.ql) + write_int64(context.ql, params["Handle"], handle) + + return EFI_SUCCESS + +def ReinstallProtocolInterface(context, params): + handle = params["Handle"] + + if handle not in context.protocols: + return EFI_NOT_FOUND + + dic = context.protocols[handle] + protocol = params["Protocol"] + + if protocol not in dic: + return EFI_NOT_FOUND + + dic[protocol] = params["NewInterface"] + + return EFI_SUCCESS + +def UninstallProtocolInterface(context, params): + handle = params["Handle"] + + if handle not in context.protocols: + return EFI_NOT_FOUND + + dic = context.protocols[handle] + protocol = params["Protocol"] + + if protocol not in dic: + return EFI_NOT_FOUND + + del dic[protocol] + + return EFI_SUCCESS + +def HandleProtocol(context, params): + handle = params["Handle"] + protocol = params["Protocol"] + interface = params['Interface'] + + if handle in context.protocols: + supported = context.protocols[handle] + + if protocol in supported: + write_int64(context.ql, interface, supported[protocol]) + + return EFI_SUCCESS + + return EFI_UNSUPPORTED + +def LocateHandle(context, params): + buffer_size, handles = LocateHandles(context, params) + + if len(handles) == 0: + return EFI_NOT_FOUND + + ret = EFI_BUFFER_TOO_SMALL + + if read_int64(context.ql, params["BufferSize"]) >= buffer_size: + ptr = params["Buffer"] + + for handle in handles: + write_int64(context.ql, ptr, handle) + ptr += pointer_size + + ret = EFI_SUCCESS + + write_int64(context.ql, params["BufferSize"], buffer_size) + + return ret + +def LocateProtocol(context, params): + protocol = params['Protocol'] + + for handle, guid_dic in context.protocols.items(): + if "Handle" in params and params["Handle"] != handle: + continue + + if protocol in guid_dic: + # write protocol address to out variable Interface + write_int64(context.ql, params['Interface'], guid_dic[protocol]) + return EFI_SUCCESS + + context.ql.log.warning(f'protocol with guid {protocol} not found') + + return EFI_NOT_FOUND + +def InstallConfigurationTable(context, params): + guid = params["Guid"] + table = params["Table"] + + if not guid: + return EFI_INVALID_PARAMETER + + context.install_configuration_table(guid, table) + + return EFI_SUCCESS diff --git a/qiling/os/uefi/rt.py b/qiling/os/uefi/rt.py index bcb06f373..64c81968c 100644 --- a/qiling/os/uefi/rt.py +++ b/qiling/os/uefi/rt.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.const import * from .const import * diff --git a/qiling/os/uefi/shutdown.py b/qiling/os/uefi/shutdown.py index c5028e674..5346ebdab 100644 --- a/qiling/os/uefi/shutdown.py +++ b/qiling/os/uefi/shutdown.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from .utils import check_and_notify_protocols def hook_EndOfExecution(ql): @@ -15,12 +15,11 @@ def hook_EndOfExecution(ql): if check_and_notify_protocols(ql): return - if len(ql.loader.modules) < 1: + if ql.loader.modules: + ql.loader.execute_next_module() + else: if ql.loader.unload_modules(): return - logging.info(f'[+] No more modules to run') + ql.log.info(f'No more modules to run') ql.emu_stop() - else: - ql.loader.execute_next_module() - diff --git a/qiling/os/uefi/smst.py b/qiling/os/uefi/smst.py index 10f7f57ac..2c1c94963 100644 --- a/qiling/os/uefi/smst.py +++ b/qiling/os/uefi/smst.py @@ -1,20 +1,22 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import binascii from qiling.const import * from qiling.os.const import * -from .utils import * -from .fncc import * -from .ProcessorBind import * -from .UefiBaseType import * -from .UefiMultiPhase import * -from .UefiSpec import * - -# TODO: find a better solution than hardcoding this -pointer_size = 8 +from qiling.os.uefi.const import EFI_SUCCESS, EFI_NOT_FOUND, EFI_OUT_OF_RESOURCES +from qiling.os.uefi.utils import * +from qiling.os.uefi.fncc import * +from qiling.os.uefi.ProcessorBind import * +from qiling.os.uefi.UefiBaseType import * +from qiling.os.uefi.UefiMultiPhase import * +from qiling.os.uefi.UefiSpec import * +from qiling.os.uefi.protocols import common +from qiling.os.uefi import rt # @see: MdePkg\Include\Pi\PiSmmCis.h @@ -80,10 +82,7 @@ class EFI_SMM_SYSTEM_TABLE2(STRUCT): "Table" : POINTER # PTR(VOID) }) def hook_SmmInstallConfigurationTable(ql, address, params): - guid = params["Guid"] - table = params["Table"] - - return CoreInstallConfigurationTable(ql, guid, table) + return common.InstallConfigurationTable(ql.loader.smm_context, params) @dxeapi(params = { "type" : INT, # EFI_ALLOCATE_TYPE @@ -158,19 +157,7 @@ def hook_SmmStartupThisAp(ql, address, params): "Interface" : POINTER, # PTR(VOID) }) def hook_SmmInstallProtocolInterface(ql, address, params): - handle = read_int64(ql, params["Handle"]) - - if handle == 0: - handle = ql.loader.smm_context.heap.alloc(1) - - dic = ql.loader.smm_context.protocols.get(handle, {}) - - dic[params["Protocol"]] = params["Interface"] - ql.loader.smm_context.protocols[handle] = dic - check_and_notify_protocols(ql) - write_int64(ql, params["Handle"], handle) - - return EFI_SUCCESS + return common.InstallProtocolInterface(ql.loader.smm_context, params) @dxeapi(params = { "Handle" : POINTER, # EFI_HANDLE @@ -178,20 +165,7 @@ def hook_SmmInstallProtocolInterface(ql, address, params): "Interface" : POINTER # PTR(VOID) }) def hook_SmmUninstallProtocolInterface(ql, address, params): - handle = params["Handle"] - - if handle not in ql.loader.smm_context.protocols: - return EFI_NOT_FOUND - - dic = ql.loader.smm_context.protocols[handle] - protocol = params["Protocol"] - - if protocol not in dic: - return EFI_NOT_FOUND - - del dic[protocol] - - return EFI_SUCCESS + return common.UninstallProtocolInterface(ql.loader.smm_context, params) @dxeapi(params = { "Handle" : POINTER, # EFI_HANDLE @@ -199,18 +173,7 @@ def hook_SmmUninstallProtocolInterface(ql, address, params): "Interface" : POINTER # PTR(PTR(VOID)) }) def hook_SmmHandleProtocol(ql, address, params): - handle = params["Handle"] - protocol = params["Protocol"] - interface = params['Interface'] - - hdict = ql.loader.smm_context.protocols - - if handle in hdict and protocol in hdict[handle]: - write_int64(ql, interface, hdict[handle][protocol]) - - return EFI_SUCCESS - - return EFI_NOT_FOUND + return common.HandleProtocol(ql.loader.smm_context, params) @dxeapi(params = { "Protocol" : GUID, # PTR(EFI_GUID) @@ -237,25 +200,7 @@ def hook_SmmRegisterProtocolNotify(ql, address, params): "Buffer" : POINTER # PTR(EFI_HANDLE) }) def hook_SmmLocateHandle(ql, address, params): - buffer_size, handles = LocateHandles(ql.loader.smm_context, params) - - if len(handles) == 0: - return EFI_NOT_FOUND - - ret = EFI_BUFFER_TOO_SMALL - - if read_int64(ql, params["BufferSize"]) >= buffer_size: - ptr = params["Buffer"] - - for handle in handles: - write_int64(ql, ptr, handle) - ptr += pointer_size - - ret = EFI_SUCCESS - - write_int64(ql, params["BufferSize"], buffer_size) - - return ret + return common.LocateHandle(ql.loader.smm_context, params) @dxeapi(params = { "Protocol" : GUID, # PTR(EFI_GUID) @@ -263,7 +208,7 @@ def hook_SmmLocateHandle(ql, address, params): "Interface" : POINTER # PTR(PTR(VOID)) }) def hook_SmmLocateProtocol(ql, address, params): - return LocateProtocol(ql.loader.smm_context, params) + return common.LocateProtocol(ql.loader.smm_context, params) @dxeapi(params = { "HandlerType" : GUID, @@ -289,6 +234,13 @@ def hook_SmiHandlerUnRegister(ql, address, params): return EFI_SUCCESS def initialize(ql, gSmst : int): + ql.loader.gSmst = gSmst + + gSmmRT = gSmst + EFI_SMM_SYSTEM_TABLE2.sizeof() # smm runtime services + cfg = gSmmRT + EFI_RUNTIME_SERVICES.sizeof() # configuration tables array + + rt.initialize(ql, gSmmRT) + descriptor = { 'struct' : EFI_SMM_SYSTEM_TABLE2, 'fields' : ( @@ -307,8 +259,8 @@ def initialize(ql, gSmst : int): ('NumberOfCpus', None), ('CpuSaveStateSize', None), ('CpuSaveState', None), - ('NumberOfTableEntries', None), - ('SmmConfigurationTable', 0), # TODO: set this to gST conf table array? + ('NumberOfTableEntries', 0), + ('SmmConfigurationTable', cfg), ('SmmInstallProtocolInterface', hook_SmmInstallProtocolInterface), ('SmmUninstallProtocolInterface', hook_SmmUninstallProtocolInterface), ('SmmHandleProtocol', hook_SmmHandleProtocol), @@ -324,6 +276,24 @@ def initialize(ql, gSmst : int): instance = init_struct(ql, gSmst, descriptor) instance.saveTo(ql, gSmst) + # configuration tables bookkeeping + confs = [] + + # these are needed for utils.SmmInstallConfigurationTable + ql.loader.smm_context.conf_table_array = confs + ql.loader.smm_context.conf_table_array_ptr = cfg + + # configuration table data space; its location is calculated by leaving + # enough space for 100 configuration table entries. only a few entries are + # expected, so 100 should definitely suffice + conf_data = cfg + EFI_CONFIGURATION_TABLE.sizeof() * 100 + ql.loader.smm_context.conf_table_data_ptr = conf_data + ql.loader.smm_context.conf_table_data_next_ptr = conf_data + + install_configuration_table(ql.loader.smm_context, "SMM_RUNTIME_SERVICES_TABLE", gSmmRT) + + + __all__ = [ 'EFI_SMM_SYSTEM_TABLE2', 'initialize' diff --git a/qiling/os/uefi/st.py b/qiling/os/uefi/st.py index 2cf62c924..ecd157ad3 100644 --- a/qiling/os/uefi/st.py +++ b/qiling/os/uefi/st.py @@ -1,23 +1,21 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) - -import logging +# -from .utils import CoreInstallConfigurationTable -from .UefiSpec import EFI_SYSTEM_TABLE, EFI_BOOT_SERVICES, EFI_RUNTIME_SERVICES -from . import bs, rt, ds +from qiling.os.uefi import bs, rt, ds +from qiling.os.uefi.utils import install_configuration_table +from qiling.os.uefi.UefiSpec import EFI_SYSTEM_TABLE, EFI_BOOT_SERVICES, EFI_RUNTIME_SERVICES, EFI_CONFIGURATION_TABLE # static mem layout: # # +-- EFI_SYSTEM_TABLE ---------+ # | | # | ... | -# | RuntimeServices -> (1) | -# | BootServices -> (2) | +# | RuntimeServices* -> (1) | +# | BootServices* -> (2) | # | NumberOfTableEntries | -# | ConfigurationTable -> (4) | +# | ConfigurationTable* -> (4) | # +-----------------------------+ # (1) +-- EFI_RUNTIME_SERVICES -----+ # | | @@ -33,62 +31,66 @@ # +-----------------------------+ # (4) +-- EFI_CONFIGURATION_TABLE --+ of HOB_LIST # | VendorGuid | -# | VendorTable -> (5) | +# | VendorTable* -> (5) | # +-----------------------------+ # +-- EFI_CONFIGURATION_TABLE --+ of DXE_SERVICE_TABLE # | VendorGuid | -# | VendorTable -> (3) | +# | VendorTable* -> (3) | # +-----------------------------+ # -# ... sizeof(EFI_CONFIGURATION_TABLE) x 98 skipped +# ... sizeof EFI_CONFIGURATION_TABLE x 98 # -# (5) +-----------------------------+ -# | vendortable | +# (5) +-- VOID* --------------------+ +# | ... | # +-----------------------------+ - -def install_configuration_table(ql, key, table): - cfgtable = ql.os.profile[key] - guid = cfgtable['Guid'] - - if table is None: - table = int(cfgtable['Table'], 0) - - CoreInstallConfigurationTable(ql, guid, table) +# +# ... the remainder of the 256 KiB chunk may be used for more conf table data def initialize(ql, gST : int): + ql.loader.gST = gST + gBS = gST + EFI_SYSTEM_TABLE.sizeof() # boot services gRT = gBS + EFI_BOOT_SERVICES.sizeof() # runtime services gDS = gRT + EFI_RUNTIME_SERVICES.sizeof() # dxe services cfg = gDS + ds.EFI_DXE_SERVICES.sizeof() # configuration tables array - logging.info(f'Global tables:') - logging.info(f' | gST {gST:#010x}') - logging.info(f' | gBS {gBS:#010x}') - logging.info(f' | gRT {gRT:#010x}') - logging.info(f' | gDS {gDS:#010x}') - logging.info(f'') + ql.log.info(f'Global tables:') + ql.log.info(f' | gST {gST:#010x}') + ql.log.info(f' | gBS {gBS:#010x}') + ql.log.info(f' | gRT {gRT:#010x}') + ql.log.info(f' | gDS {gDS:#010x}') + ql.log.info(f'') bs.initialize(ql, gBS) rt.initialize(ql, gRT) ds.initialize(ql, gDS) + instance = EFI_SYSTEM_TABLE() + instance.RuntimeServices = gRT + instance.BootServices = gBS + instance.NumberOfTableEntries = 0 + instance.ConfigurationTable = cfg + + instance.saveTo(ql, gST) + # configuration tables bookkeeping confs = [] # these are needed for utils.CoreInstallConfigurationTable - ql.loader.efi_configuration_table = confs - ql.loader.efi_configuration_table_ptr = cfg + ql.loader.dxe_context.conf_table_array = confs + ql.loader.dxe_context.conf_table_array_ptr = cfg - install_configuration_table(ql, "HOB_LIST", None) - install_configuration_table(ql, "DXE_SERVICE_TABLE", gDS) + # configuration table data space; its location is calculated by leaving + # enough space for 100 configuration table entries. only a few entries are + # expected, so 100 should definitely suffice + conf_data = cfg + EFI_CONFIGURATION_TABLE.sizeof() * 100 + ql.loader.dxe_context.conf_table_data_ptr = conf_data + ql.loader.dxe_context.conf_table_data_next_ptr = conf_data + + install_configuration_table(ql.loader.dxe_context, "HOB_LIST", None) + install_configuration_table(ql.loader.dxe_context, "DXE_SERVICE_TABLE", gDS) - instance = EFI_SYSTEM_TABLE() - instance.RuntimeServices = gRT - instance.BootServices = gBS - instance.NumberOfTableEntries = len(confs) # HOB_LIST and DXE_SERVICES - instance.ConfigurationTable = cfg - instance.saveTo(ql, gST) __all__ = [ 'initialize' diff --git a/qiling/os/uefi/type32.py b/qiling/os/uefi/type32.py index 2ec7db72c..68550c0e0 100644 --- a/qiling/os/uefi/type32.py +++ b/qiling/os/uefi/type32.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # -*- coding: utf-8 -*- # diff --git a/qiling/os/uefi/type64.py b/qiling/os/uefi/type64.py index 41ad11cc4..303a553d9 100644 --- a/qiling/os/uefi/type64.py +++ b/qiling/os/uefi/type64.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # -*- coding: utf-8 -*- # diff --git a/qiling/os/uefi/uefi.py b/qiling/os/uefi/uefi.py index 7352d0bf5..0e1303966 100644 --- a/qiling/os/uefi/uefi.py +++ b/qiling/os/uefi/uefi.py @@ -1,15 +1,18 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from unicorn import UcError + from qiling.os.os import QlOs +from qiling.os.fncc import QlOsFncc -class QlOsUefi(QlOs): +class QlOsUefi(QlOs, QlOsFncc): def __init__(self, ql): - super(QlOsUefi, self).__init__(ql) + QlOs.__init__(self, ql) + QlOsFncc.__init__(self, ql) self.ql = ql self.entry_point = 0 self.running_module = None @@ -19,24 +22,29 @@ def __init__(self, ql): self.PE_RUN = True self.heap = None # Will be initialized by the loader. + def save(self): saved_state = super(QlOsUefi, self).save() saved_state['entry_point'] = self.entry_point return saved_state + def restore(self, saved_state): super(QlOsUefi, self).restore(saved_state) self.entry_point = saved_state['entry_point'] + @staticmethod def notify_after_module_execution(ql, number_of_modules_left): return False + @staticmethod def notify_before_module_execution(ql, module): ql.os.running_module = module return False + def emit_context(self): # TODO: add xmm, ymm, zmm registers rgroups = ( @@ -69,15 +77,16 @@ def emit_context(self): sizes = (64, 32, 16, 8, 8) - logging.error(f'CPU Context:') + self.ql.log.error(f'CPU Context:') for grp in rgroups: - logging.error(', '.join((f'{reg:4s} = {self.ql.reg.read(reg):0{bits // 4}x}') for reg, bits in zip(grp, sizes) if reg)) + self.ql.log.error(', '.join((f'{reg:4s} = {self.ql.reg.read(reg):0{bits // 4}x}') for reg, bits in zip(grp, sizes) if reg)) + + self.ql.log.error(f'') - logging.error(f'') - def emit_hexdump(self, address : int, data : str, num_cols=16): - logging.error('Hexdump:') + def emit_hexdump(self, address: int, data: bytearray, num_cols: int = 16): + self.ql.log.error('Hexdump:') # align hexdump to numbers of columns pre_padding = [None] * (address % num_cols) @@ -87,14 +96,15 @@ def emit_hexdump(self, address : int, data : str, num_cols=16): for i in range(0, len(chars), num_cols): hexdump = ' '.join(f' ' if ch is None else f'{ch:02x}' for ch in chars[i: i + num_cols]) - logging.error(f'{address + i:08x} : {hexdump}') + self.ql.log.error(f'{address + i:08x} : {hexdump}') + + self.ql.log.error(f'') - logging.error(f'') - def emit_disasm(self, address : int, data : str, num_insns=8): + def emit_disasm(self, address: int, data: bytearray, num_insns: int = 8): md = self.ql.create_disassembler() - logging.error('Disassembly:') + self.ql.log.error('Disassembly:') for insn in tuple(md.disasm(data, address))[:num_insns]: opcodes = ''.join(f'{ch:02x}' for ch in insn.bytes[:10]) @@ -102,9 +112,10 @@ def emit_disasm(self, address : int, data : str, num_insns=8): if len(insn.bytes) > 10: opcodes += '.' - logging.error(f'{insn.address:08x} {opcodes:<20s} {insn.mnemonic:<10s} {insn.op_str:s}') + self.ql.log.error(f'{insn.address:08x} : {opcodes:<20s} {insn.mnemonic:<10s} {insn.op_str:s}') + + self.ql.log.error(f'') - logging.error(f'') def emu_error(self): dump_len = 64 @@ -119,12 +130,13 @@ def emu_error(self): containing_image = self.find_containing_image(pc) img_info = f' ({containing_image.path} + {pc - containing_image.base:#x})' if containing_image else '' - logging.error(f'PC = {pc:#010x}{img_info}') + self.ql.log.error(f'PC = {pc:#010x}{img_info}') - logging.error(f'Memory map:') + self.ql.log.error(f'Memory map:') self.ql.mem.show_mapinfo() except UcError: - logging.error(f'Error: PC({pc:#x}) is unreachable') + self.ql.log.error(f'Error: PC({pc:#x}) is unreachable') + def run(self): self.notify_before_module_execution(self.ql, self.running_module) @@ -138,7 +150,7 @@ def run(self): try: self.ql.emu_start(self.ql.loader.entry_point, self.exit_point, self.ql.timeout, self.ql.count) except KeyboardInterrupt as ex: - logging.critical(f'Execution interrupted by user') + self.ql.log.critical(f'Execution interrupted by user') if self.ql._internal_exception is ex: self.ql._internal_exception = None diff --git a/qiling/os/uefi/utils.py b/qiling/os/uefi/utils.py index 56cefadbc..7fcf2062f 100644 --- a/qiling/os/uefi/utils.py +++ b/qiling/os/uefi/utils.py @@ -1,69 +1,72 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import ctypes -import struct -import logging +import binascii -from .const import * -from .UefiSpec import EFI_LOCATE_SEARCH_TYPE, EFI_CONFIGURATION_TABLE +from uuid import UUID +from typing import Optional -def check_and_notify_protocols(ql): - if len(ql.loader.notify_list) > 0: +from qiling.os.uefi.const import EFI_SUCCESS, EFI_INVALID_PARAMETER +from qiling.os.uefi.UefiSpec import EFI_CONFIGURATION_TABLE, EFI_SYSTEM_TABLE +from qiling.os.uefi.UefiBaseType import EFI_GUID + +def signal_event(ql, event_id: int) -> None: + event = ql.loader.events[event_id] + + if not event["Set"]: + event["Set"] = True + notify_func = event["NotifyFunction"] + notify_context = event["NotifyContext"] + ql.loader.notify_list.append((event_id, notify_func, notify_context)) + +def check_and_notify_protocols(ql) -> bool: + if ql.loader.notify_list: event_id, notify_func, notify_context = ql.loader.notify_list.pop(0) - logging.info(f'Notify event:{event_id} calling:{notify_func:x} context:{notify_context:x}') + ql.log.info(f'Notify event:{event_id} calling:{notify_func:x} context:{notify_context:x}') - ql.stack_push(ql.loader.end_of_execution_ptr) - ql.reg.rcx = notify_context - ql.reg.arch_pc = notify_func + ql.loader.call_function(notify_func, [notify_context], ql.loader.end_of_execution_ptr) return True return False -def ptr_read8(ql, addr : int) -> int: +def ptr_read8(ql, addr: int) -> int: """Read BYTE data from a pointer """ - val = ql.mem.read(addr, 1) - - return struct.unpack(' None: """Write BYTE data to a pointer """ - ql.mem.write(addr, struct.pack(' int: +def ptr_read32(ql, addr: int) -> int: """Read DWORD data from a pointer """ - val = ql.mem.read(addr, 4) + return ql.unpack32(ql.mem.read(addr, 4)) - return struct.unpack(' None: """Write DWORD data to a pointer """ - ql.mem.write(addr, struct.pack(' int: +def ptr_read64(ql, addr: int) -> int: """Read QWORD data from a pointer """ - val = ql.mem.read(addr, 8) - - return struct.unpack(' None: """Write QWORD data to a pointer """ - ql.mem.write(addr, struct.pack(' EFI_GUID: + """Construct an EFI_GUID structure out of a plain GUID string. + """ - return EFI_NOT_FOUND + buff = UUID(hex=guid).bytes_le -def to_byte_values(val, nbytes): - while nbytes > 0: - yield val & 0xff - val >>= 8 - nbytes -= 1 + return EFI_GUID.from_buffer_copy(buff) -# see: MdeModulePkg/Core/Dxe/Misc/InstallConfigurationTable.c -def CoreInstallConfigurationTable(ql, guid, table): - if not guid: - return EFI_INVALID_PARAMETER +def install_configuration_table(context, key: str, table: int): + """Create a new Configuration Table entry and add it to the list. - confs = ql.loader.efi_configuration_table + Args: + ql : Qiling instance + key : profile section name that holds the entry data + table : address of configuration table data; if None, data will be read + from profile section into memory + """ - # find configuration table entry by guid. if found, idx would be set to the entry index - # in the array. if not, idx would be set to one past end of array - if guid not in confs: - confs.append(guid) - #TODO: NumberOfTableEntries++ + cfgtable = context.ql.os.profile[key] + guid = cfgtable['Guid'] - idx = confs.index(guid) - ptr = ql.loader.efi_configuration_table_ptr + (idx * EFI_CONFIGURATION_TABLE.sizeof()) + # if pointer to table data was not specified, load table data + # from profile and have table pointing to it + if table is None: + data = binascii.unhexlify(cfgtable['TableData']) + table = context.conf_table_data_next_ptr - instance = EFI_CONFIGURATION_TABLE() - vendguid = instance.VendorGuid + context.ql.mem.write(table, data) + context.conf_table_data_next_ptr += len(data) - # parse guid string - elems = [int(e, 16) for e in guid.split('-')] - elems[3] = (elems[3] << 48) | elems[4] + context.install_configuration_table(guid, table) - # populate vendor guid struct - vendguid.Data1 = elems[0] - vendguid.Data2 = elems[1] - vendguid.Data3 = elems[2] +def GetEfiConfigurationTable(context, guid: str) -> Optional[int]: + """Find a configuration table by its GUID. + """ - for i, el in enumerate(to_byte_values(elems[3], 8)): - vendguid.Data4[i] = el + guid = guid.lower() + confs = context.conf_table_array - instance.VendorTable = table - instance.saveTo(ql, ptr) + if guid in confs: + idx = confs.index(guid) + ptr = context.conf_table_array_ptr + (idx * EFI_CONFIGURATION_TABLE.sizeof()) - # keep track of guid - confs.append(guid) + return ptr - return EFI_SUCCESS + # not found + return None diff --git a/qiling/os/utils.py b/qiling/os/utils.py index 3850b85c7..811b10604 100644 --- a/qiling/os/utils.py +++ b/qiling/os/utils.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# """ This module is intended for general purpose functions that are only used in qiling.os """ -import ctypes, inspect, os, struct, uuid, logging +import ctypes, inspect, os, struct, uuid from json import dumps from pathlib import Path, PurePosixPath, PureWindowsPath, PosixPath, WindowsPath @@ -16,7 +16,6 @@ from unicorn.x86_const import * from unicorn.arm64_const import * from unicorn.mips_const import * - from capstone import * from capstone.arm_const import * from capstone.x86_const import * @@ -24,10 +23,10 @@ from capstone.mips_const import * from keystone import * + from qiling.const import * from qiling.exception import * from .const import * - from qiling.os.windows.wdk_const import * from qiling.os.windows.structs import * from qiling.utils import verify_ret @@ -127,6 +126,155 @@ def __init__(self, ql): self._disasm_hook = None self._block_hook = None + + def string_appearance(self, string): + strings = string.split(" ") + for string in strings: + val = self.appeared_strings.get(string, set()) + val.add(self.syscalls_counter) + self.appeared_strings[string] = val + + + def read_wstring(self, address): + result = "" + char = self.ql.mem.read(address, 2) + while char.decode(errors="ignore") != "\x00\x00": + address += 2 + result += char.decode(errors="ignore") + char = self.ql.mem.read(address, 2) + # We need to remove \x00 inside the string. Compares do not work otherwise + result = result.replace("\x00", "") + self.string_appearance(result) + return result + + + def read_cstring(self, address): + result = "" + char = self.ql.mem.read(address, 1) + while char.decode(errors="ignore") != "\x00": + address += 1 + result += char.decode(errors="ignore") + char = self.ql.mem.read(address, 1) + self.string_appearance(result) + return result + + def print_function(self, address, function_name, params, ret, passthru=False): + PRINTK_LEVEL = { + 0: 'KERN_EMERGE', + 1: 'KERN_ALERT', + 1: 'KERN_CRIT', + 2: 'KERN_INFO', + 3: 'KERN_ERR', + 4: 'KERN_WARNING', + 5: 'KERN_NOTICE', + 6: 'KERN_INFO', + 7: 'KERN_DEBUG', + 8: '', + 9: 'KERN_CONT', + } + + if function_name.startswith('hook_'): + function_name = function_name[5:] + + if function_name in ("__stdio_common_vfprintf", "__stdio_common_vfwprintf", "printf", "wsprintfW", "sprintf"): + return + + def _parse_param(param): + name, value = param + + if type(value) is str: + return f'{name:s} = "{value}"' + elif type(value) is bytearray: + return f'{name:s} = "{value.decode("utf-8")}"' + elif type(value) is tuple: + # we just need the string, not the address in the log + return f'{name:s} = "{value[1]}"' + + # default to hexadecimal representation + return f'{name:s} = {value:#x}' + + # arguments list + fargs = (_parse_param(param) for param in params.items()) + + # optional suffixes: return value and passthrough + fret = f' = {ret:#x}' if ret is not None else '' + fpass = f' (PASSTHRU)' if passthru else '' + + #TODO: Old code from demigod, ready to cleanup + if self.ql.ostype in QL_OS_POSIX and self.ql.loader.is_driver: + log = '0x%0.2x: %s(' % (address, function_name) + for each in params: + value = params[each] + if type(value) == str or type(value) == bytearray: + if function_name == 'printk': + info = value[:2] + try: + level = PRINTK_LEVEL[int(info[1])] + value = value[2:] + log += '%s = %s "%s", ' %(each, level, value) + except: + log += '%s = "%s", ' %(each, value) + else: + log += '%s = "%s", ' %(each, value) + elif type(value) == tuple: + log += '%s = 0x%x, ' % (each, value[0]) + else: + log += '%s = 0x%x, ' % (each, value) + log = log.strip(", ") + log += ')' + if ret is not None: + # do not print result for printk() + if function_name != 'printk': + log += ' = 0x%x' % ret + else: + log = f'0x{address:02x}: {function_name:s}({", ".join(fargs)}){fret}{fpass}' + + if self.ql.output == QL_OUTPUT.DEBUG: + self.ql.log.debug(log) + else: + log = log.partition(" ")[-1] + self.ql.log.info(log) + + def vprintf(self, address, fmt, params_addr, name, wstring=False): + count = fmt.count("%") + params = [] + if count > 0: + for i in range(count): + param = self.ql.mem.read(params_addr + i * self.ql.pointersize, self.ql.pointersize) + params.append( + self.ql.unpack(param) + ) + return self.printf(address, fmt, params, name, wstring) + + def printf(self, address, fmt, params, name, wstring=False): + if len(params) > 0: + formats = fmt.split("%")[1:] + index = 0 + for f in formats: + if f.startswith("s"): + if wstring: + params[index] = self.read_wstring(params[index]) + else: + params[index] = self.read_cstring(params[index]) + index += 1 + + output = '%s(format = %s' % (name, repr(fmt)) + for each in params: + if type(each) == str: + output += ', "%s"' % each + else: + output += ', 0x%0.2x' % each + output += ')' + fmt = fmt.replace("%llx", "%x") + stdout = fmt % tuple(params) + output += " = 0x%x" % len(stdout) + else: + output = '%s(format = %s) = 0x%x' % (name, repr(fmt), len(fmt)) + stdout = fmt + self.ql.log.info(output) + self.ql.os.stdout.write(bytes(stdout, 'utf-8')) + return len(stdout), stdout + def lsbmsb_convert(self, sc, size=4): split_bytes = [] n = size @@ -158,7 +306,7 @@ def transform_to_link_path(self, path): # Sanity check. if cur_path[0] != '/': - logging.info(f"[!] Warning: cur_path doesn't start with a /") + self.ql.log.info(f"Warning: cur_path doesn't start with a /") rootfs = self.ql.rootfs real_path = self.convert_path(rootfs, cur_path, path) @@ -175,7 +323,7 @@ def transform_to_real_path(self, path): # Sanity check. if cur_path[0] != '/': - logging.info(f"[!] Warning: cur_path must start with /") + self.ql.log.info(f"Warning: cur_path must start with /") rootfs = self.ql.rootfs real_path = self.convert_path(rootfs, cur_path, path) @@ -197,20 +345,20 @@ def transform_to_relative_path(self, path): return str(Path(cur_path) / path) def post_report(self): - logging.debug("[+] Syscalls called") + self.ql.log.debug("Syscalls called") for key, values in self.ql.os.syscalls.items(): - logging.debug("[-] %s:" % key) + self.ql.log.debug("%s:" % key) for value in values: - logging.debug("[-] %s " % str(dumps(value))) - logging.debug("[+] Registries accessed") + self.ql.log.debug("%s " % str(dumps(value))) + self.ql.log.debug("Registries accessed") for key, values in self.ql.os.registry_manager.accessed.items(): - logging.debug("[-] %s:" % key) + self.ql.log.debug("%s:" % key) for value in values: - logging.debug("[-] %s " % str(dumps(value))) - logging.debug("[+] Strings") + self.ql.log.debug("%s " % str(dumps(value))) + self.ql.log.debug("Strings") for key, values in self.ql.os.appeared_strings.items(): val = " ".join([str(word) for word in values]) - logging.debug("[-] %s: %s" % (key, val)) + self.ql.log.debug("%s: %s" % (key, val)) def exec_arbitrary(self, start, end): @@ -220,7 +368,7 @@ def exec_arbitrary(self, start, end): ret = self.ql.stack_read(0) def restore(ql): - logging.debug(f"[+] Executed code from 0x{start:x} to 0x{end:x}") + self.ql.log.debug(f"Executed code from 0x{start:x} to 0x{end:x}") # now we can restore the register to be where we were supposed to old_hook_addr = ql.reg.arch_pc ql.reg.arch_sp = old_sp + (ql.archbit // 8) @@ -244,6 +392,8 @@ def disassembler(self, ql, address, size): if not self.md: self.md = self.ql.create_disassembler() + elif self.ql.archtype == QL_ARCH.ARM: # Update disassembler for arm considering thumb swtich. + self.md = self.ql.create_disassembler() insn = self.md.disasm(tmp, address) opsize = int(size) @@ -262,18 +412,18 @@ def disassembler(self, ql, address, size): log_data += '\n> ' first = False log_data += "%s %s" % (i.mnemonic, i.op_str) - logging.info(log_data) + self.ql.log.info(log_data) if self.ql.output == QL_OUTPUT.DUMP: for reg in self.ql.reg.register_mapping: if isinstance(reg, str): REG_NAME = reg REG_VAL = self.ql.reg.read(reg) - logging.debug("%s\t:\t 0x%x" % (REG_NAME, REG_VAL)) + self.ql.log.debug("%s\t:\t 0x%x" % (REG_NAME, REG_VAL)) def setup_output(self): def ql_hook_block_disasm(ql, address, size): - logging.info("\n[+] Tracing basic block at 0x%x" % (address)) + self.ql.log.info("\nTracing basic block at 0x%x" % (address)) if self._disasm_hook: self._disasm_hook.remove() @@ -298,109 +448,6 @@ def read_guid(self, address): raw_guid = self.ql.mem.read(address, 16) return uuid.UUID(bytes_le=bytes(raw_guid)) - - def string_appearance(self, string): - strings = string.split(" ") - for string in strings: - val = self.appeared_strings.get(string, set()) - val.add(self.syscalls_counter) - self.appeared_strings[string] = val - - - def read_wstring(self, address): - result = "" - char = self.ql.mem.read(address, 2) - while char.decode(errors="ignore") != "\x00\x00": - address += 2 - result += char.decode(errors="ignore") - char = self.ql.mem.read(address, 2) - # We need to remove \x00 inside the string. Compares do not work otherwise - result = result.replace("\x00", "") - self.string_appearance(result) - return result - - - def read_cstring(self, address): - result = "" - char = self.ql.mem.read(address, 1) - while char.decode(errors="ignore") != "\x00": - address += 1 - result += char.decode(errors="ignore") - char = self.ql.mem.read(address, 1) - self.string_appearance(result) - return result - - def print_function(self, address, function_name, params, ret, passthru=False): - if function_name.startswith('hook_'): - function_name = function_name[5:] - - if function_name in ("__stdio_common_vfprintf", "__stdio_common_vfwprintf", "printf", "wsprintfW", "sprintf"): - return - - def _parse_param(param): - name, value = param - - if isinstance(value, str) or type(value) == bytearray: - return f'{name:s} = "{value}"' - elif isinstance(value, tuple): - # we just need the string, not the address in the log - return f'{name:s} = "{value[1]}"' - - # default to hexadecimal representation - return f'{name:s} = {value:#x}' - - # arguments list - fargs = (_parse_param(param) for param in params.items()) - - # optional suffixes: return value and passthrough - fret = f' = {ret:#x}' if ret is not None else '' - fpass = f' (PASSTHRU)' if passthru else '' - - log = f'{address:02x}: {function_name:s}({", ".join(fargs)}){fret}{fpass}' - - if self.ql.output == QL_OUTPUT.DEBUG: - logging.debug(log) - else: - log = log.partition(" ")[-1] - logging.info(log) - - def printf(self, address, fmt, params_addr, name, wstring=False): - count = fmt.count("%") - params = [] - if count > 0: - for i in range(count): - param = self.ql.mem.read(params_addr + i * self.ql.pointersize, self.ql.pointersize) - params.append( - self.ql.unpack(param) - ) - - formats = fmt.split("%")[1:] - index = 0 - for f in formats: - if f.startswith("s"): - if wstring: - params[index] = self.read_wstring(params[index]) - else: - params[index] = self.read_cstring(params[index]) - index += 1 - - output = '%s(format = %s' % (name, repr(fmt)) - for each in params: - if type(each) == str: - output += ', "%s"' % each - else: - output += ', 0x%0.2x' % each - output += ')' - fmt = fmt.replace("%llx", "%x") - stdout = fmt % tuple(params) - output += " = 0x%x" % len(stdout) - else: - output = '%s(format = %s) = 0x%x' % (name, repr(fmt), len(fmt)) - stdout = fmt - logging.info(output) - self.ql.os.stdout.write(bytes(stdout, 'utf-8')) - return len(stdout), stdout - def io_Write(self, in_buffer): if self.ql.ostype == QL_OS.WINDOWS: @@ -598,7 +645,7 @@ def build_mdl(buffer_size, data=None): #print("32 irpstack offset = 0x%x" %IRP32.irpstack.offset) #print("irp at %x, irpstack at %x" %(irp_addr, irpstack_addr)) - logging.info("IRP is at 0x%x, IO_STACK_LOCATION is at 0x%x" %(irp_addr, irpstack_addr)) + self.ql.log.info("IRP is at 0x%x, IO_STACK_LOCATION is at 0x%x" %(irp_addr, irpstack_addr)) irpstack.Parameters.DeviceIoControl.IoControlCode = ioctl_code(devicetype, function, ctl_method, access) irpstack.Parameters.DeviceIoControl.OutputBufferLength = output_buffer_size @@ -637,7 +684,7 @@ def build_mdl(buffer_size, data=None): self.ql.mem.write(irp_addr, bytes(irp)) # set function args - logging.info("Executing IOCTL with DeviceObject = 0x%x, IRP = 0x%x" %(self.ql.loader.driver_object.DeviceObject, irp_addr)) + self.ql.log.info("Executing IOCTL with DeviceObject = 0x%x, IRP = 0x%x" %(self.ql.loader.driver_object.DeviceObject, irp_addr)) self.set_function_args((self.ql.loader.driver_object.DeviceObject, irp_addr)) try: diff --git a/qiling/os/windows/api.py b/qiling/os/windows/api.py index 52e975450..dbea95d69 100644 --- a/qiling/os/windows/api.py +++ b/qiling/os/windows/api.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.const import * from .fncc import * diff --git a/qiling/os/windows/clipboard.py b/qiling/os/windows/clipboard.py index 5ee35231a..02c1dc007 100644 --- a/qiling/os/windows/clipboard.py +++ b/qiling/os/windows/clipboard.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # A Simple Windows Clipboard Simulation NOT_LOCKED = -1 diff --git a/qiling/os/windows/const.py b/qiling/os/windows/const.py index 490460980..58f69f970 100644 --- a/qiling/os/windows/const.py +++ b/qiling/os/windows/const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from Registry import Registry # ERRORS CODE diff --git a/qiling/os/windows/dlls/__init__.py b/qiling/os/windows/dlls/__init__.py index e77136652..5653325cc 100644 --- a/qiling/os/windows/dlls/__init__.py +++ b/qiling/os/windows/dlls/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from .advapi32 import * from .msvcrt import * from .user32 import * diff --git a/qiling/os/windows/dlls/advapi32.py b/qiling/os/windows/dlls/advapi32.py index 3a94cd272..e69947c79 100644 --- a/qiling/os/windows/dlls/advapi32.py +++ b/qiling/os/windows/dlls/advapi32.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import struct, logging +# +import struct + from qiling.os.windows.fncc import * from qiling.os.const import * from qiling.os.windows.utils import * @@ -16,10 +17,10 @@ def _RegOpenKey(ql, address, params): hKey = params["hKey"] s_lpSubKey = params["lpSubKey"] phkResult = params["phkResult"] - logging.debug("[+] Key %s %s" % (hKey, s_lpSubKey)) + ql.log.debug("Key %s %s" % (hKey, s_lpSubKey)) if hKey not in REG_KEYS: - logging.debug("[!] Key %s %s not present" % (hKey, s_lpSubKey)) + ql.log.debug("Key %s %s not present" % (hKey, s_lpSubKey)) return ERROR_FILE_NOT_FOUND else: s_hKey = REG_KEYS[hKey] @@ -28,11 +29,11 @@ def _RegOpenKey(ql, address, params): # Keys in the profile are saved as KEY\PARAM = VALUE, so i just want to check that the key is the same keys_profile = [key.rsplit("\\", 1)[0] for key in ql.os.profile["REGISTRY"].keys()] if key.lower() in keys_profile: - logging.debug("[+] Using profile for key of %s" % key) + ql.log.debug("Using profile for key of %s" % key) ql.os.registry_manager.access(key) else: if not ql.os.registry_manager.exists(key): - logging.debug("[!] Value key %s not present" % key) + ql.log.debug("Value key %s not present" % key) return ERROR_FILE_NOT_FOUND # new handle @@ -61,7 +62,7 @@ def RegQueryValue(ql, address, params): try: # Keys in the profile are saved as KEY\PARAM = VALUE, so i just want to check that the key is the same value = ql.os.profile["REGISTRY"][s_hKey + "\\" + s_lpValueName] - logging.debug("[+] Using profile for value of key %s" % (s_hKey + "\\" + s_lpValueName,)) + ql.log.debug("Using profile for value of key %s" % (s_hKey + "\\" + s_lpValueName,)) # TODO i have no fucking idea on how to set a None value, fucking configparser if value == "None": return ERROR_FILE_NOT_FOUND @@ -75,7 +76,7 @@ def RegQueryValue(ql, address, params): # error key if reg_type is None or value is None: - logging.debug("[!] Key value not found") + ql.log.debug("Key value not found") return ERROR_FILE_NOT_FOUND else: # set lpData @@ -407,7 +408,7 @@ def hook_GetTokenInformation(ql, address, params): information_value = token.get(information) ql.mem.write(return_point, len(information_value).to_bytes(4, byteorder="little")) return_size = int.from_bytes(ql.mem.read(return_point, 4), byteorder="little") - logging.debug("[=] The target is checking for its permissions") + ql.log.debug("The target is checking for its permissions") if return_size > max_size: ql.os.last_error = ERROR_INSUFFICIENT_BUFFER return 0 @@ -415,7 +416,7 @@ def hook_GetTokenInformation(ql, address, params): ql.mem.write(dst, information_value) return 1 else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") # PUCHAR GetSidSubAuthorityCount( diff --git a/qiling/os/windows/dlls/cng.py b/qiling/os/windows/dlls/cng.py index 7f9b06171..96c4c5412 100644 --- a/qiling/os/windows/dlls/cng.py +++ b/qiling/os/windows/dlls/cng.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.os.windows.const import * from qiling.os.windows.fncc import * from qiling.os.const import * @@ -52,5 +52,5 @@ def hook_RtlGetVersion(ql, address, params): ql.os.profile.getint("SYSTEM", "minorVersion").to_bytes(4, byteorder="little")) - logging.debug("[=] The sample is checking the windows Version!") + ql.log.debug("The sample is checking the windows Version!") return STATUS_SUCCESS diff --git a/qiling/os/windows/dlls/const.py b/qiling/os/windows/dlls/const.py index e2ce8c418..986cf7e0d 100644 --- a/qiling/os/windows/dlls/const.py +++ b/qiling/os/windows/dlls/const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# IDCANCEL = 2 diff --git a/qiling/os/windows/dlls/crypt32.py b/qiling/os/windows/dlls/crypt32.py index 0ab01b0e2..a1d61536f 100644 --- a/qiling/os/windows/dlls/crypt32.py +++ b/qiling/os/windows/dlls/crypt32.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import base64 -import logging + from qiling.os.windows.fncc import * from qiling.os.const import * from qiling.os.windows.utils import * @@ -34,7 +34,7 @@ def hook_CryptStringToBinaryA(ql, address, params): size_dst = int.from_bytes(ql.mem.read(size_dst_pointer, 4), byteorder="little") if size_dst != 0 and size_dst < size_src: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") if flag_src == CRYPT_STRING_BASE64: # Had a padding error, hope this always works add_pad = 4 - (len(string_src) % 4) @@ -42,9 +42,9 @@ def hook_CryptStringToBinaryA(ql, address, params): string_src += "=" * add_pad output = base64.b64decode(string_src).decode("utf-16le") + "\x00" else: - logging.debug("Flag") - logging.debug(flag_src) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug("Flag") + ql.log.debug(flag_src) + raise QlErrorNotImplemented("API not implemented") if string_dst == 0: # Only wants the length diff --git a/qiling/os/windows/dlls/kernel32/consoleapi2.py b/qiling/os/windows/dlls/kernel32/consoleapi2.py index 32f361d9d..057cc85ef 100644 --- a/qiling/os/windows/dlls/kernel32/consoleapi2.py +++ b/qiling/os/windows/dlls/kernel32/consoleapi2.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/consoleapi3.py b/qiling/os/windows/dlls/kernel32/consoleapi3.py index 4f3018adf..7a5b4e443 100644 --- a/qiling/os/windows/dlls/kernel32/consoleapi3.py +++ b/qiling/os/windows/dlls/kernel32/consoleapi3.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/debugapi.py b/qiling/os/windows/dlls/kernel32/debugapi.py index 4a04b8970..f02aa1240 100644 --- a/qiling/os/windows/dlls/kernel32/debugapi.py +++ b/qiling/os/windows/dlls/kernel32/debugapi.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time -import logging + + from qiling.os.windows.const import * from qiling.os.const import * from qiling.os.windows.fncc import * @@ -40,7 +41,7 @@ def hook_CheckRemoteDebuggerPresent(ql, address, params): @winsdkapi(cc=STDCALL, dllname=dllname) def hook_OutputDebugStringW(ql, address, params): string = params["lpOutputString"] - logging.info('OutputDebugStringW: "%s"' % (string.encode())) + ql.log.info('OutputDebugStringW: "%s"' % (string.encode())) return 0 @@ -49,5 +50,5 @@ def hook_OutputDebugStringW(ql, address, params): # ); @winsdkapi(cc=STDCALL, dllname=dllname) def hook_OutputDebugStringA(ql, address, params): - logging.info('OutputDebugStringA: "%s"' % (params['lpOutputString'])) + ql.log.info('OutputDebugStringA: "%s"' % (params['lpOutputString'])) return 0 diff --git a/qiling/os/windows/dlls/kernel32/errhandlingapi.py b/qiling/os/windows/dlls/kernel32/errhandlingapi.py index 312eca3d5..d366f6661 100644 --- a/qiling/os/windows/dlls/kernel32/errhandlingapi.py +++ b/qiling/os/windows/dlls/kernel32/errhandlingapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/fibersapi.py b/qiling/os/windows/dlls/kernel32/fibersapi.py index 3204fa8fb..a2adb75c6 100644 --- a/qiling/os/windows/dlls/kernel32/fibersapi.py +++ b/qiling/os/windows/dlls/kernel32/fibersapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/fileapi.py b/qiling/os/windows/dlls/kernel32/fileapi.py index d53e0c554..f23694ec3 100644 --- a/qiling/os/windows/dlls/kernel32/fileapi.py +++ b/qiling/os/windows/dlls/kernel32/fileapi.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import struct, time, os -import struct, time, os, logging from shutil import copyfile from datetime import datetime + from qiling.exception import * from qiling.os.windows.const import * @@ -15,7 +17,6 @@ from qiling.os.windows.utils import * from qiling.os.windows.thread import * from qiling.os.windows.handle import * -from qiling.os.windows.utils import canonical_path from qiling.exception import * from qiling.os.windows.structs import * @@ -32,7 +33,7 @@ def hook_GetFileType(ql, address, params): else: obj = ql.os.handle_manager.get(hFile) if obj is None: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") else: # technically is not always a type_char but.. almost ret = FILE_TYPE_CHAR @@ -143,7 +144,7 @@ def hook_ReadFile(ql, address, params): # TODO maybe insert a good random generation input s = (b"A" * (nNumberOfBytesToRead - 1)) + b"\x00" else: - logging.debug("Insert input") + ql.log.debug("Insert input") s = ql.os.stdin.read(nNumberOfBytesToRead) slen = len(s) read_len = slen @@ -202,7 +203,6 @@ def hook_WriteFile(ql, address, params): def _CreateFile(ql, address, params, name): ret = INVALID_HANDLE_VALUE - s_lpFileName = params["lpFileName"] dwDesiredAccess = params["dwDesiredAccess"] dwShareMode = params["dwShareMode"] @@ -218,16 +218,15 @@ def _CreateFile(ql, address, params, name): else: mode += "r" - # create thread handle try: f = ql.os.fs_mapper.open(s_lpFileName, mode) except FileNotFoundError: ql.os.last_error = ERROR_FILE_NOT_FOUND return INVALID_HANDLE_VALUE + new_handle = Handle(obj=f) ql.os.handle_manager.append(new_handle) ret = new_handle.id - return ret @@ -366,7 +365,7 @@ def hook_GetVolumeInformationW(ql, address, params): system_type = (ql.os.profile["VOLUME"]["type"] + "\x00").encode("utf-16le") ql.mem.write(pt_system_type, system_type) else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") return 1 @@ -381,7 +380,7 @@ def hook_GetDriveTypeW(ql, address, params): return DRIVE_FIXED # TODO add configuration for drives else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") return DRIVE_NO_ROOT_DIR @@ -409,7 +408,7 @@ def hook_GetDiskFreeSpaceW(ql, address, params): ql.mem.write(pt_free_clust, free_clust) ql.mem.write(pt_total_clust, total_clust) else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") return 0 @@ -560,12 +559,13 @@ def hook_UnmapViewOfFile(ql, address, params): "bFailIfExists": DWORD }) def hook_CopyFileA(ql, address, params): - lpExistingFileName = canonical_path(ql, params["lpExistingFileName"]) - lpNewFileName = canonical_path(ql, params["lpNewFileName"]) + lpExistingFileName = ql.os.transform_to_real_path(params["lpExistingFileName"]) + lpNewFileName = ql.os.transform_to_real_path(params["lpNewFileName"]) bFailIfExists = params["bFailIfExists"] - + if bFailIfExists and os.path.exists(lpNewFileName): return 0 + copyfile(lpExistingFileName, lpNewFileName) return 1 diff --git a/qiling/os/windows/dlls/kernel32/handleapi.py b/qiling/os/windows/dlls/kernel32/handleapi.py index f07602bd5..774f5f3fe 100644 --- a/qiling/os/windows/dlls/kernel32/handleapi.py +++ b/qiling/os/windows/dlls/kernel32/handleapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/heapapi.py b/qiling/os/windows/dlls/kernel32/heapapi.py index 2015541a5..9c87099fe 100644 --- a/qiling/os/windows/dlls/kernel32/heapapi.py +++ b/qiling/os/windows/dlls/kernel32/heapapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/interlockedapi.py b/qiling/os/windows/dlls/kernel32/interlockedapi.py index 2771176d9..8a2dca6c7 100644 --- a/qiling/os/windows/dlls/kernel32/interlockedapi.py +++ b/qiling/os/windows/dlls/kernel32/interlockedapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/ioapiset.py b/qiling/os/windows/dlls/kernel32/ioapiset.py index 791d118f2..17c62a29f 100644 --- a/qiling/os/windows/dlls/kernel32/ioapiset.py +++ b/qiling/os/windows/dlls/kernel32/ioapiset.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/libloaderapi.py b/qiling/os/windows/dlls/kernel32/libloaderapi.py index bc2d62211..fcb0b7b51 100644 --- a/qiling/os/windows/dlls/kernel32/libloaderapi.py +++ b/qiling/os/windows/dlls/kernel32/libloaderapi.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os -import logging + from qiling.exception import * from qiling.os.windows.const import * @@ -29,7 +29,7 @@ def _GetModuleHandle(ql, address, params): if lpModuleName in ql.loader.dlls: ret = ql.loader.dlls[lpModuleName] else: - logging.debug("[!] Library %s not imported" % lpModuleName) + ql.log.debug("Library %s not imported" % lpModuleName) ret = 0 return ret @@ -77,7 +77,7 @@ def hook_GetModuleFileNameA(ql, address, params): # GetModuleHandle can return pe_image_address as handle, and GetModuleFileName will try to retrieve it. # Pretty much 0 and pe_image_address value should do the same operations - if not ql.shellcoder and (hModule == 0 or hModule == ql.loader.pe_image_address): + if not ql.code and (hModule == 0 or hModule == ql.loader.pe_image_address): filename = ql.loader.filepath filename_len = len(filename) if filename_len > nSize - 1: @@ -87,8 +87,8 @@ def hook_GetModuleFileNameA(ql, address, params): ret = filename_len ql.mem.write(lpFilename, filename + b"\x00") else: - logging.debug("hModule %x" % hModule) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug("hModule %x" % hModule) + raise QlErrorNotImplemented("API not implemented") return ret @@ -105,7 +105,7 @@ def hook_GetModuleFileNameW(ql, address, params): nSize = params["nSize"] # GetModuleHandle can return pe_image_address as handle, and GetModuleFileName will try to retrieve it. # Pretty much 0 and pe_image_address value should do the same operations - if not ql.shellcoder and (hModule == 0 or hModule == ql.loader.pe_image_address): + if not ql.code and (hModule == 0 or hModule == ql.loader.pe_image_address): filename = ql.loader.filepath.decode('ascii').encode('utf-16le') filename_len = len(filename) if filename_len > nSize - 1: @@ -115,7 +115,7 @@ def hook_GetModuleFileNameW(ql, address, params): ret = filename_len ql.mem.write(lpFilename, filename + b"\x00") else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") return ret @@ -140,7 +140,7 @@ def hook_GetProcAddress(ql, address, params): try: dll_name = [key for key, value in ql.loader.dlls.items() if value == params['hModule']][0] except IndexError as ie: - logging.info('[!] Failed to import function "%s" with handle 0x%X' % (lpProcName, params['hModule'])) + ql.log.info('Failed to import function "%s" with handle 0x%X' % (lpProcName, params['hModule'])) return 0 # Handle case where module is self @@ -161,7 +161,7 @@ def hook_GetProcAddress(ql, address, params): @winsdkapi(cc=STDCALL, dllname=dllname) def hook_LoadLibraryA(ql, address, params): lpLibFileName = params["lpLibFileName"] - if not ql.shellcoder and lpLibFileName == ql.loader.filepath.decode(): + if not ql.code and lpLibFileName == ql.loader.filepath.decode(): # Loading self return ql.loader.pe_image_address dll_base = ql.loader.load_dll(lpLibFileName.encode()) @@ -257,4 +257,4 @@ def hook_SetDefaultDllDirectories(ql, address, params): if value == LOAD_LIBRARY_SEARCH_USER_DIRS: # TODO we have to probably set an handler for this, since it can be a not default value. # And we have to change the default path of load - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") diff --git a/qiling/os/windows/dlls/kernel32/memoryapi.py b/qiling/os/windows/dlls/kernel32/memoryapi.py index 9013086d4..48721caf9 100644 --- a/qiling/os/windows/dlls/kernel32/memoryapi.py +++ b/qiling/os/windows/dlls/kernel32/memoryapi.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time -import logging + from qiling.os.windows.const import * from qiling.os.const import * from qiling.os.windows.fncc import * @@ -81,7 +81,7 @@ def hook_VirtualQuery(ql, address, params): if not base and not size: # Page not found # printable = sorted(['0x%x-0x%x' % (chunk.address, chunk.address+chunk.size) for chunk in ql.os.heap.chunks]) - # logging.debug('Could not find memory chunk containing address 0x%x in %s' % (params['lpAddress'], + # ql.log.debug('Could not find memory chunk containing address 0x%x in %s' % (params['lpAddress'], # printable)) ql.os.last_error = ERROR_INVALID_PARAMETER return 0 diff --git a/qiling/os/windows/dlls/kernel32/processenv.py b/qiling/os/windows/dlls/kernel32/processenv.py index 6f77e559b..c7cbb900b 100644 --- a/qiling/os/windows/dlls/kernel32/processenv.py +++ b/qiling/os/windows/dlls/kernel32/processenv.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.os.windows.fncc import * from qiling.exception import * @@ -80,8 +80,8 @@ def hook_ExpandEnvironmentStringsW(ql, address, params): substring = string[start + 1:end] result = ql.os.profile["PATH"].get(substring, None) if result is None: - logging.debug(substring) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug(substring) + raise QlErrorNotImplemented("API not implemented") result = (string[:start] + result + string[end + 1:] + "\x00").encode("utf-16le") dst = params["lpDst"] max_size = params["nSize"] diff --git a/qiling/os/windows/dlls/kernel32/processthreadsapi.py b/qiling/os/windows/dlls/kernel32/processthreadsapi.py index 80319d7b6..41516eb17 100644 --- a/qiling/os/windows/dlls/kernel32/processthreadsapi.py +++ b/qiling/os/windows/dlls/kernel32/processthreadsapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/profileapi.py b/qiling/os/windows/dlls/kernel32/profileapi.py index 782a484f8..a31bbf83d 100644 --- a/qiling/os/windows/dlls/kernel32/profileapi.py +++ b/qiling/os/windows/dlls/kernel32/profileapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/psapi.py b/qiling/os/windows/dlls/kernel32/psapi.py index 133ff5c9d..b9acbe421 100644 --- a/qiling/os/windows/dlls/kernel32/psapi.py +++ b/qiling/os/windows/dlls/kernel32/psapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/stringapiset.py b/qiling/os/windows/dlls/kernel32/stringapiset.py index 1e98d5ce4..430cbfa90 100644 --- a/qiling/os/windows/dlls/kernel32/stringapiset.py +++ b/qiling/os/windows/dlls/kernel32/stringapiset.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/synchapi.py b/qiling/os/windows/dlls/kernel32/synchapi.py index 9c2de7690..2051c5f77 100644 --- a/qiling/os/windows/dlls/kernel32/synchapi.py +++ b/qiling/os/windows/dlls/kernel32/synchapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time @@ -163,14 +163,14 @@ def hook_OpenMutexW(ql, address, params): if mutex.isFree(): mutex.lock() else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") else: if handle is None: # If a named mutex does not exist, the function fails and GetLastError returns ERROR_FILE_NOT_FOUND. ql.os.last_error = ERROR_FILE_NOT_FOUND return 0 else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") # HANDLE OpenMutexA( diff --git a/qiling/os/windows/dlls/kernel32/sysinfoapi.py b/qiling/os/windows/dlls/kernel32/sysinfoapi.py index 574e20a18..738462796 100644 --- a/qiling/os/windows/dlls/kernel32/sysinfoapi.py +++ b/qiling/os/windows/dlls/kernel32/sysinfoapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/kernel32/tlhelp32.py b/qiling/os/windows/dlls/kernel32/tlhelp32.py index 4cabffb20..d68d7766e 100644 --- a/qiling/os/windows/dlls/kernel32/tlhelp32.py +++ b/qiling/os/windows/dlls/kernel32/tlhelp32.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time -import logging from qiling.os.windows.const import * from qiling.os.const import * from qiling.os.windows.fncc import * @@ -27,9 +26,9 @@ def hook_CreateToolhelp32Snapshot(ql, address, params): # TODO thinking about implementing an handler, gonna see if is really necessary flag = params["dwFlags"] if flag == TH32CS_SNAPPROCESS: - logging.debug("[=] The target is checking every process!") + ql.log.debug("The target is checking every process!") else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") return 0xD10C diff --git a/qiling/os/windows/dlls/kernel32/winbase.py b/qiling/os/windows/dlls/kernel32/winbase.py index 2aee358ab..c20660a42 100644 --- a/qiling/os/windows/dlls/kernel32/winbase.py +++ b/qiling/os/windows/dlls/kernel32/winbase.py @@ -1,15 +1,16 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.windows.const import * from qiling.os.windows.fncc import * from qiling.os.windows.thread import * from qiling.exception import * -import configparser, logging +import configparser from qiling.os.windows.structs import * + dllname = 'kernel32_dll' # __analysis_noreturn VOID FatalExit( @@ -300,7 +301,7 @@ def compare(p1, operator, p2): elif operator == "<=": return p1 <= p2 else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") # typedef struct _OSVERSIONINFOEXA { @@ -343,7 +344,7 @@ def hook_VerifyVersionInfoW(ql, address, params): elif value == VER_LESS_EQUAL: operator = "<=" else: - raise QlErrorNotImplemented("[!] API not implemented with operator %d" % value) + raise QlErrorNotImplemented("API not implemented with operator %d" % value) # Versions should be compared together if key == VER_MAJORVERSION or key == VER_MINORVERSION or key == VER_PRODUCT_TYPE: major_version_asked = os_asked.major[0] @@ -353,12 +354,12 @@ def hook_VerifyVersionInfoW(ql, address, params): # Just a print for analysts, will remove it from here in the future if key == VER_MAJORVERSION: - logging.debug("[=] The Target is checking the windows Version!") + ql.log.debug("The Target is checking the windows Version!") version_asked = SYSTEMS_VERSION.get(concat, None) if version_asked is None: - raise QlErrorNotImplemented("[!] API not implemented for version %s" % concat) + raise QlErrorNotImplemented("API not implemented for version %s" % concat) else: - logging.debug("[=] The target asks for version %s %s" % (operator, version_asked)) + ql.log.debug("The target asks for version %s %s" % (operator, version_asked)) # We can finally compare qiling_os = str(ql.os.profile.get("SYSTEM", "majorVersion")) + str( ql.os.profile.get("SYSTEM", "minorVersion")) + str( @@ -367,7 +368,7 @@ def hook_VerifyVersionInfoW(ql, address, params): elif key == VER_SERVICEPACKMAJOR: res = compare(ql.os.profile.getint("SYSTEM", "VER_SERVICEPACKMAJOR"), operator, os_asked.service_major[0]) else: - raise QlErrorNotImplemented("[!] API not implemented for key %s" % key) + raise QlErrorNotImplemented("API not implemented for key %s" % key) # The result is a AND between every value, so if we find a False we just exit from the loop if not res: ql.os.last_error = ERROR_OLD_WIN_VERSION @@ -585,7 +586,7 @@ def hook_CharLowerBuffA(ql, address, params): def hook_CharLowerA(ql, address, params): lpsz = params["lpsz"] if (lpsz >> 16) > 0: - value = read_cstring(ql, lpsz) + value = ql.os.read_cstring(lpsz) value = value.lower() value = value.encode("utf-8") ql.mem.write(lpsz, value) diff --git a/qiling/os/windows/dlls/kernel32/winnls.py b/qiling/os/windows/dlls/kernel32/winnls.py index 934da7e63..b7149549a 100644 --- a/qiling/os/windows/dlls/kernel32/winnls.py +++ b/qiling/os/windows/dlls/kernel32/winnls.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time @@ -62,7 +62,7 @@ def hook_GetLocaleInfoA(ql, address, params): local_dict = LOCALE.get(locale_value, None) if local_dict is None: - # raise QlErrorNotImplemented("[!] API not implemented") + # raise QlErrorNotImplemented("API not implemented") ql.os.last_error = ERROR_INVALID_PARAMETER return 0 diff --git a/qiling/os/windows/dlls/kernel32/winnt.py b/qiling/os/windows/dlls/kernel32/winnt.py index c12772851..9b188aaa6 100644 --- a/qiling/os/windows/dlls/kernel32/winnt.py +++ b/qiling/os/windows/dlls/kernel32/winnt.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from qiling.os.windows.const import * from qiling.os.windows.fncc import * diff --git a/qiling/os/windows/dlls/kernel32/wow64apiset.py b/qiling/os/windows/dlls/kernel32/wow64apiset.py index 789971a51..98a97b1c2 100644 --- a/qiling/os/windows/dlls/kernel32/wow64apiset.py +++ b/qiling/os/windows/dlls/kernel32/wow64apiset.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time @@ -29,5 +29,5 @@ def hook_IsWow64Process(ql, address, params): if ql.archbit == 32: ql.mem.write(pointer, false) else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") return 1 diff --git a/qiling/os/windows/dlls/mscoree.py b/qiling/os/windows/dlls/mscoree.py index 5af2f1e19..508bf4491 100644 --- a/qiling/os/windows/dlls/mscoree.py +++ b/qiling/os/windows/dlls/mscoree.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import base64 from qiling.os.windows.fncc import * diff --git a/qiling/os/windows/dlls/msi.py b/qiling/os/windows/dlls/msi.py index 20b90c040..0a687ca86 100644 --- a/qiling/os/windows/dlls/msi.py +++ b/qiling/os/windows/dlls/msi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct from qiling.os.windows.const import * diff --git a/qiling/os/windows/dlls/msvbvm60.py b/qiling/os/windows/dlls/msvbvm60.py index 420bfa6f9..fd0dba3f4 100644 --- a/qiling/os/windows/dlls/msvbvm60.py +++ b/qiling/os/windows/dlls/msvbvm60.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct from qiling.os.windows.const import * diff --git a/qiling/os/windows/dlls/msvcrt.py b/qiling/os/windows/dlls/msvcrt.py index 2f074f4c2..ea798797c 100644 --- a/qiling/os/windows/dlls/msvcrt.py +++ b/qiling/os/windows/dlls/msvcrt.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os import time -import logging + from qiling.os.windows.fncc import * from qiling.os.const import * from qiling.os.windows.const import * @@ -49,13 +49,20 @@ def hook___p__commode(ql, address, params): return addr -# int * __p__acmdln( +# char ** __p__acmdln( # ); @winsdkapi(cc=CDECL) def hook___p__acmdln(ql, address, params): addr = ql.loader.import_address_table['msvcrt.dll'][b'_acmdln'] return addr +# wchar_t ** __p__wcmdln( +# ); +@winsdkapi(cc=CDECL) +def hook___p__wcmdln(ql, address, params): + addr = ql.loader.import_address_table['msvcrt.dll'][b'_wcmdln'] + return addr + # unsigned int _controlfp( # unsigned int new, @@ -155,7 +162,7 @@ def hook___p___argv(ql, address, params): # int* __p___argc(void) @winsdkapi(cc=CDECL) def hook___p___argc(ql, address, params): - logging.debug("_p___argc") + ql.log.debug("_p___argc") ret = ql.os.heap.alloc(ql.pointersize) ql.mem.write(ret, ql.pack(len(ql.argv))) return ret @@ -181,15 +188,15 @@ def hook_sprintf(ql, address, _): str_ptr, format_ptr = ql.os.get_function_param(2) if not format_ptr: - logging.info('printf(format = 0x0) = 0x%x' % ret) + ql.log.info('printf(format = 0x0) = 0x%x' % ret) return ret sp = ql.reg.esp if ql.archtype == QL_ARCH.X86 else ql.reg.rsp p_args = sp + ql.pointersize * 3 format_string = ql.os.read_cstring(format_ptr) - str_size, str_data = ql.os.printf(address, format_string, p_args, "sprintf") - logging.info() + str_size, str_data = ql.os.vprintf(address, format_string, p_args, "sprintf") + ql.log.info() count = format_string.count('%') if ql.archtype == QL_ARCH.X8664: @@ -209,17 +216,17 @@ def hook_printf(ql, address, _): format_string = ql.os.get_function_param(1) if format_string == 0: - logging.info('printf(format = 0x0) = 0x%x' % ret) + ql.log.info('printf(format = 0x0) = 0x%x' % ret) return ret format_string = ql.os.read_cstring(format_string) - param_addr = ql.reg.arch_sp + ql.pointersize * 2 - ret, _ = ql.os.printf(address, format_string, param_addr, "printf") + count = format_string.count("%") + params = ql.os.get_function_param(count + 1)[1:] if count > 0 else [] + ret, _ = ql.os.printf(address, format_string, params, "printf") ql.os.set_return_value(ret) - count = format_string.count('%') # x8664 fastcall donnot known the real number of parameters # so you need to manually pop the stack if ql.archtype == QL_ARCH.X8664: @@ -229,6 +236,32 @@ def hook_printf(ql, address, _): return None +# int wprintf(const wchar_t *format, ...) +@winsdkapi(cc=CDECL, param_num=1) +def hook_wprintf(ql, address, _): + ret = 0 + format_string = ql.os.get_function_param(1) + + if format_string == 0: + ql.log.info('wprintf(format = 0x0) = 0x%x' % ret) + return ret + + format_string = ql.os.read_wstring(format_string) + + count = format_string.count("%") + params = ql.os.get_function_param(count + 1)[1:] if count > 0 else [] + ret, _ = ql.os.printf(address, format_string, params, "wprintf", wstring=True) + + ql.os.set_return_value(ret) + + # x8664 fastcall donnot known the real number of parameters + # so you need to manually pop the stack + if ql.archtype == QL_ARCH.X8664: + # if number of params > 4 + if count + 1 > 4: + ql.reg.rsp = ql.reg.rsp + ((count - 4 + 1) * 8) + + return None # MSVCRT_FILE * CDECL MSVCRT___acrt_iob_func(unsigned idx) @winsdkapi(cc=CDECL, replace_params={"idx": UINT}) @@ -245,7 +278,7 @@ def hook___stdio_common_vfprintf(ql, address, _): else: _, _, _, p_format, _, p_args = ql.os.get_function_param(6) fmt = ql.os.read_cstring(p_format) - ql.os.printf(address, fmt, p_args, '__stdio_common_vfprintf') + ql.os.vprintf(address, fmt, p_args, '__stdio_common_vfprintf') return ret @@ -255,7 +288,7 @@ def hook___stdio_common_vfwprintf(ql, address, _): _, _, _, p_format, _, p_args = ql.os.get_function_param(6) fmt = ql.os.read_wstring(p_format) - ql.os.printf(address, fmt, p_args, '__stdio_common_vfwprintf', wstring=True) + ql.os.vprintf(address, fmt, p_args, '__stdio_common_vfwprintf', wstring=True) return ret @@ -265,16 +298,20 @@ def hook___stdio_common_vswprintf_s(ql, address, _): _, size, p_format, p_args = ql.os.get_function_param(4) fmt = ql.os.read_wstring(p_format) - ql.os.printf(address, fmt, p_args, '__stdio_common_vswprintf_s', wstring=True) + ql.os.vprintf(address, fmt, p_args, '__stdio_common_vswprintf_s', wstring=True) return ret # int lstrlenA( # LPCSTR lpString # ); -@winsdkapi(cc=CDECL, replace_params={'lpString': POINTER}) +@winsdkapi(cc=STDCALL, replace_params={'lpString': POINTER}) def hook_lstrlenA(ql, address, params): addr = params["lpString"] + + if addr == 0: + return 0 + string = b"" val = ql.mem.read(addr, 1) while bytes(val) != b"\x00": @@ -291,6 +328,10 @@ def hook_lstrlenA(ql, address, params): @winsdkapi(cc=CDECL, replace_params={'lpString': POINTER}) def hook_lstrlenW(ql, address, params): addr = params["lpString"] + + if addr == 0: + return 0 + string = b"" val = ql.mem.read(addr, 2) while bytes(val) != b"\x00\x00": @@ -407,7 +448,7 @@ def hook__ismbblead(ql, address, params): if loc[0x1004] == "utf-8": return 0 else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") # errno_t _wfopen_s( diff --git a/qiling/os/windows/dlls/ntdll.py b/qiling/os/windows/dlls/ntdll.py index 4a64f16e8..3a8f271d9 100644 --- a/qiling/os/windows/dlls/ntdll.py +++ b/qiling/os/windows/dlls/ntdll.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import struct + -import struct, logging from qiling.os.windows.const import * from qiling.os.windows.fncc import * from qiling.os.const import * @@ -26,7 +28,7 @@ def hook_memcpy(ql, address, params): data = bytes(ql.mem.read(params['src'], params['count'])) ql.mem.write(params['dest'], data) except Exception as e: - logging.exception("") + ql.log.exception("") return params['dest'] @@ -50,9 +52,9 @@ def _QueryInformationProcess(ql, address, params): pbi.write(addr) value = addr.to_bytes(ql.pointersize, "little") else: - logging.debug(str(flag)) - raise QlErrorNotImplemented("[!] API not implemented") - logging.debug("[=] The target is checking the debugger via QueryInformationProcess ") + ql.log.debug(str(flag)) + raise QlErrorNotImplemented("API not implemented") + ql.log.debug("The target is checking the debugger via QueryInformationProcess ") ql.mem.write(dst, value) if pt_res != 0: ql.mem.write(pt_res, 0x8.to_bytes(1, byteorder="little")) @@ -129,8 +131,8 @@ def _QuerySystemInformation(ql, address, params): ql.mem.write(pt_res, sbi.size.to_bytes(1, byteorder="little")) return STATUS_INFO_LENGTH_MISMATCH else: - logging.debug(str(siClass)) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug(str(siClass)) + raise QlErrorNotImplemented("API not implemented") return STATUS_SUCCESS @@ -194,8 +196,8 @@ def hook_ZwQueryObject(ql, address, params): size_dest = params["ReturnLength"] string = "DebugObject".encode("utf-16le") string_addr = ql.os.heap.alloc(len(string)) - logging.debug(str(string_addr)) - logging.debug(str(string)) + ql.log.debug(str(string_addr)) + ql.log.debug(str(string)) ql.mem.write(string_addr, string) us = qiling.os.windows.structs.UnicodeString(ql, length=len(string), maxLength=len(string), buffer=string_addr) @@ -209,7 +211,7 @@ def hook_ZwQueryObject(ql, address, params): # res = qiling.os.windows.structs.ObjectAllTypesInformation(ql, 2, oti) return 1 else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") if dest != 0 and params["Handle"] != 0: res.write(dest) if size_dest != 0: @@ -246,9 +248,9 @@ def _SetInformationProcess(ql, address, params): elif flag == ProcessDebugObjectHandle: return STATUS_PORT_NOT_SET elif flag == ProcessBreakOnTermination: - logging.debug("[=] The target may be attempting modify a the 'critical' flag of the process") + ql.log.debug("The target may be attempting modify a the 'critical' flag of the process") elif flag == ProcessExecuteFlags: - logging.debug("[=] The target may be attempting to modify DEP for the process") + ql.log.debug("The target may be attempting to modify DEP for the process") if dst != 0: ql.mem.write(dst, 0x0.to_bytes(1, byteorder="little")) @@ -260,13 +262,13 @@ def _SetInformationProcess(ql, address, params): uniqueId=ql.os.profile.getint("KERNEL", "pid"), parentPid=ql.os.profile.geting("KERNEL", "parent_pid") ) - logging.debug("[=] The target may be attempting to modify the PEB debug flag") + ql.log.debug("The target may be attempting to modify the PEB debug flag") addr = ql.os.heap.alloc(pbi.size) pbi.write(addr) value = addr.to_bytes(ql.pointersize, "little") else: - logging.debug(str(flag)) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug(str(flag)) + raise QlErrorNotImplemented("API not implemented") return STATUS_SUCCESS @@ -297,7 +299,7 @@ def hook_LdrGetProcedureAddress(ql, address, params): try: dll_name = [key for key, value in ql.loader.dlls.items() if value == params['ModuleHandle']][0] except IndexError as ie: - logging.info('[!] Failed to import function "%s" with handle 0x%X' % (lpProcName, params['ModuleHandle'])) + ql.log.info('Failed to import function "%s" with handle 0x%X' % (lpProcName, params['ModuleHandle'])) return 0 if identifier in ql.loader.import_address_table[dll_name]: diff --git a/qiling/os/windows/dlls/ntoskrnl.py b/qiling/os/windows/dlls/ntoskrnl.py index 38c666ca3..fdff3bd2b 100644 --- a/qiling/os/windows/dlls/ntoskrnl.py +++ b/qiling/os/windows/dlls/ntoskrnl.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + -import logging from qiling.os.windows.const import * from qiling.os.windows.fncc import * from qiling.os.const import * @@ -37,7 +37,7 @@ def hook_RtlGetVersion(ql, address, params): os.major[0] = ql.os.profile.getint("SYSTEM", "majorVersion") os.minor[0] = ql.os.profile.getint("SYSTEM", "minorVersion") os.write(pointer) - logging.debug("[=] The target is checking the windows Version!") + ql.log.debug("The target is checking the windows Version!") return STATUS_SUCCESS @@ -59,11 +59,11 @@ def hook_ZwSetInformationThread(ql, address, params): if size >= 100: return STATUS_INFO_LENGTH_MISMATCH if information == ThreadHideFromDebugger: - logging.debug("[=] The target is checking debugger via SetInformationThread") + ql.log.debug("The target is checking debugger via SetInformationThread") if dst != 0: ql.mem.write(dst, 0x0.to_bytes(1, byteorder="little")) else: - raise QlErrorNotImplemented("[!] API not implemented %d " % + raise QlErrorNotImplemented("API not implemented %d " % information) else: @@ -100,31 +100,27 @@ def hook_NtClose(ql, address, params): @winsdkapi(cc=CDECL, dllname=dllname, param_num=3) def hook_DbgPrintEx(ql, address, _): ret = 0 - format_string = ql.os.get_function_param(3) - - if len(format_string) < 3: - logging.info('0x%0.2x: printf(format = 0x0) = 0x%x\n' % (address, ret)) - return ret - - format_string = read_cstring(ql, format_string[2]) + format_string_addr = ql.os.get_function_param(1) + format_string = ql.os.read_cstring(format_string_addr) if format_string.count('%') == 0: param_addr = ql.reg.sp + ql.pointersize * 2 else: param_addr = ql.reg.sp + ql.pointersize * 3 - ret, _ = printf(ql, address, format_string, param_addr, "DbgPrintEx") - + count = format_string.count('%') + args = ql.os.get_function_param(2 + count)[2:] + + ret, _ = ql.os.printf(address, format_string, args, "DbgPrintEx") ql.os.set_return_value(ret) - count = format_string.count('%') # x8664 fastcall does not known the real number of parameters # so we need to manually pop the stack if ql.archtype == QL_ARCH.X8664: # if number of params > 4 if count + 1 > 4: - rsp = ql.uc.reg_read(UC_X86_REG_RSP) - ql.register(UC_X86_REG_RSP, rsp + (count - 4 + 1) * 8) + rsp = ql.reg.rsp + ql.reg.rsp = (rsp + (count - 4 + 1) * 8) return None @@ -136,25 +132,26 @@ def hook_DbgPrintEx(ql, address, _): def hook_DbgPrint(ql, address, _): ret = 0 format_string_addr = ql.os.get_function_param(1) - format_string = read_cstring(ql, format_string_addr) + format_string = ql.os.read_cstring(format_string_addr) if format_string.count('%') == 0: param_addr = ql.reg.sp + ql.pointersize * 2 else: param_addr = ql.reg.sp + ql.pointersize * 3 - ret, _ = printf(ql, address, format_string, param_addr, "DbgPrint") - - ql.os.set_return_value(ql) - count = format_string.count('%') + args = ql.os.get_function_param(2 + count)[2:] + + ret, _ = ql.os.printf(address, format_string, args, "DbgPrint") + ql.os.set_return_value(ret) + # x8664 fastcall does not known the real number of parameters # so we need to manually pop the stack if ql.archtype == QL_ARCH.X8664: # if number of params > 4 if count + 1 > 4: - rsp = ql.uc.reg_read(UC_X86_REG_RSP) - ql.register(UC_X86_REG_RSP, rsp + (count - 4 + 1) * 8) + rsp = ql.reg.rsp + ql.reg.rsp = (rsp + (count - 4 + 1) * 8) return None @@ -795,10 +792,11 @@ def _NtQuerySystemInformation(ql, address, params): module = RTL_PROCESS_MODULE_INFORMATION64() else: module = RTL_PROCESS_MODULE_INFORMATION32() - + module.Section = 0 module.MappedBase = 0 - module.ImageBase = ql.loader.dlls["ntoskrnl.exe"] + if ql.loader.is_driver == True: + module.ImageBase = ql.loader.dlls.get("ntoskrnl.exe") module.ImageSize = 0xab000 module.Flags = 0x8804000 module.LoadOrderIndex = 0 # order of this module @@ -1099,7 +1097,7 @@ def hook_PsLookupProcessByProcessId(ql, address, params): else: addr = ql.os.heap.alloc(ctypes.sizeof(EPROCESS32)) ql.mem.write(Process, ql.pack(addr)) - logging.info("PID = 0x%x, addrof(EPROCESS) == 0x%x" % (ProcessId, addr)) + ql.log.info("PID = 0x%x, addrof(EPROCESS) == 0x%x" % (ProcessId, addr)) return STATUS_SUCCESS # NTSYSAPI NTSTATUS ZwOpenKey( @@ -1219,6 +1217,19 @@ def hook_ObReferenceObjectByHandle(ql, address, params): def hook_KeSetEvent(ql, address, params): return 0 +# LONG KeResetEvent( +# PRKEVENT Event, +# KPRIORITY Increment, +# BOOLEAN Wait +# ); +@winsdkapi(cc=STDCALL, dllname=dllname, replace_params={ + "Event": POINTER, + "Increment": ULONG, + "Wait": ULONG + }) +def hook_KeResetEvent(ql, address, params): + return 0 + # void KeClearEvent( # PRKEVENT Event # ); @@ -1276,7 +1287,7 @@ def hook_ObOpenObjectByPointer(ql, address, params): new_handle = Handle(name="p=%x" % Object) ql.os.handle_manager.append(new_handle) ql.mem.write(point_to_new_handle, ql.pack(new_handle.id)) - logging.info("New handle of 0x%x is 0x%x" % (Object, new_handle.id)) + ql.log.info("New handle of 0x%x is 0x%x" % (Object, new_handle.id)) return STATUS_SUCCESS @winsdkapi(cc=CDECL, dllname=dllname) diff --git a/qiling/os/windows/dlls/ole32.py b/qiling/os/windows/dlls/ole32.py index b2dc822e1..047f1cb8a 100644 --- a/qiling/os/windows/dlls/ole32.py +++ b/qiling/os/windows/dlls/ole32.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/oleaut32.py b/qiling/os/windows/dlls/oleaut32.py index ad19df644..4213e4e70 100644 --- a/qiling/os/windows/dlls/oleaut32.py +++ b/qiling/os/windows/dlls/oleaut32.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct import time diff --git a/qiling/os/windows/dlls/shell32.py b/qiling/os/windows/dlls/shell32.py index 5981f3aab..dcff9179f 100644 --- a/qiling/os/windows/dlls/shell32.py +++ b/qiling/os/windows/dlls/shell32.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import struct, time, os -import struct, time, os, logging from qiling.os.windows.const import * from qiling.os.const import * @@ -42,8 +43,8 @@ def hook_SHGetFileInfoW(ql, address, params): if flags == SHGFI_LARGEICON: return 1 else: - logging.debug(flags) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug(flags) + raise QlErrorNotImplemented("API not implemented") def _ShellExecute(ql, obj: ShellExecuteInfoA): @@ -53,13 +54,13 @@ def _ShellExecute(ql, obj: ShellExecuteInfoA): file = ql.os.read_wstring(obj.file[0]) if obj.file[0] != 0 else "" directory = ql.os.read_wstring(obj.dir[0]) if obj.dir[0] != 0 else "" - logging.debug("[=] Target executed a shell command!") - logging.debug("[-] Operation: %s " % operation) - logging.debug("[-] Parameters: %s " % params) - logging.debug("[-] File: %s " % file) - logging.debug("[-] Directory: %s " % directory) + ql.log.debug("Target executed a shell command!") + ql.log.debug("Operation: %s " % operation) + ql.log.debug("Parameters: %s " % params) + ql.log.debug("File: %s " % file) + ql.log.debug("Directory: %s " % directory) if obj.show[0] == SW_HIDE: - logging.debug("[=] With an hidden window") + ql.log.debug("With an hidden window") process = QlWindowsThread(ql, status=0, isFake=True) handle = Handle(obj=process) ql.os.handle_manager.append(handle) @@ -115,17 +116,17 @@ def hook_SHGetSpecialFolderPathW(ql, address, params): path = str(ql.os.userprofile + "AppData\\") # We always create the directory appdata_dir = path.split("C:\\")[1].replace("\\", "/") - logging.debug("[+] dir path: %s" % path) + ql.log.debug("dir path: %s" % path) path_emulated = os.path.join(ql.rootfs, appdata_dir) - logging.debug("[!] emulated path: %s" % path_emulated) + ql.log.debug("emulated path: %s" % path_emulated) ql.mem.write(dst, (path + "\x00").encode("utf-16le")) # FIXME: Somehow winodws path is wrong if not os.path.exists(path_emulated): try: os.makedirs(path_emulated, 0o755) - logging.debug("[!] os.makedirs completed") + ql.log.debug("os.makedirs completed") except OSError: - logging.debug("[!] os.makedirs fail") + ql.log.debug("os.makedirs fail") else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") return 1 diff --git a/qiling/os/windows/dlls/shlwapi.py b/qiling/os/windows/dlls/shlwapi.py index c0f508f42..7f50588e4 100644 --- a/qiling/os/windows/dlls/shlwapi.py +++ b/qiling/os/windows/dlls/shlwapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct from qiling.os.windows.fncc import * diff --git a/qiling/os/windows/dlls/ucrtbased.py b/qiling/os/windows/dlls/ucrtbased.py index 5d42782cb..facd3fc4a 100644 --- a/qiling/os/windows/dlls/ucrtbased.py +++ b/qiling/os/windows/dlls/ucrtbased.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct from qiling.os.windows.const import * diff --git a/qiling/os/windows/dlls/user32.py b/qiling/os/windows/dlls/user32.py index ec607d357..62fe7d533 100644 --- a/qiling/os/windows/dlls/user32.py +++ b/qiling/os/windows/dlls/user32.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct -import logging + + from qiling.os.windows.fncc import * from qiling.os.const import * from qiling.os.windows.utils import * @@ -141,7 +142,7 @@ def hook_GetClipboardData(ql, address, params): ql.mem.write(addr, data) return addr else: - logging.debug('Failed to get clipboard data') + ql.log.debug('Failed to get clipboard data') return 0 @@ -168,11 +169,11 @@ def hook_MapVirtualKeyW(ql, address, params): if code is not None: return code else: - logging.debug("Code value %x" % code_value) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug("Code value %x" % code_value) + raise QlErrorNotImplemented("API not implemented") else: - logging.debug("Map value %x" % map_value) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug("Map value %x" % map_value) + raise QlErrorNotImplemented("API not implemented") # SHORT GetKeyState( @@ -181,7 +182,7 @@ def hook_MapVirtualKeyW(ql, address, params): @winsdkapi(cc=STDCALL, dllname=dllname, replace_params_type={'int': 'UINT'}) def hook_GetKeyState(ql, address, params): let = chr(params["nVirtKey"]) - logging.debug(let) + ql.log.debug(let) UP = 2 DOWN = 0 return UP @@ -242,8 +243,8 @@ def hook_GetSystemMetrics(ql, address, params): elif info == SM_CYHSCROLL: return 300 else: - logging.debug("Info value %x" % info) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug("Info value %x" % info) + raise QlErrorNotImplemented("API not implemented") # HDC GetDC( @@ -540,15 +541,14 @@ def hook_CharPrevA(ql, address, params): def hook_wsprintfW(ql, address, params): dst, p_format = ql.os.get_function_param(2) - sp = ql.reg.esp if ql.archtype == QL_ARCH.X86 else ql.reg.rsp - p_args = sp + ql.pointersize * 3 format_string = ql.os.read_wstring(p_format) - size, string = ql.os.printf(address, format_string, p_args, "wsprintfW", wstring=True) - count = format_string.count('%') + args = ql.os.get_function_param(2 + count)[2:] + size, string = ql.os.printf(address, format_string, args, "wsprintfW", wstring=True) + if ql.archtype == QL_ARCH.X8664: # We must pop the stack correctly - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") ql.mem.write(dst, (string + "\x00").encode("utf-16le")) return size @@ -561,14 +561,15 @@ def hook_wsprintfW(ql, address, params): # ); @winsdkapi(cc=CDECL, dllname=dllname, param_num=3) def hook_sprintf(ql, address, params): - dst, p_format, p_args = ql.os.get_function_param(3) + dst, p_format = ql.os.get_function_param(2) format_string = ql.os.read_wstring(p_format) - size, string = ql.os.printf(address, format_string, p_args, "sprintf", wstring=True) - count = format_string.count('%') + args = ql.os.get_function_param(2 + count)[2:] + size, string = ql.os.printf(address, format_string, args, "sprintf", wstring=True) + if ql.archtype == QL_ARCH.X8664: # We must pop the stack correctly - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") ql.mem.write(dst, (string + "\x00").encode("utf-16le")) return size @@ -631,13 +632,14 @@ def hook_wvsprintfA(ql, address, params): @winsdkapi(cc=CDECL, dllname=dllname, param_num=3) def hook_wsprintfA(ql, address, params): dst, p_format, p_args = ql.os.get_function_param(3) - format_string = read_cstring(ql, p_format) - size, string = printf(ql, address, format_string, p_args, "wsprintfA") - + format_string = ql.os.read_cstring(p_format) count = format_string.count('%') + args = ql.os.get_function_param(2 + count)[2:] + size, string = ql.os.printf(address, format_string, args, "wsprintfA") + if ql.archtype== QL_ARCH.X8664: # We must pop the stack correctly - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") ql.mem.write(dst, (string + "\x00").encode("utf-8")) return size @@ -657,8 +659,8 @@ def hook_MessageBoxW(ql, address, params): if type_box == MB_YESNO or type_box == MB_YESNOCANCEL: return IDYES else: - logging.debug(type_box) - raise QlErrorNotImplemented("[!] API not implemented") + ql.log.debug(type_box) + raise QlErrorNotImplemented("API not implemented") # int MessageBoxA( @@ -706,7 +708,7 @@ def hook_GetWindowThreadProcessId(ql, address, params): if target == ql.os.profile.getint("KERNEL", "pid") or target == ql.os.profile.getint("KERNEL", "shell_pid"): pid = ql.os.profile.getint("KERNEL", "parent_pid") else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") dst = params["lpdwProcessId"] if dst != 0: ql.mem.write(dst, pid.to_bytes(4, "little")) diff --git a/qiling/os/windows/dlls/wininet.py b/qiling/os/windows/dlls/wininet.py index a83f4e063..26606cad6 100644 --- a/qiling/os/windows/dlls/wininet.py +++ b/qiling/os/windows/dlls/wininet.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct from qiling.os.windows.fncc import * diff --git a/qiling/os/windows/dlls/wsock32.py b/qiling/os/windows/dlls/wsock32.py index 4e955f4b5..ff8382b53 100644 --- a/qiling/os/windows/dlls/wsock32.py +++ b/qiling/os/windows/dlls/wsock32.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct -import logging + + from qiling.os.windows.const import * from qiling.os.windows.fncc import * from qiling.os.const import * @@ -56,10 +57,10 @@ def hook_connect(ql, address, params): [str(octet) for octet in ql.mem.read(params["name"] + 4, 4)] ) else: - logging.debug("[!] sockaddr sin_family unhandled variant") + ql.log.debug("sockaddr sin_family unhandled variant") return 0 - logging.debug(f"0x{params['name']:08x}: sockaddr_in{6 if sin_family == 0x17 else ''}", + ql.log.debug(f"0x{params['name']:08x}: sockaddr_in{6 if sin_family == 0x17 else ''}", f"{{sin_family=0x{sin_family:02x}, sin_port={sin_port}, sin_addr={sin_addr}}}", sep="", ) diff --git a/qiling/os/windows/dlls/wudplatform.py b/qiling/os/windows/dlls/wudplatform.py index 41be8f072..bbad1efdd 100644 --- a/qiling/os/windows/dlls/wudplatform.py +++ b/qiling/os/windows/dlls/wudplatform.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import struct from qiling.os.windows.const import * diff --git a/qiling/os/windows/fiber.py b/qiling/os/windows/fiber.py index efbac5928..0a420a364 100644 --- a/qiling/os/windows/fiber.py +++ b/qiling/os/windows/fiber.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # A Simple Windows Clipboard Simulation -import logging + from unicorn import * from unicorn.x86_const import * @@ -36,7 +36,7 @@ def free(self, idx): else: fiber = self.fibers[idx] if fiber.cb: - logging.debug("Skipping emulation of callback function 0x%X for fiber 0x%X" % (fiber.cb, fiber.idx)) + self.ql.log.debug("Skipping emulation of callback function 0x%X for fiber 0x%X" % (fiber.cb, fiber.idx)) """ ret_addr = self.ql.reg.read(UC_X86_REG_RIP + 6 ) #FIXME, use capstone to get addr of next instr? @@ -51,7 +51,7 @@ def free(self, idx): else: self.ql.stack_push(ret_addr) self.ql.stack_push(ret_addr) - logging.debug("Jumping to callback @ 0x%X" % fiber.cb) + self.ql.log.debug("Jumping to callback @ 0x%X" % fiber.cb) self.ql.reg.write(UC_X86_REG_RIP, fiber.cb) # All of this gets overwritten by the rest of the code in fncc.py # Not sure how to actually make unicorn emulate the callback function due to that diff --git a/qiling/os/windows/fncc.py b/qiling/os/windows/fncc.py index 50b45f418..9424d5634 100644 --- a/qiling/os/windows/fncc.py +++ b/qiling/os/windows/fncc.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import os -import json -import struct -import logging +import json, os, struct + +from typing import Callable, Sequence, Mapping, MutableMapping, Any from functools import wraps from qiling.os.const import * @@ -14,7 +13,10 @@ from qiling.const import * from qiling.exception import * from qiling.os.windows.structs import * - +from qiling.os.os import QlOs + +from .utils import * + def replacetype(type, specialtype=None): if specialtype is None: @@ -28,6 +30,7 @@ def replacetype(type, specialtype=None): else: return type + # x86/x8664 PE should share Windows APIs def winsdkapi(cc, param_num=None, dllname=None, replace_params_type=None, replace_params={}, passthru=False): """ @@ -51,10 +54,10 @@ def wrapper(*args, **kwargs): winsdk_path = os.path.join(windows_abspath[:-11], 'extensions', 'windows_sdk', 'defs', dllname + '.json') if os.path.exists(winsdk_path): - f = open(winsdk_path, 'r') - funclist = json.load(f) + with open(winsdk_path, 'r') as f: + funclist = json.load(f) else: - logging.info('[!]', winsdk_path, 'not found') + ql.log.info('%s not found', winsdk_path) if funcname not in funclist: params = replace_params else: @@ -94,7 +97,7 @@ def wrapper(*args, **kwargs): elif ql.archtype == QL_ARCH.X8664: return ql.os.x8664_fastcall(param_num, params, func, args, kwargs, passthru) else: - raise QlErrorArch("[!] Unknown self.ql.arch") + raise QlErrorArch("Unknown self.ql.arch") return wrapper return decorator diff --git a/qiling/os/windows/handle.py b/qiling/os/windows/handle.py index b63380aa4..2a018ba13 100644 --- a/qiling/os/windows/handle.py +++ b/qiling/os/windows/handle.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# # A Simple Windows Handle Simulation diff --git a/qiling/os/windows/registry.py b/qiling/os/windows/registry.py index 8d51f7f2b..178e6c05d 100644 --- a/qiling/os/windows/registry.py +++ b/qiling/os/windows/registry.py @@ -1,18 +1,17 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# +import json, os, sys -import os -import json -import sys -import logging from Registry import Registry + from qiling.os.windows.const import * from qiling.exception import * from qiling.const import * + # Registry Manager reads data from two places # 1. config.json # if you want to modify the registry key/value, you can modify config.json @@ -27,12 +26,12 @@ class RegistryManager: def __init__(self, ql, hive=None): self.ql = ql - self.log_registry_dir = self.ql.log_dir + self.log_registry_dir = self.ql.rootfs if self.log_registry_dir == None: self.log_registry_dir = "qlog" - self.registry_diff = self.ql.targetname + "_" + self.ql.append + ".json" + self.registry_diff = self.ql.targetname + "_diff.json" self.regdiff = os.path.join(self.log_registry_dir, "registry", self.registry_diff) # hive dir @@ -40,8 +39,8 @@ def __init__(self, ql, hive=None): self.hive = hive else: self.hive = os.path.join(ql.rootfs, "Windows", "registry") - logging.debug("[+] Windows Registry PATH: %s" % self.hive) - if not os.path.exists(self.hive) and not self.ql.shellcoder: + ql.log.debug("Windows Registry PATH: %s" % self.hive) + if not os.path.exists(self.hive) and not self.ql.code: raise QlErrorFileNotFound(f"Error: Registry files not found in '{self.hive}'!") if not os.path.exists(self.regdiff): @@ -62,7 +61,7 @@ def __init__(self, ql, hive=None): try: self.registry_config = json.loads(data) except json.decoder.JSONDecodeError: - raise QlErrorJsonDecode("[!] Windows Registry JSON decode error") + raise QlErrorJsonDecode("Windows Registry JSON decode error") finally: self.f_config.close() @@ -77,10 +76,10 @@ def __init__(self, ql, hive=None): # hkey current user self.hkcu = Registry.Registry(os.path.join(self.hive, 'NTUSER.DAT')) except FileNotFoundError: - if not ql.shellcoder: + if not ql.code: QlErrorFileNotFound("WARNING: Registry files not found!") except Exception: - if not ql.shellcoder: + if not ql.code: QlErrorFileNotFound("WARNING: Registry files format error") self.accessed = {} @@ -99,7 +98,7 @@ def exists(self, key): sub = "\\".join(keys[1:]) data = reg.open(sub) else: - raise QlErrorNotImplemented("[!] Windows Registry %s not implemented" % (keys[0])) + raise QlErrorNotImplemented("Windows Registry %s not implemented" % (keys[0])) except Exception: return False @@ -114,7 +113,7 @@ def read(self, key, subkey, reg_type): return REG_TYPES[self.regdiff[key][subkey].type], self.regdiff[key][subkey].value else: raise QlErrorNotImplemented( - "[!] Windows Registry Type %s not implemented" % self.regdiff[key][subkey].type) + "Windows Registry Type %s not implemented" % self.regdiff[key][subkey].type) # read hive reg = None @@ -130,7 +129,7 @@ def read(self, key, subkey, reg_type): sub = "\\".join(keys[1:]) data = reg.open(sub) else: - raise QlErrorNotImplemented("[!] Windows Registry %s not implemented" % (keys[0])) + raise QlErrorNotImplemented("Windows Registry %s not implemented" % (keys[0])) for value in data.values(): if value.name() == subkey and (reg_type == Registry.RegNone or @@ -192,7 +191,7 @@ def write_reg_value_into_mem(self, reg_value, reg_type, address): self.ql.mem.write(address, bytes(reg_value)) length = len(reg_value) else: - raise QlErrorNotImplemented("[!] Windows Registry Type not implemented") + raise QlErrorNotImplemented("Windows Registry Type not implemented") elif reg_type == Registry.RegDWord: data = self.ql.pack32(reg_value) self.ql.mem.write(address, data) @@ -203,7 +202,7 @@ def write_reg_value_into_mem(self, reg_value, reg_type, address): length = len(data) else: raise QlErrorNotImplemented( - "[!] Windows Registry Type write to memory %s not implemented" % (REG_TYPES[reg_type])) + "Windows Registry Type write to memory %s not implemented" % (REG_TYPES[reg_type])) return length diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 52e1c5445..2e205d1f2 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -import struct, logging +# + +import ctypes, struct + +from enum import IntEnum + from unicorn.x86_const import * + + from qiling.const import * -from enum import IntEnum from qiling.os.windows.handle import * from qiling.exception import * - - -import ctypes from .wdk_const import * @@ -291,14 +293,14 @@ def bytes(self): s += self.ql.pack16(self.FullDllName['Length']) # 0x24 s += self.ql.pack16(self.FullDllName['MaximumLength']) # 0x26 - if self.ql.arch == QL_X8664: + if self.ql.arch == QL_ARCH.X8664: s += self.ql.pack32(0) s += self.ql.pack(self.FullDllName['BufferPtr']) # 0x28 s += self.ql.pack16(self.BaseDllName['Length']) s += self.ql.pack16(self.BaseDllName['MaximumLength']) - if self.ql.arch == QL_X8664: + if self.ql.arch == QL_ARCH.X8664: s += self.ql.pack32(0) s += self.ql.pack(self.BaseDllName['BufferPtr']) @@ -1015,27 +1017,30 @@ class MDL32(ctypes.Structure): ('ByteCount', ctypes.c_uint32), ('ByteOffset', ctypes.c_uint32)) +#TODO: Repeated and might not be needed -class DISPATCHER_HEADER64(ctypes.Structure): - _fields_ = ( - ('Type', ctypes.c_uint8), - ('TimerControlFlags', ctypes.c_uint8), - ('ThreadControlFlags', ctypes.c_uint8), - ('TimerMiscFlags', ctypes.c_uint8), - ('SignalState', ctypes.c_int32), - ('WaitListHead', LIST_ENTRY64), - ) +# class DISPATCHER_HEADER64(ctypes.Structure): +# _fields_ = ( +# ('Lock', ctypes.c_int32), +# ('Type', ctypes.c_uint8), +# ('TimerControlFlags', ctypes.c_uint8), +# ('ThreadControlFlags', ctypes.c_uint8), +# ('TimerMiscFlags', ctypes.c_uint8), +# ('SignalState', ctypes.c_int32), +# ('WaitListHead', LIST_ENTRY64), +# ) -class DISPATCHER_HEADER32(ctypes.Structure): - _fields_ = ( - ('Type', ctypes.c_uint8), - ('TimerControlFlags', ctypes.c_uint8), - ('ThreadControlFlags', ctypes.c_uint8), - ('TimerMiscFlags', ctypes.c_uint8), - ('SignalState', ctypes.c_int32), - ('WaitListHead', LIST_ENTRY32), - ) +# class DISPATCHER_HEADER32(ctypes.Structure): +# _fields_ = ( +# ('Lock', ctypes.c_int32), +# ('SignalState', ctypes.c_int32), +# ('WaitListHead', LIST_ENTRY32), +# ('Type', ctypes.c_uint8), +# ('TimerControlFlags', ctypes.c_uint8), +# ('ThreadControlFlags', ctypes.c_uint8), +# ('TimerMiscFlags', ctypes.c_uint8), +# ) class KAPC_STATE64(ctypes.Structure): @@ -1647,37 +1652,37 @@ def read(self, addr): raise NotImplementedError def generic_write(self, addr: int, attributes: list): - logging.debug("[+] Writing Windows object " + self.__class__.__name__) + self.ql.log.debug("Writing Windows object " + self.__class__.__name__) already_written = 0 for elem in attributes: (val, size, endianness, typ) = elem if typ == int: value = val.to_bytes(size, endianness) - logging.debug("[+] Writing to %d with value %s" % (addr + already_written, value)) + self.ql.log.debug("Writing to %d with value %s" % (addr + already_written, value)) self.ql.mem.write(addr + already_written, value) elif typ == bytes: if isinstance(val, bytearray): value = bytes(val) else: value = val - logging.debug("[+] Writing at addr %d value %s" % (addr + already_written, value)) + self.ql.log.debug("Writing at addr %d value %s" % (addr + already_written, value)) self.ql.mem.write(addr + already_written, value) elif issubclass(typ, WindowsStruct): val.write(addr) else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") already_written += size self.addr = addr def generic_read(self, addr: int, attributes: list): - logging.debug("[+] Reading Windows object " + self.__class__.__name__) + self.ql.log.debug("Reading Windows object " + self.__class__.__name__) already_read = 0 for elem in attributes: (val, size, endianness, type) = elem value = self.ql.mem.read(addr + already_read, size) - logging.debug("[+] Reading from %d value %s" % (addr + already_read, value)) + self.ql.log.debug("Reading from %d value %s" % (addr + already_read, value)) if type == int: elem[0] = int.from_bytes(value, endianness) elif type == bytes: @@ -1687,7 +1692,7 @@ def generic_read(self, addr: int, attributes: list): obj.read(addr) elem[0] = obj else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") already_read += size self.addr = addr @@ -1765,7 +1770,7 @@ def __init__(self, ql): def get(self, value): res = self.struct[value] if res is None: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") else: return res diff --git a/qiling/os/windows/thread.py b/qiling/os/windows/thread.py index 5d3b609bb..58bd1dd4e 100644 --- a/qiling/os/windows/thread.py +++ b/qiling/os/windows/thread.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# from unicorn.x86_const import * + from qiling.exception import * from qiling.os.thread import * from .utils import * diff --git a/qiling/os/windows/utils.py b/qiling/os/windows/utils.py index 5f8fbe926..1fd9bbd06 100644 --- a/qiling/os/windows/utils.py +++ b/qiling/os/windows/utils.py @@ -1,17 +1,15 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# + +import ntpath, os, uuid -import os -import uuid -import ntpath -import logging from sys import getsizeof from qiling.const import * -from qiling.os.const import * +from qiling.os.const import * from .registry import RegistryManager from .clipboard import Clipboard from .fiber import FiberManager @@ -21,7 +19,7 @@ def ql_x86_windows_hook_mem_error(ql, access, addr, size, value): - logging.debug("[+] ERROR: unmapped memory access at 0x%x" % addr) + ql.log.debug("ERROR: unmapped memory access at 0x%x" % addr) return False @@ -35,76 +33,6 @@ def read_guid(ql, address): return uuid.UUID(bytes_le=bytes(raw_guid)) -def print_function(ql, passthru, address, function_name, params, ret): - function_name = function_name.replace('hook_', '') - if function_name in ("__stdio_common_vfprintf", "__stdio_common_vfwprintf", - "printf", "wsprintfW", "sprintf"): - return - log = '0x%0.2x: %s(' % (address, function_name) - for each in params: - value = params[each] - if type(value) == str or type(value) == bytearray: - log += '%s = "%s", ' % (each, value) - elif type(value) == tuple: - log += '%s = 0x%x, ' % (each, value[0]) - else: - log += '%s = 0x%x, ' % (each, value) - log = log.strip(", ") - log += ')' - if ret is not None: - log += ' = 0x%x' % ret - - if passthru: - log += ' (PASSTHRU)' - - if ql.output != QL_OUTPUT.DEBUG: - log = log.partition(" ")[-1] - logging.info(log) - else: - logging.debug(log) - - -def read_wstring(ql, address): - result = "" - char = ql.mem.read(address, 2) - # decode as utf-16-le only - while char.decode('utf-16-le', errors="backslashreplace") != "\x00": - address += 2 - result += char.decode('utf-16-le', errors="backslashreplace") - char = ql.mem.read(address, 2) - - # We need to remove \x00 inside the string. Compares do not work otherwise - result = result.replace("\x00", "") - string_appearance(ql, result) - return result - - -def read_wchar(ql, val): - return val.decode('utf-16-le', errors="backslashreplace") - - -def read_punicode_string(ql, address): - if ql.archbit == 64: # Win64 - ub = ql.mem.read(address, getsizeof(UNICODE_STRING64)) - us = UNICODE_STRING64.from_buffer_copy(ub) - else: # Win32 - ub = ql.mem.read(address, getsizeof(UNICODE_STRING32)) - us = UNICODE_STRING32.from_buffer_copy(ub) - - return read_wstring(ql, us.Buffer) - - -def read_cstring(ql, address): - result = "" - char = ql.mem.read(address, 1) - while char.decode(errors="ignore") != "\x00": - address += 1 - result += char.decode(errors="ignore") - char = ql.mem.read(address, 1) - string_appearance(ql, result) - return result - - def env_dict_to_array(env_dict): env_list = [] for item in env_dict: @@ -114,9 +42,9 @@ def env_dict_to_array(env_dict): def debug_print_stack(ql, num, message=None): if message: - logging.debug("========== %s ==========" % message) + ql.log.debug("========== %s ==========" % message) sp = ql.reg.arch_sp - logging.debug(hex(sp + ql.pointersize * num) + ": " + hex(ql.stack_read(num * ql.pointersize))) + ql.log.debug(hex(sp + ql.pointersize * num) + ": " + hex(ql.stack_read(num * ql.pointersize))) def is_file_library(string): @@ -137,72 +65,11 @@ def string_appearance(ql, string): ql.os.appeared_strings[string] = val -def printf(ql, - address, - fmt, - params_addr, - name, - wstring=False, - double_pointer=False): - count = fmt.count("%") - params = [] - if count > 0: - for i in range(count): - # We don't need to mem_read here, otherwise we have a problem with strings, since read_wstring/read_cstring - # already take a pointer, and we will have pointer -> pointer -> STRING instead of pointer -> STRING - params.append(params_addr + i * ql.pointersize, ) - - formats = fmt.split("%")[1:] - index = 0 - for f in formats: - if f.startswith("s"): - if wstring: - if double_pointer: - params[index] = ql.unpack32( - ql.mem.read(params[index], ql.pointersize)) - params[index] = read_wstring(ql, params[index]) - else: - params[index] = read_cstring(ql, params[index]) - else: - # if is not a string, then they are already values! - pass - index += 1 - - output = '%s(format = %s' % (name, repr(fmt)) - for each in params: - if type(each) == str: - output += ', "%s"' % each - else: - output += ', 0x%0.2x' % each - output += ')' - fmt = fmt.replace("%llx", "%x") - stdout = fmt % tuple(params) - output += " = 0x%x" % len(stdout) - else: - output = '%s(format = %s) = 0x%x' % (name, repr(fmt), len(fmt)) - stdout = fmt - logging.info(output) - ql.os.stdout.write(bytes(stdout, 'utf-8')) - return len(stdout), stdout - -def canonical_path(ql, file_path): - file_path = file_path.replace("C:", ql.rootfs) - file_path = file_path.replace("\\", os.sep) - if ql.archbit == 32: - real_path = os.path.join(ql.rootfs, "Windows", "System32") - if not os.path.exists(real_path): - real_path = os.path.join(ql.rootfs, "Windows", "SysWOW64") - else: - real_path = os.path.join(ql.rootfs, "Windows", "System32") - - system_path = os.path.join(ql.rootfs, "Windows", "System32") - if system_path in file_path: - file_path = file_path.replace(system_path, real_path) - return file_path - def path_leaf(path): head, tail = ntpath.split(path) return tail or ntpath.basename(head) + + def find_size_function(ql, func_addr): # We have to retrieve the return address position code = ql.mem.read(func_addr, 0x100) diff --git a/qiling/os/windows/wdk_const.py b/qiling/os/windows/wdk_const.py index b9c8e5108..1da316ec5 100644 --- a/qiling/os/windows/wdk_const.py +++ b/qiling/os/windows/wdk_const.py @@ -1,3 +1,8 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + PROCESSOR_FEATURE_MAX = 64 MAX_WOW64_SHARED_ENTRIES = 16 diff --git a/qiling/os/windows/windows.py b/qiling/os/windows/windows.py index 6934f5ab4..60f7d740c 100644 --- a/qiling/os/windows/windows.py +++ b/qiling/os/windows/windows.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import types, logging +import types from unicorn import * @@ -11,14 +11,17 @@ from qiling.arch.x86 import * from qiling.const import * from qiling.os.os import QlOs +from qiling.os.fncc import QlOsFncc from .dlls import * from .const import * from .utils import * -class QlOsWindows(QlOs): + +class QlOsWindows(QlOs, QlOsFncc): def __init__(self, ql): QlOs.__init__(self, ql) + QlOsFncc.__init__(self, ql) self.ql = ql self.PE_RUN = True self.last_error = 0 @@ -56,6 +59,7 @@ def setupGDT(self): elif self.ql.archtype == QL_ARCH.X8664: ql_x8664_set_gs(self.ql) + def setupComponents(self): # handle manager self.handle_manager = HandleManager() @@ -73,6 +77,7 @@ def setupComponents(self): new_handle = Handle(obj=main_thread) self.handle_manager.append(new_handle) + # hook WinAPI in PE EMU def hook_winapi(self, int, address, size): if address in self.ql.loader.import_symbols: @@ -110,15 +115,16 @@ def hook_winapi(self, int, address, size): try: winapi_func(self.ql, address, {}) - except Exception: - logging.exception("") - logging.info("[!] %s Exception Found" % winapi_name) + except Exception as ex: + self.ql.log.exception(ex) + self.ql.log.info("%s Exception Found" % winapi_name) self.emu_error() - raise QlErrorSyscallError("[!] Windows API Implementation Error") + raise QlErrorSyscallError("Windows API Implementation Error") else: - logging.warning("[!] %s is not implemented" % winapi_name) + self.ql.log.warning("%s is not implemented" % winapi_name) if self.ql.debug_stop: - raise QlErrorSyscallNotFound("[!] Windows API Implementation Not Found") + raise QlErrorSyscallNotFound("Windows API Implementation Not Found") + def run(self): if self.ql.exit_point is not None: @@ -137,8 +143,8 @@ def run(self): self.stderr = self.ql.stderr try: - if self.ql.shellcoder: - self.ql.emu_start(self.ql.loader.entry_point, (self.ql.loader.entry_point + len(self.ql.shellcoder)), self.ql.timeout, self.ql.count) + if self.ql.code: + self.ql.emu_start(self.ql.loader.entry_point, (self.ql.loader.entry_point + len(self.ql.code)), self.ql.timeout, self.ql.count) else: self.ql.emu_start(self.ql.loader.entry_point, self.exit_point, self.ql.timeout, self.ql.count) except UcError: diff --git a/qiling/profiles/freebsd.ql b/qiling/profiles/freebsd.ql index 8d8c52f0f..7829ce0e4 100644 --- a/qiling/profiles/freebsd.ql +++ b/qiling/profiles/freebsd.ql @@ -1,4 +1,4 @@ -[SHELLCODER] +[CODE] ram_size = 0xa00000 entry_point = 0x1000000 diff --git a/qiling/profiles/linux.ql b/qiling/profiles/linux.ql index 8c4f9bc9f..5b53d982a 100644 --- a/qiling/profiles/linux.ql +++ b/qiling/profiles/linux.ql @@ -1,4 +1,4 @@ -[SHELLCODER] +[CODE] # ram_size 0xa00000 is 10MB ram_size = 0xa00000 entry_point = 0x1000000 diff --git a/qiling/profiles/macos.ql b/qiling/profiles/macos.ql index 94d218e93..50445e42b 100644 --- a/qiling/profiles/macos.ql +++ b/qiling/profiles/macos.ql @@ -12,7 +12,7 @@ heap_address = 0x500000000 heap_size = 0x5000000 -[SHELLCODER] +[CODE] # ram_size 0xa00000 is 10MB ram_size = 0xa00000 entry_point = 0x1000000 diff --git a/qiling/profiles/uefi.ql b/qiling/profiles/uefi.ql index 71f319627..9d29a88f9 100644 --- a/qiling/profiles/uefi.ql +++ b/qiling/profiles/uefi.ql @@ -3,16 +3,14 @@ heap_address = 0x78000000 heap_size = 0x02000000 stack_address = 0x77800000 stack_size = 0x00800000 -image_address = 0x77000000 -# entry_point = 0x77400000 +image_address = 0x00100000 [OS32] heap_address = 0x78000000 heap_size = 0x02000000 stack_address = 0x77800000 stack_size = 0x00800000 -image_address = 0x77000000 -# entry_point = 0x77400000 +image_address = 0x00100000 [SMRAM] heap_address = 0x7A000000 @@ -24,15 +22,17 @@ heap_size = 0x02000000 [HOB_LIST] # EFI_GLOBAL_VARIABLE Guid = 7739f24c-93d7-11d4-9a3a-0090273fc14d -Table = 0x00000000ffffffff +# the HOB list must end with an entry whose HobType is FFFF +TableData = FFFF000000000000 [DXE_SERVICE_TABLE] Guid = 05ad34ba-6f02-4214-952e-4da0398e2bb9 -Table = 0x00000000ffffffff + +[SMM_RUNTIME_SERVICES_TABLE] +Guid = 395c33fe-287f-413e-a055-8088c0e1d43e [LOADED_IMAGE_PROTOCOL] -guid = 5b1b31a1-9562-11d2-8e3f-00a0c969723b -revision = 0x1000 +Guid = 5b1b31a1-9562-11d2-8e3f-00a0c969723b [MMIO] sbreg_base = 0xFD000000 @@ -45,7 +45,6 @@ dir = # split log file, use with multithread split = False - [MISC] # append string into different logs # maily for multiple times Ql run with one file diff --git a/qiling/profiles/windows.ql b/qiling/profiles/windows.ql index 0ad6a9bb5..d8046a125 100644 --- a/qiling/profiles/windows.ql +++ b/qiling/profiles/windows.ql @@ -16,7 +16,7 @@ image_address = 0x400000 dll_address = 0x10000000 entry_point = 0x40000 -[SHELLCODER] +[CODE] # ram_size 0xa00000 is 10MB ram_size = 0xa00000 entry_point = 0x1000000 diff --git a/qiling/utils.py b/qiling/utils.py index 833449760..82b92b841 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -1,24 +1,25 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# """ This module is intended for general purpose functions that can be used thoughout the qiling framework """ -import importlib, logging, os, logging, copy, re, pefile, configparser +import importlib, os, copy, re, pefile, configparser, logging from logging import LogRecord from pathlib import Path -from .exception import * -from .const import QL_ARCH, QL_ARCH_ALL, QL_ENDIAN, QL_OS, QL_OS_ALL, QL_OUTPUT, QL_DEBUGGER, QL_ARCH_32BIT, QL_ARCH_64BIT, QL_ARCH_16BIT -from .const import debugger_map, arch_map, os_map, D_INFO from unicorn import UcError, UC_ERR_READ_UNMAPPED, UC_ERR_FETCH_UNMAPPED from keystone import * from capstone import * -FMT_STR = "[%(levelname)s] [%(filename)s:%(lineno)d]\t%(message)s" +from .exception import * +from .const import QL_ARCH, QL_ARCH_ALL, QL_ENDIAN, QL_OS, QL_OS_ALL, QL_OUTPUT, QL_DEBUGGER, QL_ARCH_32BIT, QL_ARCH_64BIT, QL_ARCH_16BIT +from .const import debugger_map, arch_map, os_map + +FMT_STR = "%(levelname)s\t%(message)s" # \033 -> ESC # ESC [ -> CSI @@ -41,49 +42,64 @@ class COLOR_CODE: 'CRITICAL': COLOR_CODE.CRIMSON, 'ERROR': COLOR_CODE.RED } +class QilingColoredFormatter(logging.Formatter): + def __init__(self, ql, *args, **kwargs): + super(QilingColoredFormatter, self).__init__(*args, **kwargs) + self._ql = ql -LEVEL_NAME = { - 'WARNING': f"{COLOR_CODE.YELLOW}!{COLOR_CODE.ENDC}", - 'INFO': f"{COLOR_CODE.BLUE}={COLOR_CODE.ENDC}", - 'DEBUG': f"{COLOR_CODE.MAGENTA}+{COLOR_CODE.ENDC}", - 'CRITICAL': f"{COLOR_CODE.CRIMSON}!{COLOR_CODE.ENDC}", - 'ERROR': f"{COLOR_CODE.RED}x{COLOR_CODE.ENDC}" -} - -class ColoredFormatter(logging.Formatter): - def __init__(self, *args, **kwargs): - super(ColoredFormatter, self).__init__(*args, **kwargs) - def get_colored_level(self, record: LogRecord): - levelname = record.levelname - return LEVEL_NAME[levelname] + LEVEL_NAME = { + 'WARNING': f"{COLOR_CODE.YELLOW}[!]{COLOR_CODE.ENDC}", + 'INFO': f"{COLOR_CODE.BLUE}[=]{COLOR_CODE.ENDC}", + 'DEBUG': f"{COLOR_CODE.MAGENTA}[+]{COLOR_CODE.ENDC}", + 'CRITICAL': f"{COLOR_CODE.CRIMSON}[x]{COLOR_CODE.ENDC}", + 'ERROR': f"{COLOR_CODE.RED}[x]{COLOR_CODE.ENDC}" + } + return LEVEL_NAME[record.levelname] def format(self, record: LogRecord): - _record = copy.copy(record) - _record.levelname = self.get_colored_level(_record) - return super(ColoredFormatter, self).format(_record) + record.levelname = self.get_colored_level(record) + try: + cur_thread = self._ql.os.thread_management.cur_thread + if cur_thread is not None: + record.levelname = f"{record.levelname} {COLOR_CODE.GREEN}{str(cur_thread)}{COLOR_CODE.ENDC}" + except AttributeError: + pass + return super(QilingColoredFormatter, self).format(record) -class MultithreadColoredFormatter(ColoredFormatter): +class QilingPlainFormatter(logging.Formatter): def __init__(self, ql, *args, **kwargs): - super(MultithreadColoredFormatter, self).__init__(*args, **kwargs) + super(QilingPlainFormatter, self).__init__(*args, **kwargs) self._ql = ql + + def get_level(self, record: LogRecord): + LEVEL_NAME = { + 'WARNING': "[!]", + 'INFO': "[=]", + 'DEBUG': "[+]", + 'CRITICAL': "[x]", + 'ERROR': "[x]" + } + return LEVEL_NAME[record.levelname] def format(self, record: LogRecord): + record.levelname = self.get_level(record) try: cur_thread = self._ql.os.thread_management.cur_thread + if cur_thread is not None: + record.levelname = f"{record.levelname} {str(cur_thread)}" except AttributeError: - return super(MultithreadColoredFormatter, self).format(record) - _record = copy.copy(record) - levelname = self.get_colored_level(_record) - _record.levelname = f"{levelname}]\t[{COLOR_CODE.GREEN}Thread {cur_thread.id}{COLOR_CODE.ENDC}" - msg = super(ColoredFormatter, self).format(_record) - return msg + pass + return super(QilingPlainFormatter, self).format(record) class RegexFilter(logging.Filter): def __init__(self, filters): super(RegexFilter, self).__init__() - self._filters = [ re.compile(ft) for ft in filters ] + self.update_filters(filters) + def update_filters(self, filters): + self._filters = [ re.compile(ft) for ft in filters ] + def filter(self, record: LogRecord): msg = record.getMessage() for ft in self._filters: @@ -91,21 +107,6 @@ def filter(self, record: LogRecord): return True return False -class MultithreadSplitHandler(logging.Handler): - def __init__(self, ql): - super(MultithreadSplitHandler, self).__init__() - self._ql = ql - - def emit(self, record: LogRecord): - msg = self.format(record) - try: - cur_thread = self._ql.os.thread_management.cur_thread - except AttributeError: - self._ql._msg_before_main_thread.append((record.levelno, msg)) - return - cur_thread.log_file_fd.log(record.levelno, msg) - - class QlFileDes: def __init__(self, init): self.__fds = init @@ -167,7 +168,7 @@ def ql_get_arch_bits(arch): return 32 if arch in QL_ARCH_64BIT: return 64 - raise QlErrorArch("[!] Invalid Arch") + raise QlErrorArch("Invalid Arch") def ql_is_valid_ostype(ostype): if ostype not in QL_OS_ALL: @@ -258,12 +259,12 @@ def ql_get_module_function(module_name, function_name = None): try: imp_module = importlib.import_module(module_name) except Exception as ex: - raise QlErrorModuleNotFound("[!] Unable to import module %s (%s)" % (module_name, ex)) + raise QlErrorModuleNotFound("Unable to import module %s (%s)" % (module_name, ex)) try: module_function = getattr(imp_module, function_name) except: - raise QlErrorModuleFunctionNotFound("[!] Unable to import %s from %s" % (function_name, imp_module)) + raise QlErrorModuleFunctionNotFound("Unable to import %s from %s" % (function_name, imp_module)) return module_function @@ -353,6 +354,7 @@ def getident(): return arch, ostype, archendian + def ql_pe_parse_emu_env(path): try: pe = pefile.PE(path, fast_load=True) @@ -387,6 +389,7 @@ def ql_pe_parse_emu_env(path): return arch, ostype, archendian + def ql_guess_emu_env(path): arch = None ostype = None @@ -413,19 +416,22 @@ def ql_guess_emu_env(path): arch, ostype, archendian = ql_pe_parse_emu_env(path) if ostype not in (QL_OS_ALL): - raise QlErrorOsType("[!] File does not belong to either 'linux', 'windows', 'freebsd', 'macos', 'ios', 'dos'") + raise QlErrorOsType("File does not belong to either 'linux', 'windows', 'freebsd', 'macos', 'ios', 'dos'") return arch, ostype, archendian + def loader_setup(ostype, ql): loadertype_str = loadertype_convert_str(ostype) function_name = "QlLoader" + loadertype_str return ql_get_module_function(f"qiling.loader.{loadertype_str.lower()}", function_name)(ql) + def component_setup(component_type, component_name, ql): function_name = "Ql" + component_name.capitalize() + "Manager" return ql_get_module_function(f"qiling.{component_type}.{component_name}", function_name)(ql) + def debugger_setup(debugger, ql): # default remote server remotedebugsrv = "gdb" @@ -441,7 +447,7 @@ def debugger_setup(debugger, ql): if debugger_convert(remotedebugsrv) not in (QL_DEBUGGER): - raise QlErrorOutput("[!] Error: Debugger not supported") + raise QlErrorOutput("Error: Debugger not supported") debugsession = ql_get_module_function(f"qiling.debugger.{remotedebugsrv}.{remotedebugsrv}", f"Ql{str.capitalize(remotedebugsrv)}") @@ -449,7 +455,7 @@ def debugger_setup(debugger, ql): def arch_setup(archtype, ql): if not ql_is_valid_arch(archtype): - raise QlErrorArch("[!] Invalid Arch") + raise QlErrorArch("Invalid Arch") if archtype == QL_ARCH.ARM_THUMB: archtype = QL_ARCH.ARM @@ -464,17 +470,19 @@ def arch_setup(archtype, ql): return ql_get_module_function(f"qiling.arch.{arch_str.lower()}", archmanager)(ql) + # This function is extracted from os_setup so I put it here. def ql_syscall_mapping_function(ostype): ostype_str = ostype_convert_str(ostype) return ql_get_module_function(f"qiling.os.{ostype_str.lower()}.map_syscall", "map_syscall") + def os_setup(archtype, ostype, ql): if not ql_is_valid_ostype(ostype): - raise QlErrorOsType("[!] Invalid OSType") + raise QlErrorOsType("Invalid OSType") if not ql_is_valid_arch(archtype): - raise QlErrorArch("[!] Invalid Arch %s" % archtype) + raise QlErrorArch("Invalid Arch %s" % archtype) ostype_str = ostype_convert_str(ostype) ostype_str = ostype_str.capitalize() @@ -483,7 +491,11 @@ def os_setup(archtype, ostype, ql): def profile_setup(ostype, profile, ql): - logging.debug("Customized profile: %s" % profile) + _profile = "Default" + if profile != None: + _profile = profile + + debugmsg = "Profile: %s" % _profile os_profile = os.path.join(os.path.dirname(os.path.abspath(__file__)), "profiles", ostype_convert_str(ostype) + ".ql") @@ -494,7 +506,7 @@ def profile_setup(ostype, profile, ql): config = configparser.ConfigParser() config.read(profiles) - return config + return config, debugmsg def ql_resolve_logger_level(output, verbose): level = logging.INFO @@ -507,155 +519,61 @@ def ql_resolve_logger_level(output, verbose): level = logging.DEBUG elif verbose >= 1: level = logging.INFO - - logging.getLogger().setLevel(level) - - -# TODO: qltool compatibility -def ql_setup_logger(ql, log_dir, log_filename, log_split, console, filter, multithread): - # Covered use cases: - # - Normal console output. - # - Write to a single file. - # - Write to splitted log files. - - # Clear all handlers and filters. - lger = logging.getLogger() - lger.handlers = [] - lger.filters = [] - - # Do we have console output? - if console: - handler = logging.StreamHandler() - if multithread: - formatter = MultithreadColoredFormatter(ql, FMT_STR) - else: - formatter = ColoredFormatter(FMT_STR) - handler.setFormatter(formatter) - lger.addHandler(handler) - else: - logging.disable(level=logging.CRITICAL) - - # If log_dir isn't specified, return. - if log_dir is None or log_dir == "": - if log_split: - raise QlErrorOutput("log_split should be used with log_dir") - return - - os.makedirs(log_dir, 0o755, exist_ok=True) - - # If we don't have to split logs, that's the most simple case. - if not log_split: - handler = logging.FileHandler(Path(log_dir) / log_filename) - handler.setFormatter(logging.Formatter(FMT_STR)) - lger.addHandler(handler) - else: - if multithread: - # A placeholder for messages before the first(main) thread is created. - ql._msg_before_main_thread = [] - handler = MultithreadSplitHandler(ql) - handler.setFormatter(logging.Formatter(FMT_STR)) - lger.addHandler(handler) - # For spliting logs with child process, we do that during fork. - - # Remeber to add filters. - if filter is not None and type(filter) == list and len(filter) != 0: - lger.addFilter(RegexFilter(filter)) - lger.setLevel(logging.INFO) - -def ql_create_disassembler(archtype, archendian, reg_cpsr=None): - if archtype == QL_ARCH.ARM: # QL_ARM - mode = CS_MODE_ARM - if archendian == QL_ENDIAN.EB: - # TODO: Test for big endian. - reg_cpsr_v = 0b100000 - # reg_cpsr_v = 0b000000 - else: - reg_cpsr_v = 0b100000 - - if reg_cpsr & reg_cpsr_v != 0: - mode = CS_MODE_THUMB - - if archendian == QL_ENDIAN.EB: - md = Cs(CS_ARCH_ARM, mode) - # md = Cs(CS_ARCH_ARM, mode + CS_MODE_BIG_ENDIAN) - else: - md = Cs(CS_ARCH_ARM, mode) - - elif archtype == QL_ARCH.ARM_THUMB: - md = Cs(CS_ARCH_ARM, CS_MODE_THUMB) - - elif archtype == QL_ARCH.X86: # QL_X86 - md = Cs(CS_ARCH_X86, CS_MODE_32) - - elif archtype == QL_ARCH.X8664: # QL_X86_64 - md = Cs(CS_ARCH_X86, CS_MODE_64) - - elif archtype == QL_ARCH.ARM64: # QL_ARM64 - md = Cs(CS_ARCH_ARM64, CS_MODE_ARM) + return level - elif archtype == QL_ARCH.A8086: # QL_A8086 - md = Cs(CS_ARCH_X86, CS_MODE_16) +QL_INSTANCE_ID = 114514 - elif archtype == QL_ARCH.MIPS: # QL_MIPS32 - if archendian == QL_ENDIAN.EB: - md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN) - else: - md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) +# TODO: qltool compatibility +def ql_setup_logger(ql, log_file, console, filters, multithread, log_override): + global QL_INSTANCE_ID + # If there is an override for our logger, then use it. + if log_override is not None: + log = log_override else: - raise QlErrorArch("[!] Unknown arch defined in utils.py (debug output mode)") - - return md - -def ql_create_assembler(archtype, archendian, reg_cpsr=None): - if archtype == QL_ARCH.ARM: # QL_ARM - mode = KS_MODE_ARM - if archendian == QL_ENDIAN.EB: - # TODO: Test for big endian. - reg_cpsr_v = 0b100000 - # reg_cpsr_v = 0b000000 - else: - reg_cpsr_v = 0b100000 - - if reg_cpsr & reg_cpsr_v != 0: - mode = KS_MODE_THUMB - - if archendian == QL_ENDIAN.EB: - ks = Ks(KS_ARCH_ARM, mode) - # md = Cs(CS_ARCH_ARM, mode + CS_MODE_BIG_ENDIAN) - else: - ks = Ks(KS_ARCH_ARM, mode) - - elif archtype == QL_ARCH.ARM_THUMB: - ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB) - - elif archtype == QL_ARCH.X86: # QL_X86 - ks = Ks(KS_ARCH_X86, KS_MODE_32) - - elif archtype == QL_ARCH.X8664: # QL_X86_64 - ks = Ks(KS_ARCH_X86, KS_MODE_64) - - elif archtype == QL_ARCH.ARM64: # QL_ARM64 - ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN) - - elif archtype == QL_ARCH.A8086: # QL_A8086 - ks = Ks(KS_ARCH_X86, KS_MODE_16) - - elif archtype == QL_ARCH.MIPS: # QL_MIPS32 - if archendian == QL_ENDIAN.EB: - ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + KS_MODE_BIG_ENDIAN) + # We should leave the root logger untouched. + log = logging.getLogger(f"qiling{QL_INSTANCE_ID}") + QL_INSTANCE_ID += 1 + + # Disable propagation to avoid duplicate output. + log.propagate = False + # Clear all handlers and filters. + log.handlers = [] + log.filters = [] + + # Do we have console output? + if console: + handler = logging.StreamHandler() + formatter = QilingColoredFormatter(ql, FMT_STR) + handler.setFormatter(formatter) + log.addHandler(handler) else: - ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + KS_MODE_LITTLE_ENDIAN) + log.setLevel(logging.CRITICAL) + + # Do we have to write log to a file? + if log_file is not None: + handler = logging.FileHandler(log_file) + formatter = QilingPlainFormatter(ql, FMT_STR) + handler.setFormatter(formatter) + log.addHandler(handler) + + # Remeber to add filters if necessary. + # If there aren't any filters, we do add the filters until users specify any. + log_filter = None + + if filters is not None and type(filters) == list and len(filters) != 0: + log_filter = RegexFilter(filters) + log.addFilter(log_filter) + + log.setLevel(logging.INFO) - else: - raise QlErrorArch("[!] Unknown arch defined in utils.py (debug output mode)") + return log, log_filter - return ks # verify if emulator returns properly def verify_ret(ql, err): - logging.debug("Got exception %u: init SP = %x, current SP = %x, PC = %x" %(err.errno, ql.os.init_sp, ql.reg.arch_sp, ql.reg.arch_pc)) + ql.log.debug("Got exception %u: init SP = %x, current SP = %x, PC = %x" %(err.errno, ql.os.init_sp, ql.reg.arch_sp, ql.reg.arch_pc)) # print("Got exception %u: init SP = %x, current SP = %x, PC = %x" %(err.errno, ql.os.init_sp, self.reg.arch_sp, self.reg.arch_pc)) ql.os.RUN = False @@ -672,14 +590,14 @@ def verify_ret(ql, err): if ql.archtype == QL_ARCH.X8664: # Win64 if ql.os.init_sp == ql.reg.arch_sp or ql.os.init_sp + 8 == ql.reg.arch_sp or ql.os.init_sp + 0x10 == ql.reg.arch_sp: # FIXME - # [+] 0x11626 c3 ret + # 0x11626 c3 ret # print("OK, stack balanced!") pass else: raise else: # Win32 if ql.os.init_sp + 12 == ql.reg.arch_sp: # 12 = 8 + 4 - # [+] 0x114dd c2 08 00 ret 8 + # 0x114dd c2 08 00 ret 8 pass else: raise diff --git a/qltool b/qltool index 8ed65e82d..882361511 100755 --- a/qltool +++ b/qltool @@ -1,14 +1,15 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import argparse, os, string, sys, ast, unicorn from binascii import unhexlify from qiling import * -from qiling.utils import ql_create_assembler, arch_convert +from qiling.arch.utils import ql_create_assembler +from qiling.utils import arch_convert from qiling.const import QL_ARCH, QL_ENDIAN from qiling.__version__ import __version__ as ql_version from qiling.extensions.coverage import utils as cov_utils @@ -36,14 +37,14 @@ def parse_args(parser, commands): parser.parse_args(argv, namespace=n) return args -# read shellcode from file +# read code from file def read_file(fname): with open(fname,"rb") as f: content = f.read() f.close return content -def run_shellcode(options): +def run_code(options): if not options.os in ("linux", "windows", "freebsd", "macos"): print("ERROR: -os required: either linux, windows, freebsd, macos") exit(1) @@ -58,22 +59,22 @@ def run_shellcode(options): elif options.hex == True: if options.input is not None: - print ("[+] Load HEX from ARGV") - shellcoder = str(options.input).strip("\\\\x").split("x") - shellcoder = "".join(shellcoder).strip() - shellcoder = bytes.fromhex(shellcoder) + print ("Load HEX from ARGV") + code = str(options.input).strip("\\\\x").split("x") + code = "".join(code).strip() + code = bytes.fromhex(code) elif options.filename is not None: - print ("[+] Load HEX from FILE") - shellcoder = str(read_file(options.filename)).strip('b\'').strip('\\n') - shellcoder = shellcoder.strip('x').split("\\\\x") - shellcoder = "".join(shellcoder).strip() - shellcoder = bytes.fromhex(shellcoder) + print ("Load HEX from FILE") + code = str(read_file(options.filename)).strip('b\'').strip('\\n') + code = code.strip('x').split("\\\\x") + code = "".join(code).strip() + code = bytes.fromhex(code) else: print("ERROR: File not found") exit(1) elif options.asm == True: - print ("[+] Load ASM from FILE") + print ("Load ASM from FILE") assembly = read_file(options.filename) archtype = arch_convert(options.arch) archendian = QL_ENDIAN.EL @@ -81,29 +82,29 @@ def run_shellcode(options): archendian = QL_ENDIAN.EB # TODO: Thumb support. assembler = ql_create_assembler(archtype, archendian, 0) - shellcoder, _ = assembler.asm(assembly) - shellcoder = bytes(shellcoder) + code, _ = assembler.asm(assembly) + code = bytes(code) else: - print ("[+] Load BIN from FILE") + print ("Load BIN from FILE") if options.filename is None: print("ERROR: File not found") exit(1) - shellcoder = read_file(options.filename) + code = read_file(options.filename) - return Qiling(shellcoder = shellcoder, archtype = options.arch, bigendian = options.bigendian, ostype = options.os, rootfs = options.rootfs, output = options.output, profile = options.profile) + return Qiling(code = code, archtype = options.arch, bigendian = options.bigendian, ostype = options.os, rootfs = options.rootfs, output = options.output, profile = options.profile) def version(): print("qltool for Qiling %s, using Unicorn %s" %(ql_version, unicorn.__version__)) def usage(): version() - print("\nUsage: ./qltool [run|shellcode] OPTIONS") + print("\nUsage: ./qltool [run|code] OPTIONS") - print("\n\nWith shellcode:") - print("\t ./qltool shellcode --os linux --arch arm --hex -f examples/shellcodes/linarm32_tcp_reverse_shell.hex") - print("\t ./qltool shellcode --os linux --arch x86 --asm -f examples/shellcodes/lin32_execve.asm") - print("\t ./qltool shellcode --os linux --arch arm --hex -f examples/shellcodes/linarm32_tcp_reverse_shell.hex --strace") + print("\n\nWith code:") + print("\t ./qltool code --os linux --arch arm --hex -f examples/codes/linarm32_tcp_reverse_shell.hex") + print("\t ./qltool code --os linux --arch x86 --asm -f examples/codes/lin32_execve.asm") + print("\t ./qltool code --os linux --arch arm --hex -f examples/codes/linarm32_tcp_reverse_shell.hex --strace") print("\n\nWith binary file:") print("\t ./qltool run -f examples/rootfs/x8664_linux/bin/x8664_hello --rootfs examples/rootfs/x8664_linux/") @@ -121,8 +122,8 @@ def usage(): print("\n\nwith binary file and various output format:") print("\t ./qltool run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --output=disasm") - print("\t ./qltool run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --strace -e open") - print("\t ./qltool run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --strace -e open --log-dir=qlog") + print("\t ./qltool run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --strace -e ^open") + print("\t ./qltool run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --strace -e ^open") print("\n\nWith UEFI file:") print("\t ./qltool run -f examples/rootfs/x8664_efi/bin/TcgPlatformSetupPolicy --rootfs examples/rootfs/x8664_efi --env examples/rootfs/x8664_efi/rom2_nvar.pickel") @@ -142,16 +143,16 @@ if __name__ == '__main__': run_parser.add_argument('--args', required=False, default=[], nargs=argparse.REMAINDER, dest="args", help="args") run_parser.add_argument('run_args', default=[], nargs=argparse.REMAINDER) - shellcode_parser = commands.add_parser('shellcode', help = 'shellcode') - shellcode_parser.add_argument('-f', '--filename', required=False, metavar="FILE", dest="filename", help="filename") - shellcode_parser.add_argument('-i', '--input', required=False, metavar="INPUT", dest="input", help='input hex value') - shellcode_parser.add_argument('--arch', required=True, help='option are x86, x8664, arm, arm64, mips') - shellcode_parser.add_argument('--bigendian', required=False, default=False, help='Type: Bool True or False') - shellcode_parser.add_argument('--os', required=True, help='option are windows, linux, freebsd and macos') - shellcode_parser.add_argument('--rootfs', required=False, help='emulated rootfs, that is where all the so or dll sits') - shellcode_parser.add_argument('--asm', action='store_true', default=False, dest='asm', help='input file format, -asm') - shellcode_parser.add_argument('--hex', action='store_true', default=False, dest='hex', help='input file format, -hex') - shellcode_parser.add_argument('--bin', action='store_true', default=True, dest='bin', help='input file format, -bin') + code_parser = commands.add_parser('code', help = 'code') + code_parser.add_argument('-f', '--filename', required=False, metavar="FILE", dest="filename", help="filename") + code_parser.add_argument('-i', '--input', required=False, metavar="INPUT", dest="input", help='input hex value') + code_parser.add_argument('--arch', required=True, help='option are x86, x8664, arm, arm64, mips') + code_parser.add_argument('--bigendian', required=False, default=False, help='Type: Bool True or False') + code_parser.add_argument('--os', required=True, help='option are windows, linux, freebsd and macos') + code_parser.add_argument('--rootfs', required=False, help='emulated rootfs, that is where all the so or dll sits') + code_parser.add_argument('--asm', action='store_true', default=False, dest='asm', help='input file format, -asm') + code_parser.add_argument('--hex', action='store_true', default=False, dest='hex', help='input file format, -hex') + code_parser.add_argument('--bin', action='store_true', default=True, dest='bin', help='input file format, -bin') if len(sys.argv) <= 1: usage() @@ -160,8 +161,8 @@ if __name__ == '__main__': if (sys.argv[1] == 'run'): comm_parser = run_parser - elif (sys.argv[1] == 'shellcode'): - comm_parser = shellcode_parser + elif (sys.argv[1] == 'code'): + comm_parser = code_parser elif (sys.argv[1] == 'version') or (sys.argv[1] == '-v') or (sys.argv[1] == '--version'): version() @@ -187,7 +188,7 @@ if __name__ == '__main__': comm_parser.add_argument('--console', required=False, default=True, dest='console', help='display in console') comm_parser.add_argument('-e', '--filter', metavar="FUNCTION NAME", required=False, dest="filter", default=None, help="Only work with strace mode, you can choose what to be printout, for multiple function calls should be separated by comma") - comm_parser.add_argument('--log-dir', required=False, metavar="DIRECTORY NAME", dest='log_dir', default=None, help='the destinartion you want to store you log') + comm_parser.add_argument('--log-file', dest="log_file", help="Write log to a file") comm_parser.add_argument('--trace', action='store_true', default=False, dest='trace', help='Run in strace mode') comm_parser.add_argument('--root', action='store_true', default=False, dest='root', help='Enable sudo required mode') comm_parser.add_argument('--debug_stop', action='store_true', default=False, dest='debug_stop', @@ -244,11 +245,13 @@ if __name__ == '__main__': if (options.subparser_name == 'run'): # with argv if options.filename is not None and options.run_args == []: - ql = Qiling(argv=[options.filename] + options.args, rootfs=options.rootfs, profile=options.profile, output=options.output, console=options.console, log_dir=options.log_dir, env=env) + ql = Qiling(argv=[options.filename] + options.args, rootfs=options.rootfs, profile=options.profile, + output=options.output, console=options.console, log_file=options.log_file, multithread=options.multithread, env=env) # Without argv elif options.filename is None and options.args == [] and options.run_args != []: - ql = Qiling(argv=options.run_args, rootfs=options.rootfs, profile=options.profile, output=options.output, console=options.console, log_dir=options.log_dir, env=env) + ql = Qiling(argv=options.run_args, rootfs=options.rootfs, profile=options.profile, + output=options.output, console=options.console, log_file=options.log_file, multithread=options.multithread, env=env) else: print("ERROR: Command error!") @@ -258,11 +261,12 @@ if __name__ == '__main__': if options.qdb == True: from qiling.debugger.qdb import QlQdb as Qdb - Qdb(ql, rr=options.rr) + Qdb(ql, rr=options.rr).interactive() + exit() - # ql shellcode setup - elif (options.subparser_name == 'shellcode'): - ql = run_shellcode(options) + # ql code setup + elif (options.subparser_name == 'code'): + ql = run_code(options) else: print("ERROR: Unknown command") @@ -278,9 +282,6 @@ if __name__ == '__main__': if options.root: ql.root = True - if options.multithread: - ql.multithread = True - if options.verbose: ql.verbose = options.verbose else: diff --git a/tests/ARKA.EXE b/tests/ARKA.EXE deleted file mode 100755 index 6fdbce1e3..000000000 Binary files a/tests/ARKA.EXE and /dev/null differ diff --git a/tests/test_android.py b/tests/test_android.py index 95bd9fb3d..e8a988758 100644 --- a/tests/test_android.py +++ b/tests/test_android.py @@ -1,12 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import unittest -import os -import sys -import logging +import os, sys, unittest sys.path.append("..") from qiling import * @@ -24,12 +21,12 @@ def syscall_getrandom(ql, buf, buflen, flags, *args, **kw): except: regreturn = -1 - logging.info("getrandom(0x%x, 0x%x, 0x%x) = %d" % + ql.log.info("getrandom(0x%x, 0x%x, 0x%x) = %d" % (buf, buflen, flags, regreturn)) if data: - logging.debug("[+] getrandom() CONTENT:") - logging.debug(str(data)) + ql.log.debug("getrandom() CONTENT:") + ql.log.debug(str(data)) return regreturn @@ -46,11 +43,11 @@ def syscall_fstatfs(ql, fd, buf, *args, **kw): except: regreturn = -1 - logging.info("fstatfs(0x%x, 0x%x) = %d" % (fd, buf, regreturn)) + ql.log.info("fstatfs(0x%x, 0x%x) = %d" % (fd, buf, regreturn)) if data: - logging.debug("[+] fstatfs() CONTENT:") - logging.debug(str(data)) + ql.log.debug("fstatfs() CONTENT:") + ql.log.debug(str(data)) return regreturn diff --git a/tests/test_basic.py b/tests/test_basic.py deleted file mode 100644 index a459db90c..000000000 --- a/tests/test_basic.py +++ /dev/null @@ -1,16 +0,0 @@ -import unittest -import sys -sys.path.append("..") -from qiling import * -from qiling.utils import * - -class TestBasic(unittest.TestCase): - def test_invalid_module(self): - self.assertRaises(QlErrorModuleNotFound, ql_get_module_function, "InvalidModule", "InvalidFunction") - - def test_invalid_module_function(self): - self.assertRaises(QlErrorModuleFunctionNotFound, ql_get_module_function, "qiling.arch.x86", "InvalidFunction") - - -if __name__ == "__main__": - unittest.main() \ No newline at end of file diff --git a/tests/test_debugger.py b/tests/test_debugger.py index c9f41073c..abec0350f 100644 --- a/tests/test_debugger.py +++ b/tests/test_debugger.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys, subprocess, threading, unittest, socket, time from binascii import unhexlify + sys.path.append("..") from qiling import * from qiling.exception import * @@ -85,7 +86,7 @@ def gdb_test_client(): def test_gdbdebug_shellcode_server(self): X8664_LIN = unhexlify('31c048bbd19d9691d08c97ff48f7db53545f995257545eb03b0f05') - ql = Qiling(shellcoder = X8664_LIN, archtype = "x8664", ostype = "linux") + ql = Qiling(code = X8664_LIN, archtype = "x8664", ostype = "linux") ql.debugger = "gdb:127.0.0.1:9998" def gdb_test_client(): diff --git a/tests/test_dos.py b/tests/test_dos.py index c44ea2942..a0600e692 100644 --- a/tests/test_dos.py +++ b/tests/test_dos.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os, random, sys, unittest + import string as st from binascii import unhexlify diff --git a/tests/test_dos_exe.py b/tests/test_dos_exe.py index aedee7d1b..f0c6f2833 100644 --- a/tests/test_dos_exe.py +++ b/tests/test_dos_exe.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os, random, sys, unittest import string as st diff --git a/tests/test_edl.py b/tests/test_edl.py index cfe98580c..31fc8a251 100644 --- a/tests/test_edl.py +++ b/tests/test_edl.py @@ -1,15 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import unittest -import os -import sys -import time -sys.path.append("..") +import os, sys, time, unittest from struct import pack -import sys + sys.path.append("..") from qiling import * from unicorn import * diff --git a/tests/test_elf.py b/tests/test_elf.py index 244242510..be60a3629 100644 --- a/tests/test_elf.py +++ b/tests/test_elf.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import sys, unittest, subprocess, string, random, os, logging +import sys, unittest, subprocess, string, random, os from unicorn import UcError, UC_ERR_READ_UNMAPPED, UC_ERR_FETCH_UNMAPPED @@ -107,7 +107,11 @@ def my_puts_exit(ql): ql.run() - self.assertEqual(0x1 or 140736282240864, self.test_exit_rdi) + + if self.test_exit_rdi == 140736282240864: + self.test_exit_rdi = 0x1 + + self.assertEqual(0x1, self.test_exit_rdi) self.assertEqual("CCCC", self.test_enter_str) del self.test_exit_rdi @@ -264,7 +268,7 @@ def my_puts(ql): all_mem = ql.mem.save() ql.mem.restore(all_mem) - ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_hello"], "../examples/rootfs/arm_linux", output = "debug", profile='profiles/append_test.ql', log_split=True) + ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_hello"], "../examples/rootfs/arm_linux", output = "debug", profile='profiles/append_test.ql') ql.set_api('puts', my_puts) ql.run() del ql @@ -702,7 +706,7 @@ def my_syscall_write(ql, write_fd, write_buf, write_count, *args, **kw): regreturn = 0 buf = None mapaddr = ql.mem.map_anywhere(0x100000) - logging.info("0x%x" % mapaddr) + ql.log.info("0x%x" % mapaddr) reg = ql.reg.read("r0") print("reg : 0x%x" % reg) @@ -711,12 +715,12 @@ def my_syscall_write(ql, write_fd, write_buf, write_count, *args, **kw): try: buf = ql.mem.read(write_buf, write_count) - logging.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) + ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) ql.os.fd[write_fd].write(buf) regreturn = write_count except: regreturn = -1 - logging.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) + ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) if ql.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP): raise self.set_syscall = reg @@ -817,7 +821,7 @@ def __init__(self): self.id = fake_id fake_id += 1 ids.append(self.id) - logging.info(f"Creating Fake_urandom with id {self.id}") + ql.log.info(f"Creating Fake_urandom with id {self.id}") def read(self, size): return b'\x01' @@ -916,32 +920,6 @@ def test_x8664_symlink(self): ql.run() del ql - def test_demigod_m0hamed_x86(self): - ql = Qiling(["../examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko"], "../examples/rootfs/x86_linux", output="disasm") - try: - procfile_read_func_begin = ql.loader.load_address + 0x11e0 - procfile_read_func_end = ql.loader.load_address + 0x11fa - ql.run(begin=procfile_read_func_begin, end=procfile_read_func_end) - except UcError as e: - print(e) - sys.exit(-1) - del ql - - def test_demigod_m0hamed_x8664(self): - ql = Qiling(["../examples/rootfs/x8664_linux/kernel/m0hamed_rootkit.ko"], "../examples/rootfs/x8664_linux", output="disasm") - try: - ql.run() - except UcError as e: - print(e) - sys.exit(-1) - del ql - - def test_demigod_hello_mips32(self): - ql = Qiling(["../examples/rootfs/mips32_linux/kernel/hello.ko"], "../examples/rootfs/mips32_linux", output="debug") - begin = ql.loader.load_address + 0x1060 - end = ql.loader.load_address + 0x1084 - ql.run(begin=begin, end=end) - del ql def test_x8664_absolute_path(self): class MyPipe(): diff --git a/tests/test_elf.sh b/tests/test_elf.sh index 2a988833c..da35c6985 100755 --- a/tests/test_elf.sh +++ b/tests/test_elf.sh @@ -2,6 +2,13 @@ # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -python3 ./test_posix.py && python3 test_android.py && python3 ./test_debugger.py && python3 ./test_uefi.py && python3 ./test_shellcode.py && python3 ./test_edl.py +python3 ./test_posix.py && +python3 test_elf_multithread.py && +python3 test_elf_ko.py && +python3 test_android.py && +python3 ./test_debugger.py && +python3 ./test_uefi.py && +python3 ./test_shellcode.py && +python3 ./test_edl.py diff --git a/tests/test_elf_ko.py b/tests/test_elf_ko.py new file mode 100644 index 000000000..4e712e9e0 --- /dev/null +++ b/tests/test_elf_ko.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +import sys, unittest, subprocess, string, random, os + +from unicorn import UcError, UC_ERR_READ_UNMAPPED, UC_ERR_FETCH_UNMAPPED + +sys.path.append("..") +from qiling import * +from qiling.const import * +from qiling.exception import * +from qiling.os.posix import syscall +from qiling.os.mapper import QlFsMappedObject +from qiling.os.stat import Fstat + +class ELF_KO_Test(unittest.TestCase): + + def test_demigod_m0hamed_x86(self): + ql = Qiling(["../examples/rootfs/x86_linux/kernel/m0hamed_rootkit.ko"], "../examples/rootfs/x86_linux", output="disasm") + try: + procfile_read_func_begin = ql.loader.load_address + 0x11e0 + procfile_read_func_end = ql.loader.load_address + 0x11fa + ql.run(begin=procfile_read_func_begin, end=procfile_read_func_end) + except UcError as e: + print(e) + sys.exit(-1) + del ql + + def test_demigod_hello_x8664(self): + ql = Qiling(["../examples/rootfs/x8664_linux/kernel/hello.ko"], "../examples/rootfs/x8664_linux", output="disasm") + try: + procfile_read_func_begin = ql.loader.load_address + 0x1064 + procfile_read_func_end = ql.loader.load_address + 0x107e + ql.run(begin=procfile_read_func_begin, end=procfile_read_func_end) + except UcError as e: + print(e) + sys.exit(-1) + del ql + + def test_demigod_hello_mips32(self): + ql = Qiling(["../examples/rootfs/mips32_linux/kernel/hello.ko"], "../examples/rootfs/mips32_linux", output="debug") + begin = ql.loader.load_address + 0x1060 + end = ql.loader.load_address + 0x1084 + ql.run(begin=begin, end=end) + del ql + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_elf_multithread.py b/tests/test_elf_multithread.py index 4315e8508..70f209a97 100644 --- a/tests/test_elf_multithread.py +++ b/tests/test_elf_multithread.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import sys, unittest, subprocess, string, random, os, logging +import sys, unittest, subprocess, string, random, os from unicorn import UcError, UC_ERR_READ_UNMAPPED, UC_ERR_FETCH_UNMAPPED @@ -15,7 +15,6 @@ from qiling.os.mapper import QlFsMappedObject from qiling.os.stat import Fstat -# Note: This test is disabled temporarily until multithread is done. class ELFTest(unittest.TestCase): def test_elf_linux_execve_x8664(self): @@ -30,231 +29,236 @@ def test_elf_linux_execve_x8664(self): del QL_TEST del ql - def test_tcp_elf_linux_x86(self): - def check_write(ql, write_fd, write_buf, write_count, *args, **kw): - try: - buf = ql.mem.read(write_buf, write_count) - buf = buf.decode() - if buf.startswith("server send()"): - ql.buf_out = buf - except: - pass - ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_tcp_test","20001"], "../examples/rootfs/x86_linux", multithread=True) - ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) - ql.run() - - self.assertEqual("server send() 14 return 14.\n", ql.buf_out) - - del ql - - def test_tcp_elf_linux_x8664(self): + def test_multithread_elf_linux_x86(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + nonlocal buf_out try: buf = ql.mem.read(write_buf, write_count) buf = buf.decode() - if buf.startswith("server send()"): - ql.buf_out = buf + buf_out = buf except: pass - ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_tcp_test","20002"], "../examples/rootfs/x8664_linux", multithread=True) - ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) - ql.run() - - self.assertEqual("server send() 14 return 14.\n", ql.buf_out) - - del ql - - - def test_tcp_elf_linux_arm(self): - def check_write(ql, write_fd, write_buf, write_count, *args, **kw): - try: - buf = ql.mem.read(write_buf, write_count) - buf = buf.decode() - if buf.startswith("server write()"): - ql.buf_out = buf - except: - pass - ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_tcp_test","20003"], "../examples/rootfs/arm_linux", multithread=True) + buf_out = None + ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_multithreading"], "../examples/rootfs/x86_linux", multithread=True, output="debug") ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() - self.assertEqual("server write() 14 return 14.\n", ql.buf_out) + self.assertTrue("thread 2 ret val is" in buf_out) del ql - - def test_tcp_elf_linux_arm64(self): + + def test_multithread_elf_linux_arm64(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + nonlocal buf_out try: buf = ql.mem.read(write_buf, write_count) buf = buf.decode() - if buf.startswith("server send()"): - ql.buf_out = buf + buf_out = buf except: pass - ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_tcp_test","20004"], "../examples/rootfs/arm64_linux", multithread=True) + buf_out = None + ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_multithreading"], "../examples/rootfs/arm64_linux", multithread=True, output="debug") ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() - self.assertEqual("server send() 14 return 14.\n", ql.buf_out) + self.assertTrue("thread 2 ret val is" in buf_out) del ql - def test_tcp_elf_linux_mips32el(self): - ql = Qiling(["../examples/rootfs/mips32el_linux/bin/mips32el_tcp_test","20005"], "../examples/rootfs/mips32el_linux", multithread=True) - ql.run() - del ql - - - def test_udp_elf_linux_x86(self): + def test_multithread_elf_linux_x8664(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + nonlocal buf_out try: buf = ql.mem.read(write_buf, write_count) buf = buf.decode() - if buf.startswith("server sendto()"): - ql.buf_out = buf + buf_out = buf except: pass - - ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_udp_test","20007"], "../examples/rootfs/x86_linux", multithread=True) + buf_out = None + ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_multithreading"], "../examples/rootfs/x8664_linux", multithread=True, profile= "profiles/append_test.ql") ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() - self.assertEqual("server sendto() 14 return 14.\n", ql.buf_out) + self.assertTrue("thread 2 ret val is" in buf_out) del ql - def test_udp_elf_linux_x8664(self): + def test_multithread_elf_linux_mips32el(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + nonlocal buf_out try: buf = ql.mem.read(write_buf, write_count) buf = buf.decode() - if buf.startswith("server sendto()"): - ql.buf_out = buf + buf_out = buf except: pass - - ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_udp_test","20008"], "../examples/rootfs/x8664_linux", multithread=True) + buf_out = None + ql = Qiling(["../examples/rootfs/mips32el_linux/bin/mips32el_multithreading"], "../examples/rootfs/mips32el_linux", multithread=True, output="debug") ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() - self.assertEqual("server sendto() 14 return 14.\n", ql.buf_out) + self.assertTrue("thread 2 ret val is" in buf_out) del ql - def test_udp_elf_linux_arm64(self): + + def test_multithread_elf_linux_arm(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + nonlocal buf_out try: buf = ql.mem.read(write_buf, write_count) buf = buf.decode() - if buf.startswith("server sendto()"): - ql.buf_out = buf + buf_out = buf except: pass - - ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_udp_test","20009"], "../examples/rootfs/arm64_linux", multithread=True) + buf_out = None + ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_multithreading"], "../examples/rootfs/arm_linux", multithread=True, output="debug") ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() - self.assertEqual("server sendto() 14 return 14.\n", ql.buf_out) - + self.assertTrue("thread 2 ret val is" in buf_out) + del ql - def test_multithread_elf_linux_x86(self): - def check_write(ql, write_fd, write_buf, write_count, *args, **kw): - try: - buf = ql.mem.read(write_buf, write_count) - buf = buf.decode() - if buf.startswith("thread 2 ret") == True: - ql.buf_out = buf - except: - pass - ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_multithreading"], "../examples/rootfs/x86_linux", multithread=True) - ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) - ql.run() + # def test_tcp_elf_linux_x86(self): + # def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + # try: + # buf = ql.mem.read(write_buf, write_count) + # buf = buf.decode() + # if buf.startswith("server send()"): + # ql.buf_out = buf + # except: + # pass + # ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_tcp_test","20001"], "../examples/rootfs/x86_linux", multithread=True) + # ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) + # ql.run() - self.assertEqual("thread 2 ret val is : 2\n", ql.buf_out) + # self.assertEqual("server send() 14 return 14.\n", ql.buf_out) + + # del ql + + + # def test_tcp_elf_linux_x8664(self): + # def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + # try: + # buf = ql.mem.read(write_buf, write_count) + # buf = buf.decode() + # if buf.startswith("server send()"): + # ql.buf_out = buf + # except: + # pass + # ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_tcp_test","20002"], "../examples/rootfs/x8664_linux", multithread=True) + # ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) + # ql.run() - del ql - - - def test_multithread_elf_linux_arm64(self): - def check_write(ql, write_fd, write_buf, write_count, *args, **kw): - buf = "" - try: - buf = ql.mem.read(write_buf, write_count) - buf = buf.decode() - if buf.startswith("thread 2 ret") == True: - ql.buf_out = buf - except: - pass + # self.assertEqual("server send() 14 return 14.\n", ql.buf_out) - ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_multithreading"], "../examples/rootfs/arm64_linux", multithread=True) - ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) - ql.run() + # del ql + + + # def test_tcp_elf_linux_arm(self): + # def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + # try: + # buf = ql.mem.read(write_buf, write_count) + # buf = buf.decode() + # if buf.startswith("server write()"): + # ql.buf_out = buf + # except: + # pass + # ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_tcp_test","20003"], "../examples/rootfs/arm_linux", multithread=True) + # ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) + # ql.run() - self.assertEqual("thread 2 ret val is : 2\n", ql.buf_out) + # self.assertEqual("server write() 14 return 14.\n", ql.buf_out) - del ql + # del ql + + + # def test_tcp_elf_linux_arm64(self): + # def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + # try: + # buf = ql.mem.read(write_buf, write_count) + # buf = buf.decode() + # if buf.startswith("server send()"): + # ql.buf_out = buf + # except: + # pass + # ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_tcp_test","20004"], "../examples/rootfs/arm64_linux", multithread=True) + # ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) + # ql.run() + + # self.assertEqual("server send() 14 return 14.\n", ql.buf_out) + + # del ql - def test_multithread_elf_linux_x8664(self): - def check_write(ql, write_fd, write_buf, write_count, *args, **kw): - try: - buf = ql.mem.read(write_buf, write_count) - buf = buf.decode() - if buf.startswith("thread 2 ret") == True: - ql.buf_out = buf - except: - pass + # def test_tcp_elf_linux_mips32el(self): + # ql = Qiling(["../examples/rootfs/mips32el_linux/bin/mips32el_tcp_test","20005"], "../examples/rootfs/mips32el_linux", multithread=True) + # ql.run() + # del ql - ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_multithreading"], "../examples/rootfs/x8664_linux", multithread=True, profile= "profiles/append_test.ql", log_split=True) - ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) - ql.run() - self.assertEqual("thread 2 ret val is : 2\n", ql.buf_out) + # def test_udp_elf_linux_x86(self): + # def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + # try: + # buf = ql.mem.read(write_buf, write_count) + # buf = buf.decode() + # if buf.startswith("server sendto()"): + # ql.buf_out = buf + # except: + # pass - del ql + # ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_udp_test","20007"], "../examples/rootfs/x86_linux", multithread=True) + # ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) + # ql.run() + # self.assertEqual("server sendto() 14 return 14.\n", ql.buf_out) - def test_multithread_elf_linux_mips32el(self): - def check_write(ql, write_fd, write_buf, write_count, *args, **kw): - try: - buf = ql.mem.read(write_buf, write_count) - buf = buf.decode() - if buf.startswith("thread 2 ret") == True: - ql.buf_out = buf - except: - pass + # del ql - ql = Qiling(["../examples/rootfs/mips32el_linux/bin/mips32el_multithreading"], "../examples/rootfs/mips32el_linux", multithread=True) - print("MIPS32EL") - ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) - ql.run() - del ql + # def test_udp_elf_linux_x8664(self): + # def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + # try: + # buf = ql.mem.read(write_buf, write_count) + # buf = buf.decode() + # if buf.startswith("server sendto()"): + # ql.buf_out = buf + # except: + # pass - def test_multithread_elf_linux_arm(self): - def check_write(ql, write_fd, write_buf, write_count, *args, **kw): - try: - buf = ql.mem.read(write_buf, write_count) - buf = buf.decode() - if buf.startswith("thread 2 ret") == True: - ql.buf_out = buf - except: - pass - - ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_multithreading"], "../examples/rootfs/arm_linux", multithread=True) - ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) - ql.run() + # ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_udp_test","20008"], "../examples/rootfs/x8664_linux", multithread=True) + # ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) + # ql.run() - self.assertEqual("thread 2 ret val is : 2\n", ql.buf_out) - - del ql + # self.assertEqual("server sendto() 14 return 14.\n", ql.buf_out) + + # del ql + + # def test_udp_elf_linux_arm64(self): + # def check_write(ql, write_fd, write_buf, write_count, *args, **kw): + # try: + # buf = ql.mem.read(write_buf, write_count) + # buf = buf.decode() + # if buf.startswith("server sendto()"): + # ql.buf_out = buf + # except: + # pass + + # ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_udp_test","20009"], "../examples/rootfs/arm64_linux", multithread=True) + # ql.set_syscall("write", check_write, QL_INTERCEPT.ENTER) + # ql.run() + + # self.assertEqual("server sendto() 14 return 14.\n", ql.buf_out) + + # del ql if __name__ == "__main__": unittest.main() + + diff --git a/tests/test_macho.py b/tests/test_macho.py index 74b9c0e07..74511f2b3 100644 --- a/tests/test_macho.py +++ b/tests/test_macho.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) - +# import sys, unittest sys.path.append("..") + from qiling import * from qiling.exception import * + class MACHOTest(unittest.TestCase): def test_macho_macos_x8664(self): ql = Qiling(["../examples/rootfs/x8664_macos/bin/x8664_hello_usercorn"], "../examples/rootfs/x8664_macos") diff --git a/tests/test_macho.sh b/tests/test_macho.sh index 3db684eef..2db6ee74e 100755 --- a/tests/test_macho.sh +++ b/tests/test_macho.sh @@ -2,6 +2,6 @@ # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# python3 ./test_macho_macos.py && python3 ./test_macho.py diff --git a/tests/test_macho_macos.py b/tests/test_macho_macos.py index 5fdc025bd..c39b33524 100644 --- a/tests/test_macho_macos.py +++ b/tests/test_macho_macos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os, sys, unittest @@ -63,7 +63,7 @@ def hook_stop(ql): try: ql.os.load_kext() except UcError as e: - print("[!] Load driver error: %s" % e) + print("Load driver error: %s" % e) sys.exit(-1) ql.os.ev_manager.add_process(1337, "agent") diff --git a/tests/test_pathutils.py b/tests/test_pathutils.py index 3a95e7092..b2a06499d 100644 --- a/tests/test_pathutils.py +++ b/tests/test_pathutils.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import unittest -import pathlib -import sys -sys.path.append("..") +import pathlib, sys, unittest +sys.path.append("..") from qiling import * from qiling.const import * from qiling.os.utils import PathUtils diff --git a/tests/test_pe.bat b/tests/test_pe.bat index 8f14817a1..96d828eda 100755 --- a/tests/test_pe.bat +++ b/tests/test_pe.bat @@ -3,4 +3,4 @@ REM REM Cross Platform and Multi Architecture Advanced Binary Emulation Framework REM Built on top of Unicorn emulator (www.unicorn-engine.org) -python test_pe.py && python test_windows_stdio.py && python test_peshellcode.py && python test_windows_debugger.py && python test_dos.py +python test_pe.py && python test_windows_stdio.py && python test_peshellcode.py && python test_windows_debugger.py && python test_dos.py && python test_pe_sys.py diff --git a/tests/test_pe.py b/tests/test_pe.py index 11f3c5d3b..8d1e7462f 100644 --- a/tests/test_pe.py +++ b/tests/test_pe.py @@ -1,23 +1,33 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os, random, sys, unittest, logging import string as st -from binascii import unhexlify from unicorn.x86_const import * -sys.path.insert(0, "..") +sys.path.append("..") from qiling import * from qiling.const import * from qiling.exception import * +from qiling.loader.pe import QlPeCache from qiling.os.windows.fncc import * from qiling.os.windows.utils import * from qiling.os.mapper import QlFsMappedObject from qiling.os.windows.dlls.kernel32.fileapi import _CreateFile +class TestOut: + def __init__(self): + self.output = {} + + def write(self, string): + key, value = string.split(b': ', 1) + assert key not in self.output + self.output[key] = value + return len(string) + class PETest(unittest.TestCase): @@ -30,7 +40,7 @@ def test_pe_win_x8664_hello(self): def test_pe_win_x86_hello(self): ql = Qiling(["../examples/rootfs/x86_windows/bin/x86_hello.exe"], "../examples/rootfs/x86_windows", - output="default", profile="profiles/append_test.ql", log_split=True) + output="default", profile="profiles/append_test.ql") ql.run() del ql @@ -103,7 +113,7 @@ def randomize_config_value(ql, key, subkey): new_name += random.choice(st.ascii_lowercase + st.ascii_uppercase) ql.os.profile[key][subkey] = new_name else: - raise QlErrorNotImplemented("[!] API not implemented") + raise QlErrorNotImplemented("API not implemented") ql = Qiling(["../examples/rootfs/x86_windows/bin/GandCrab502.bin"], "../examples/rootfs/x86_windows", output="debug", profile="profiles/windows_gandcrab_admin.ql") @@ -210,9 +220,9 @@ def test_pe_win_x86_wannacry(self): if 'QL_FAST_TEST' in os.environ: return def stop(ql): - logging.info("killerswtichfound") - logging.disable(level=logging.CRITICAL) - logging.info("No Print") + ql.log.info("killerswtichfound") + ql.log.setLevel(logging.CRITICAL) + ql.log.info("No Print") ql.emu_stop() ql = Qiling(["../examples/rootfs/x86_windows/bin/wannacry.bin"], "../examples/rootfs/x86_windows") @@ -220,6 +230,7 @@ def stop(ql): ql.run() del ql + def test_pe_win_x86_NtQueryInformationSystem(self): ql = Qiling( ["../examples/rootfs/x86_windows/bin/NtQuerySystemInformation.exe"], @@ -227,6 +238,7 @@ def test_pe_win_x86_NtQueryInformationSystem(self): ql.run() del ql + def test_pe_win_al_khaser(self): if 'QL_FAST_TEST' in os.environ: return @@ -236,9 +248,9 @@ def test_pe_win_al_khaser(self): def results(ql): if ql.reg.ebx == 1: - print("[=] BAD") + print("BAD") else: - print("[=] GOOD ") + print("GOOD ") ql.reg.eip = 0x402ee4 #ql.hook_address(results, 0x00402e66) @@ -299,6 +311,7 @@ def my_sandbox(path, rootfs): my_sandbox(["../examples/rootfs/x8664_windows/bin/x8664_hello.exe"], "../examples/rootfs/x8664_windows") + def test_pe_win_x86_argv(self): def check_print(ql, address, params): if ql.pointersize == 8: @@ -338,6 +351,7 @@ def check_print(ql, address, params): del self.target_txt del ql + def test_pe_win_x86_crackme(self): class StringBuffer: def __init__(self): @@ -388,49 +402,78 @@ def our_sandbox(path, rootfs): our_sandbox(["../examples/rootfs/x86_windows/bin/Easy_CrackMe.exe"], "../examples/rootfs/x86_windows") - def test_pe_win_x8664_driver(self): - # Compiled sample from https://github.com/microsoft/Windows-driver-samples/tree/master/general/ioctl/wdm/sys - ql = Qiling(["../examples/rootfs/x8664_windows/bin/sioctl.sys"], "../examples/rootfs/x8664_windows", libcache=True, stop_on_stackpointer=True) - - driver_object = ql.loader.driver_object + def test_pe_win_x86_cmdln(self): + ql = Qiling( + ["../examples/rootfs/x86_windows/bin/cmdln32.exe", 'arg1', 'arg2 with spaces'], + "../examples/rootfs/x86_windows") + ql.stdout = TestOut() + ql.run() + expected_string = b'\n' + expected_keys = [b'_acmdln', b'_wcmdln', b'__p__acmdln', b'__p__wcmdln', b'GetCommandLineA', b'GetCommandLineW'] + for key in expected_keys: + self.assertTrue(key in ql.stdout.output) + self.assertEqual(expected_string, ql.stdout.output[key]) + del ql - # Verify that these start zeroed out - majorfunctions = driver_object.MajorFunction - self.assertEqual(majorfunctions[IRP_MJ_CREATE], 0) - self.assertEqual(majorfunctions[IRP_MJ_CLOSE], 0) - self.assertEqual(majorfunctions[IRP_MJ_DEVICE_CONTROL], 0) - # And a DriverUnload - self.assertEqual(driver_object.DriverUnload, 0) - # Run the simulation + def test_pe_win_x8664_cmdln(self): + ql = Qiling( + ["../examples/rootfs/x8664_windows/bin/cmdln64.exe", 'arg1', 'arg2 with spaces'], + "../examples/rootfs/x8664_windows") + ql.stdout = TestOut() ql.run() + expected_string = b'\n' + expected_keys = [b'_acmdln', b'_wcmdln', b'GetCommandLineA', b'GetCommandLineW'] + for key in expected_keys: + self.assertTrue(key in ql.stdout.output) + self.assertEqual(expected_string, ql.stdout.output[key]) + del ql - # Check that we have some MajorFunctions - majorfunctions = driver_object.MajorFunction - self.assertNotEqual(majorfunctions[IRP_MJ_CREATE], 0) - self.assertNotEqual(majorfunctions[IRP_MJ_CLOSE], 0) - self.assertNotEqual(majorfunctions[IRP_MJ_DEVICE_CONTROL], 0) - # And a DriverUnload - self.assertNotEqual(driver_object.DriverUnload, 0) - - ql.os.clear_syscalls() + class RefreshCache(QlPeCache): + def restore(self, path, address): + # If the cache entry exists, delete it + fcache = self.create_filename(path, address) + if os.path.exists(fcache): + os.remove(fcache) + return super().restore(path, address) + + class TestCache(QlPeCache): + def __init__(self, testcase): + self.testcase = testcase + super().__init__() + + def restore(self, path, address): + entry = super().restore(path, address) + self.testcase.assertTrue(entry is not None) # Check that it loaded a cache entry + if path.endswith('msvcrt.dll'): + self.testcase.assertEqual(len(entry.cmdlines), 2) + else: + self.testcase.assertEqual(len(entry.cmdlines), 0) + self.testcase.assertIsInstance(entry.data, bytearray) + return entry - IOCTL_SIOCTL_METHOD_OUT_DIRECT = (40000, 0x901, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) - output_buffer_size = 0x1000 - in_buffer = b'Test input\0' - Status, Information_value, output_data = ql.os.ioctl((IOCTL_SIOCTL_METHOD_OUT_DIRECT, output_buffer_size, in_buffer)) + def save(self, path, address, entry): + self.testcase.assertFalse(true) # This should not be called! - expected_result = b'This String is from Device Driver !!!\x00' - self.assertEqual(Status, 0) - self.assertEqual(Information_value, len(expected_result)) - self.assertEqual(output_data, expected_result) - # TODO: - # - Call majorfunctions: - # - IRP_MJ_CREATE - # - IRP_MJ_CLOSE - # - Call DriverUnload + def test_pe_win_x8664_libcache(self): + # First force the cache to be recreated + ql = Qiling(["../examples/rootfs/x8664_windows/bin/cmdln64.exe", + 'arg1', 'arg2 with spaces'], + "../examples/rootfs/x8664_windows", + libcache=PETest.RefreshCache(), + output="default") + ql.run() + del ql + # Now run with a special cache that validates that the 'real' cache will load, + # and that the file is not written again + ql = Qiling(["../examples/rootfs/x8664_windows/bin/cmdln64.exe", + 'arg1', 'arg2 with spaces'], + "../examples/rootfs/x8664_windows", + libcache=PETest.TestCache(self), + output="default") + ql.run() del ql diff --git a/tests/test_pe_sality.py b/tests/test_pe_sality.py deleted file mode 100644 index 027404209..000000000 --- a/tests/test_pe_sality.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python3 -# -# Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) - -import os, random, sys, unittest, logging -import string as st -from binascii import unhexlify - -from unicorn.x86_const import * - -sys.path.insert(0, "..") -from qiling import * -from qiling.const import * -from qiling.exception import * -from qiling.os.windows.fncc import * -from qiling.os.windows.utils import * -from qiling.os.mapper import QlFsMappedObject -from qiling.os.windows.dlls.kernel32.fileapi import _CreateFile - - -class PETest(unittest.TestCase): - - def test_pe_win_x86_sality(self): - - def init_unseen_symbols(ql, address, name, ordinal, dll_name): - ql.loader.import_symbols[address] = {"name": name, "ordinal": ordinal, "dll": dll_name.split('.')[0] } - ql.loader.import_address_table[dll_name][name] = address - if ordinal != 0: - ql.loader.import_address_table[dll_name][ordinal] = address - - - # HANDLE CreateThread( - # LPSECURITY_ATTRIBUTES lpThreadAttributes, - # SIZE_T dwStackSize, - # LPTHREAD_START_ROUTINE lpStartAddress, - # __drv_aliasesMem LPVOID lpParameter, - # DWORD dwCreationFlags, - # LPDWORD lpThreadId - # ); - @winsdkapi(cc=STDCALL, dllname='kernel32_dll') - def sality_CreateThread(ql, address, params): - # set thread handle - return 1 - - # HANDLE CreateFileA( - # LPCSTR lpFileName, - # DWORD dwDesiredAccess, - # DWORD dwShareMode, - # LPSECURITY_ATTRIBUTES lpSecurityAttributes, - # DWORD dwCreationDisposition, - # DWORD dwFlagsAndAttributes, - # HANDLE hTemplateFile - # ); - @winsdkapi(cc=STDCALL, dllname='kernel32_dll', replace_params={ - "lpFileName": STRING, - "dwDesiredAccess": DWORD, - "dwShareMode": DWORD, - "lpSecurityAttributes": POINTER, - "dwCreationDisposition": DWORD, - "dwFlagsAndAttributes": DWORD, - "hTemplateFile": HANDLE - }) - def sality_CreateFileA(ql, address, params): - lpFileName = params["lpFileName"] - if lpFileName.startswith("\\\\.\\"): - if ql.amsint32_driver: - return 0x13371337 - else: - return (-1) - else: - ret = _CreateFile(ql, address, params, "CreateFileA") - return ret - - def _WriteFile(ql, address, params): - ret = 1 - hFile = params["hFile"] - lpBuffer = params["lpBuffer"] - nNumberOfBytesToWrite = params["nNumberOfBytesToWrite"] - lpNumberOfBytesWritten = params["lpNumberOfBytesWritten"] - lpOverlapped = params["lpOverlapped"] - if hFile == 0xfffffff5: - s = ql.mem.read(lpBuffer, nNumberOfBytesToWrite) - ql.os.stdout.write(s) - string_appearance(ql, s.decode()) - ql.mem.write(lpNumberOfBytesWritten, ql.pack(nNumberOfBytesToWrite)) - else: - f = ql.os.handle_manager.get(hFile) - if f is None: - # Invalid handle - ql.os.last_error = 0xffffffff - return 0 - else: - f = f.obj - buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite) - f.write(bytes(buffer)) - ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite)) - return ret - - @winsdkapi(cc=STDCALL, dllname='kernel32_dll', replace_params={ - "hFile": HANDLE, - "lpBuffer": POINTER, - "nNumberOfBytesToWrite": DWORD, - "lpNumberOfBytesWritten": POINTER, - "lpOverlapped": POINTER - }) - def sality_WriteFile(ql, address, params): - hFile = params["hFile"] - lpBuffer = params["lpBuffer"] - nNumberOfBytesToWrite = params["nNumberOfBytesToWrite"] - lpNumberOfBytesWritten = params["lpNumberOfBytesWritten"] - if hFile == 0x13371337: - buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite) - try: - r, nNumberOfBytesToWrite = ql.amsint32_driver.os.io_Write(buffer) - ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite)) - except Exception as e: - logging.exception("") - r = 1 - if r: - return 1 - else: - return 0 - else: - return _WriteFile(ql, address, params) - - - # BOOL StartServiceA( - # SC_HANDLE hService, - # DWORD dwNumServiceArgs, - # LPCSTR *lpServiceArgVectors - # ); - @winsdkapi(cc=STDCALL, dllname='kernel32_dll') - def sality_StartServiceA(ql, address, params): - try: - hService = params["hService"] - service_handle = ql.os.handle_manager.get(hService) - if service_handle.name == "amsint32": - if service_handle.name in ql.os.services: - service_path = ql.os.services[service_handle.name] - service_path = canonical_path(ql, service_path) - ql.amsint32_driver = Qiling([service_path], ql.rootfs, output="debug") - init_unseen_symbols(ql.amsint32_driver, ql.amsint32_driver.loader.dlls["ntoskrnl.exe"]+0xb7695, b"NtTerminateProcess", 0, "ntoskrnl.exe") - #ql.amsint32_driver.debugger= ":9999" - try: - ql.amsint32_driver.load() - return 1 - except UcError as e: - print("Load driver error: ", e) - return 0 - else: - return 0 - else: - return 1 - except Exception as e: - logging.exception("") - - - def hook_stop_address(ql): - print(" >>>> Stop address: 0x%08x" % ql.reg.arch_pc) - ql.emu_stop() - - - ql = Qiling(["../examples/rootfs/x86_windows/bin/sality.dll"], "../examples/rootfs/x86_windows", output="debug") - ql.libcache = True - - # for this module - ql.amsint32_driver = None - # emulate some Windows API - ql.set_api("CreateThread", sality_CreateThread) - ql.set_api("CreateFileA", sality_CreateFileA) - ql.set_api("WriteFile", sality_WriteFile) - ql.set_api("StartServiceA", sality_StartServiceA) - #init sality - ql.hook_address(hook_stop_address, 0x40EFFB) - ql.run() - # run driver thread - ql.os.set_function_args([0]) - ql.hook_address(hook_stop_address, 0x4055FA) - ql.run(0x4053B2) - logging.info("[+] test kill thread") - if ql.amsint32_driver: - ql.amsint32_driver.os.io_Write(struct.pack(">>> Third Stop address: 0x%08x" % ql.reg.arch_pc) + self.third_stop = True + ql.emu_stop() + + + def test_pe_win_x86_sality(self): + + def init_unseen_symbols(ql, address, name, ordinal, dll_name): + ql.loader.import_symbols[address] = {"name": name, "ordinal": ordinal, "dll": dll_name.split('.')[0] } + ql.loader.import_address_table[dll_name][name] = address + if ordinal != 0: + ql.loader.import_address_table[dll_name][ordinal] = address + + + # HANDLE CreateThread( + # LPSECURITY_ATTRIBUTES lpThreadAttributes, + # SIZE_T dwStackSize, + # LPTHREAD_START_ROUTINE lpStartAddress, + # __drv_aliasesMem LPVOID lpParameter, + # DWORD dwCreationFlags, + # LPDWORD lpThreadId + # ); + @winsdkapi(cc=STDCALL, dllname='kernel32_dll') + def hook_CreateThread(ql, address, params): + # set thread handle + return 1 + + # HANDLE CreateFileA( + # LPCSTR lpFileName, + # DWORD dwDesiredAccess, + # DWORD dwShareMode, + # LPSECURITY_ATTRIBUTES lpSecurityAttributes, + # DWORD dwCreationDisposition, + # DWORD dwFlagsAndAttributes, + # HANDLE hTemplateFile + # ); + @winsdkapi(cc=STDCALL, dllname='kernel32_dll', replace_params={ + "lpFileName": STRING, + "dwDesiredAccess": DWORD, + "dwShareMode": DWORD, + "lpSecurityAttributes": POINTER, + "dwCreationDisposition": DWORD, + "dwFlagsAndAttributes": DWORD, + "hTemplateFile": HANDLE + }) + def hook_CreateFileA(ql, address, params): + lpFileName = params["lpFileName"] + if lpFileName.startswith("\\\\.\\"): + if ql.amsint32_driver: + return 0x13371337 + else: + return (-1) + else: + ret = _CreateFile(ql, address, params, "CreateFileA") + + return ret + + def _WriteFile(ql, address, params): + ret = 1 + hFile = params["hFile"] + lpBuffer = params["lpBuffer"] + nNumberOfBytesToWrite = params["nNumberOfBytesToWrite"] + lpNumberOfBytesWritten = params["lpNumberOfBytesWritten"] + #lpOverlapped = params["lpOverlapped"] + + if hFile == 0xfffffff5: + s = ql.mem.read(lpBuffer, nNumberOfBytesToWrite) + ql.os.stdout.write(s) + string_appearance(ql, s.decode()) + ql.mem.write(lpNumberOfBytesWritten, ql.pack(nNumberOfBytesToWrite)) + else: + f = ql.os.handle_manager.get(hFile) + if f is None: + # Invalid handle + ql.os.last_error = 0xffffffff + return 0 + else: + f = f.obj + buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite) + f.write(bytes(buffer)) + ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite)) + return ret + + @winsdkapi(cc=STDCALL, dllname='kernel32_dll', replace_params={ + "hFile": HANDLE, + "lpBuffer": POINTER, + "nNumberOfBytesToWrite": DWORD, + "lpNumberOfBytesWritten": POINTER, + "lpOverlapped": POINTER + }) + def hook_WriteFile(ql, address, params): + hFile = params["hFile"] + lpBuffer = params["lpBuffer"] + nNumberOfBytesToWrite = params["nNumberOfBytesToWrite"] + lpNumberOfBytesWritten = params["lpNumberOfBytesWritten"] + + if hFile == 0x13371337: + buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite) + try: + r, nNumberOfBytesToWrite = ql.amsint32_driver.os.io_Write(buffer) + ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite)) + except Exception: + print("Error") + r = 1 + if r: + return 1 + else: + return 0 + else: + return _WriteFile(ql, address, params) + + + # BOOL StartServiceA( + # SC_HANDLE hService, + # DWORD dwNumServiceArgs, + # LPCSTR *lpServiceArgVectors + # ); + @winsdkapi(cc=STDCALL, dllname='advapi32_dll') + def hook_StartServiceA(ql, address, params): + # TODO: Still Fixing + hService = params["hService"] + service_handle = ql.os.handle_manager.get(hService) + if service_handle.name == "amsint32": + if service_handle.name in ql.os.services: + service_path = ql.os.services[service_handle.name] + service_path = ql.os.transform_to_real_path(service_path) + ql.amsint32_driver = Qiling([service_path], ql.rootfs, output="disasm") + init_unseen_symbols(ql.amsint32_driver, ql.amsint32_driver.loader.dlls["ntoskrnl.exe"]+0xb7695, b"NtTerminateProcess", 0, "ntoskrnl.exe") + print("load amsint32_driver") + + try: + ql.amsint32_driver.run() + return 1 + except UcError as e: + print("Load driver error: ", e) + return 0 + else: + return 0 + else: + return 1 + + + def hook_first_stop_address(ql): + print(" >>>> First Stop address: 0x%08x" % ql.reg.arch_pc) + ql.first_stop = True + ql.emu_stop() + + + def hook_second_stop_address(ql): + print(" >>>> Second Stop address: 0x%08x" % ql.reg.arch_pc) + ql.second_stop = True + ql.emu_stop() + + + ql = Qiling(["../examples/rootfs/x86_windows/bin/sality.dll"], "../examples/rootfs/x86_windows", output="debug") + ql.libcache = False + ql.first_stop = False + ql.second_stop = False + self.third_stop = False + # for this module + ql.amsint32_driver = None + # emulate some Windows API + ql.set_api("CreateThread", hook_CreateThread) + ql.set_api("CreateFileA", hook_CreateFileA) + ql.set_api("WriteFile", hook_WriteFile) + ql.set_api("StartServiceA", hook_StartServiceA) + #init sality + ql.hook_address(hook_first_stop_address, 0x40EFFB) + ql.run() + # run driver thread + ql.os.set_function_args([0]) + ql.hook_address(hook_second_stop_address, 0x4055FA) + ql.run(begin=0x4053B2) + print("test kill thread") + if ql.amsint32_driver: + ql.amsint32_driver.os.io_Write(ql.pack32(0xdeadbeef)) + + # TODO: Should stop at 0x10423, but for now just stop at 0x0001066a + stop_addr = 0x0001066a + ql.amsint32_driver.hook_address(self.hook_third_stop_address, stop_addr) + + ql.amsint32_driver.os.set_function_args([0]) + ql.amsint32_driver.run(begin=0x102D0) + + self.assertEqual(True, ql.first_stop) + self.assertEqual(True, ql.second_stop) + self.assertEqual(True, self.third_stop) + + + def test_pe_win_x8664_driver(self): + # Compiled sample from https://github.com/microsoft/Windows-driver-samples/tree/master/general/ioctl/wdm/sys + ql = Qiling(["../examples/rootfs/x8664_windows/bin/sioctl.sys"], "../examples/rootfs/x8664_windows", libcache=True, stop_on_stackpointer=True) + + driver_object = ql.loader.driver_object + + # Verify that these start zeroed out + majorfunctions = driver_object.MajorFunction + self.assertEqual(majorfunctions[IRP_MJ_CREATE], 0) + self.assertEqual(majorfunctions[IRP_MJ_CLOSE], 0) + self.assertEqual(majorfunctions[IRP_MJ_DEVICE_CONTROL], 0) + # And a DriverUnload + self.assertEqual(driver_object.DriverUnload, 0) + + # Run the simulation + ql.run() + + # Check that we have some MajorFunctions + majorfunctions = driver_object.MajorFunction + self.assertNotEqual(majorfunctions[IRP_MJ_CREATE], 0) + self.assertNotEqual(majorfunctions[IRP_MJ_CLOSE], 0) + self.assertNotEqual(majorfunctions[IRP_MJ_DEVICE_CONTROL], 0) + # And a DriverUnload + self.assertNotEqual(driver_object.DriverUnload, 0) + + ql.os.clear_syscalls() + + IOCTL_SIOCTL_METHOD_OUT_DIRECT = (40000, 0x901, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + output_buffer_size = 0x1000 + in_buffer = b'Test input\0' + Status, Information_value, output_data = ql.os.ioctl((IOCTL_SIOCTL_METHOD_OUT_DIRECT, output_buffer_size, in_buffer)) + + expected_result = b'This String is from Device Driver !!!\x00' + self.assertEqual(Status, 0) + self.assertEqual(Information_value, len(expected_result)) + self.assertEqual(output_data, expected_result) + + # TODO: + # - Call majorfunctions: + # - IRP_MJ_CREATE + # - IRP_MJ_CLOSE + # - Call DriverUnload + + del ql + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/tests/test_perf.py b/tests/test_perf.py index c76374a80..c25ac0616 100644 --- a/tests/test_perf.py +++ b/tests/test_perf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import cProfile import pstats diff --git a/tests/test_peshellcode.py b/tests/test_peshellcode.py index bc810ceca..551b3d8cf 100644 --- a/tests/test_peshellcode.py +++ b/tests/test_peshellcode.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) - -from binascii import unhexlify +# import sys, unittest -sys.path.insert(0, "..") +from binascii import unhexlify + +sys.path.append("..") from qiling import * from qiling.exception import * @@ -19,20 +19,44 @@ 'fc4881e4f0ffffffe8d0000000415141505251564831d265488b52603e488b52183e488b52203e488b72503e480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed5241513e488b52203e8b423c4801d03e8b80880000004885c0746f4801d0503e8b48183e448b40204901d0e35c48ffc93e418b34884801d64d31c94831c0ac41c1c90d4101c138e075f13e4c034c24084539d175d6583e448b40244901d0663e418b0c483e448b401c4901d03e418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a3e488b12e949ffffff5d49c7c1000000003e488d95fe0000003e4c8d850f0100004831c941ba45835607ffd54831c941baf0b5a256ffd548656c6c6f2c2066726f6d204d534621004d657373616765426f7800' ) +POINTER_TEST = unhexlify( + '1122334455667788' +) + class PEShellcodeTest(unittest.TestCase): def test_windowssc_x86(self): - ql = Qiling(shellcoder=X86_WIN, archtype="x86", ostype="windows", rootfs="../examples/rootfs/x86_windows", + ql = Qiling(code=X86_WIN, archtype="x86", ostype="windows", rootfs="../examples/rootfs/x86_windows", output="default") ql.run() del ql def test_windowssc_x64(self): - ql = Qiling(shellcoder=X8664_WIN, archtype="x8664", ostype="windows", rootfs="../examples/rootfs/x8664_windows", + ql = Qiling(code=X8664_WIN, archtype="x8664", ostype="windows", rootfs="../examples/rootfs/x8664_windows", output="debug") ql.run() del ql + def test_read_ptr32(self): + ql = Qiling(shellcoder=POINTER_TEST, archtype="x86", ostype="windows", rootfs="../examples/rootfs/x86_windows") + + addr = ql.loader.entry_point + self.assertEqual(0x2211, ql.mem.read_ptr(addr, 2)) + self.assertEqual(0x44332211, ql.mem.read_ptr(addr, 4)) + self.assertEqual(0x44332211, ql.mem.read_ptr(addr)) + self.assertEqual(0x8877665544332211, ql.mem.read_ptr(addr, 8)) + del ql + + def test_read_ptr64(self): + ql = Qiling(shellcoder=POINTER_TEST, archtype="x8664", ostype="windows", rootfs="../examples/rootfs/x86_windows") + + addr = ql.loader.entry_point + self.assertEqual(0x2211, ql.mem.read_ptr(addr, 2)) + self.assertEqual(0x44332211, ql.mem.read_ptr(addr, 4)) + self.assertEqual(0x8877665544332211, ql.mem.read_ptr(addr, 8)) + self.assertEqual(0x8877665544332211, ql.mem.read_ptr(addr)) + del ql + if __name__ == "__main__": unittest.main() diff --git a/tests/test_posix.py b/tests/test_posix.py index eb4134719..6eb390644 100644 --- a/tests/test_posix.py +++ b/tests/test_posix.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys,unittest + sys.path.append("..") from qiling import * from qiling.exception import * diff --git a/tests/test_qltool.py b/tests/test_qltool.py index 6ccaf4f40..152e5a0d2 100644 --- a/tests/test_qltool.py +++ b/tests/test_qltool.py @@ -1,13 +1,15 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys, subprocess, unittest + sys.path.append("..") from qiling import * from qiling.exception import * +import os class Qltool_Test(unittest.TestCase): def test_qltool_exec_args(self): @@ -18,13 +20,14 @@ def test_qltool_exec_args(self): raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) def test_qltool_shellcode(self): - create = [sys.executable, '../qltool', 'shellcode', '--os','linux','--arch', 'x86','--asm', '-f', '../examples/shellcodes/lin32_execve.asm'] + create = [sys.executable, '../qltool', 'code', '--os','linux','--arch', 'x86','--asm', '-f', '../examples/shellcodes/lin32_execve.asm'] try: subprocess.check_output(create,stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) def test_qltool_coverage(self): + os.makedirs("./log_test", exist_ok=True) create = [sys.executable, '../qltool', 'run', '-f','../examples/rootfs/x8664_efi/bin/TcgPlatformSetupPolicy','--rootfs', '../examples/rootfs/x8664_efi','--coverage-format', 'drcov', '--coverage-file', 'log_test/TcgPlatformSetupPolicy'] try: subprocess.check_output(create,stderr=subprocess.STDOUT) diff --git a/tests/test_shellcode.py b/tests/test_shellcode.py index 30407d19d..ded4a9d2f 100644 --- a/tests/test_shellcode.py +++ b/tests/test_shellcode.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# -import sys,unittest +import sys, unittest from binascii import unhexlify + sys.path.append("..") from qiling import * from qiling.exception import * @@ -23,65 +24,65 @@ class TestShellcode(unittest.TestCase): def test_linux_x86(self): print("Linux X86 32bit Shellcode") - ql = Qiling(shellcoder = X86_LIN, archtype = "x86", ostype = "linux", output = "off") + ql = Qiling(code = X86_LIN, archtype = "x86", ostype = "linux", output = "off") ql.run() def test_linux_x64(self): print("Linux X86 64bit Shellcode") - ql = Qiling(shellcoder = X8664_LIN, archtype = "x8664", ostype = "linux", output = "off") + ql = Qiling(code = X8664_LIN, archtype = "x8664", ostype = "linux", output = "off") ql.run() def test_linux_mips32(self): print("Linux MIPS 32bit EL Shellcode") - ql = Qiling(shellcoder = MIPS32EL_LIN, archtype = "mips", ostype = "linux", output = "off") + ql = Qiling(code = MIPS32EL_LIN, archtype = "mips", ostype = "linux", output = "off") ql.run() #This shellcode needs to be changed to something non-blocking def test_linux_arm(self): print("Linux ARM 32bit Shellcode") - ql = Qiling(shellcoder = ARM_LIN, archtype = "arm", ostype = "linux", output = "off") + ql = Qiling(code = ARM_LIN, archtype = "arm", ostype = "linux", output = "off") ql.run() def test_linux_arm64(self): print("Linux ARM 64bit Shellcode") - ql = Qiling(shellcoder = ARM64_LIN, archtype = "arm64", ostype = "linux", output = "off") + ql = Qiling(code = ARM64_LIN, archtype = "arm64", ostype = "linux", output = "off") ql.run() # #This shellcode needs to be changed to something simpler not requiring rootfs # def test_windows_x86(self): # print("Windows X86 32bit Shellcode") - # ql = Qiling(shellcoder = X86_WIN, archtype = "x86", ostype = "windows", rootfs="../examples/rootfs/x86_reactos", output="off") + # ql = Qiling(code = X86_WIN, archtype = "x86", ostype = "windows", rootfs="../examples/rootfs/x86_reactos", output="off") # ql.run() # #This shellcode needs to be changed to something simpler not requiring rootfs # def test_windows_x64(self): # print("\nWindows X8664 64bit Shellcode") - # ql = Qiling(shellcoder = X8664_WIN, archtype = "x8664", ostype = "windows", rootfs="../examples/rootfs/x86_reactos", output="off") + # ql = Qiling(code = X8664_WIN, archtype = "x8664", ostype = "windows", rootfs="../examples/rootfs/x86_reactos", output="off") # ql.run() #This shellcode needs to be changed to something simpler, listen is blocking #def test_freebsd_x64(self): # print("FreeBSD X86 64bit Shellcode") - # ql = Qiling(shellcoder = X8664_FBSD, archtype = "x8664", ostype = "freebsd", output = "off") + # ql = Qiling(code = X8664_FBSD, archtype = "x8664", ostype = "freebsd", output = "off") # ql.run() # def test_macos_x64(self): # print("macos X86 64bit Shellcode") - # ql = Qiling(shellcoder = X8664_macos, archtype = "x8664", ostype = "macos", output = "off") + # ql = Qiling(code = X8664_macos, archtype = "x8664", ostype = "macos", output = "off") # ql.run() # def test_invalid_os(self): # print("Testing Unknown OS") - # self.assertRaises(QlErrorOsType, Qiling, shellcoder = test, archtype = "arm64", ostype = "qilingos", output = "default" ) + # self.assertRaises(QlErrorOsType, Qiling, code = test, archtype = "arm64", ostype = "qilingos", output = "default" ) # def test_invalid_arch(self): # print("Testing Unknown Arch") - # self.assertRaises(QlErrorArch, Qiling, shellcoder = test, archtype = "qilingarch", ostype = "linux", output = "default" ) + # self.assertRaises(QlErrorArch, Qiling, code = test, archtype = "qilingarch", ostype = "linux", output = "default" ) # def test_invalid_output(self): # print("Testing Invalid output") - # self.assertRaises(QlErrorOutput, Qiling, shellcoder = test, archtype = "arm64", ostype = "linux", output = "blafooxyz" ) + # self.assertRaises(QlErrorOutput, Qiling, code = test, archtype = "arm64", ostype = "linux", output = "blafooxyz" ) if __name__ == "__main__": diff --git a/tests/test_uefi.py b/tests/test_uefi.py index f179c5bb3..ecfaa4712 100644 --- a/tests/test_uefi.py +++ b/tests/test_uefi.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import os, pickle, sys,unittest + sys.path.append("..") from qiling import Qiling from qiling.extensions.sanitizers.heap import QlSanitizedMemoryHeap diff --git a/tests/test_windows_debugger.py b/tests/test_windows_debugger.py index a1a65a427..7c9e0dd86 100644 --- a/tests/test_windows_debugger.py +++ b/tests/test_windows_debugger.py @@ -1,10 +1,12 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) +# import sys, subprocess, threading, unittest, socket, time + from binascii import unhexlify + sys.path.append("..") from qiling import * from qiling.exception import * diff --git a/tests/test_windows_stdio.py b/tests/test_windows_stdio.py index 57bcfbb2b..d60a698aa 100644 --- a/tests/test_windows_stdio.py +++ b/tests/test_windows_stdio.py @@ -1,15 +1,17 @@ #!/usr/bin/env python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# Built on top of Unicorn emulator (www.unicorn-engine.org) -from unicorn import * +# + +import sys +from unicorn import * from unicorn.x86_const import * -import sys sys.path.append("..") from qiling import * + class StringBuffer: def __init__(self): self.buffer = b'' diff --git a/tests/view_perf_results.py b/tests/view_perf_results.py index 7a78cf89b..2312114b9 100644 --- a/tests/view_perf_results.py +++ b/tests/view_perf_results.py @@ -1,7 +1,12 @@ -import sys -import os -import io +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +import io, os, sys + import pstats + from tkinter import * from tkinter import filedialog from tkinter import messagebox as msgbox @@ -39,7 +44,6 @@ def open_perf_file(self): self.scrollData.insert(INSERT, output_stream.getvalue()) self.highlight_pattern("qiling_highlight", "qiling") except Exception as e: - logging.exception("") msgbox.showerror("Error...", "Unable to load selected file") def highlight_pattern(self, tag, keyword):