Compare commits

...

28 Commits

Author SHA1 Message Date
mio
a1cbd67ae8
Fix off-by-one bug and add a unit test 2024-10-13 15:22:51 +08:00
mio
ff88348a51
Fix memory leak of g_array_free 2024-10-13 15:22:51 +08:00
Eli
429dbf3012
Revamp Python regression tests suite (#2022)
* Fix Python regression test suite (partial)

* Fix Python regression test suite

* Add a test for mapping at high addresses

* Add ctl tests
2024-10-13 15:22:50 +08:00
Eli
1742486319
Python3 bindings improvements (#2024)
* Allow Uc subclasses to use additional constructor args

* Add missing conext reg write batch prorotype

* Sort uc prototypes for better readability

* Redefine internal C API structures

* Add ctypes alises to improve readability

* Added documentation for ctl methods

* Added ctl tcg buffer size accessors

* Fix tcg buffer size return type
2024-10-13 15:22:50 +08:00
Daniel Roethlisberger
d0d0b697e6
python: Fix assertion failure on ctl_flush_tb() (#2023) 2024-10-13 15:22:50 +08:00
lazymio
dccba3f445
Format code 2024-10-13 15:22:49 +08:00
lazymio
b860629595
Fix segfault if tlb is flushed in the hooks 2024-10-13 15:22:49 +08:00
Eli
22591634ec
Support additional API on Python 3 bindings (#2016)
* Styling and commets fixes

* Add errno API support

* Improve OOP approach by adjusting the way reg types are selected

* Leverage new approach to deduplicate reg_read and reg_write code

* Adjust reg_read_batch

* Add support for reg_write_batch

* Adjust x86 MSR accessors

* Turn asserts into descriptive exceptions

* Improve comments and styling

* Fix ARM memcpy neon regression test

* Modify canonicals import

* Introduce ARM CP reg accessors
2024-10-13 15:22:48 +08:00
mio
28dcf7c29f
Implement UC_ARM64_REG_WSP 2024-10-13 15:22:48 +08:00
mio
c20d196316
Refine warning 2024-10-13 15:22:47 +08:00
mio
92bd175b4c
Revert previous break changes that return UC_ERR_ARG for non-existing registers
But print a warning instead
2024-10-13 15:22:47 +08:00
mio
be2b45bd49
Add .gdb_history 2024-10-13 15:22:47 +08:00
elicn
5b8c5885c8
Fix I0-7 registers write 2024-10-13 15:22:46 +08:00
elicn
3c84dd399a
Add read and write support for i386 ZMM0-31 2024-10-13 15:22:46 +08:00
elicn
f645fd087b
Add read and write support for i386 YMM8-31 2024-10-13 15:22:45 +08:00
elicn
69c5a707c8
Add read and write support for i386 XMM16-31 2024-10-13 15:22:45 +08:00
elicn
0626d4799a
Avoid updating CR3 when writing CR1 and CR2 2024-10-13 15:22:45 +08:00
elicn
2181f187ba
Add read and write cases for i386 CR8 2024-10-13 15:22:44 +08:00
Antelox
b978b09d66 Python bindings: Specify SYSTEM_VERSION_COMPAT=0 env var for macos-13 x86_64 runner as per cibuildwheel warning message 2024-10-11 18:22:33 +02:00
Antelox
bbb238ec43 Replaced macos-12 runner with macos-13 since it will be removed soon 2024-10-11 17:56:56 +02:00
Antelox
5a1c8ea8b5 Python bindings:
- Replaced old PyPI-publishing.yml workflow with brand-new one based on cibuildwheel
- Removed old building scripts
2024-10-11 10:14:21 +02:00
Antelox
f90429db72 Python bindings:
- Moved project package settings to the new TOML format
- Refactored setup.py to cleanup/improve the code and make it ready for cibuildwheel
- Updated README.md with the package long description part
- Removed setup.cfg since universal wheel building will be deprecated soon
2024-10-11 10:03:23 +02:00
Antelox
683b97497b Python bindings: Update CMakeLists.txt in order to set CMAKE_OSX_ARCHITECTURES var 2024-10-11 09:33:15 +02:00
Antelox
16fa9ed521 Update .gitignore to exclude PyCharm-related files/folders 2024-10-11 09:30:36 +02:00
Antelox
4e18047c8a Python bindings: Update MANIFEST.in with new paths 2024-10-11 09:29:41 +02:00
Antelox
c9a51ccf43 Python bindings: Remove .gdb_history 2024-10-11 09:27:43 +02:00
Antelox
8d21f262e0 Python bindings: Make the test scripts handy for pytest 2024-10-11 09:27:03 +02:00
mio
8b8126cb75 Fix compatibility of 3.8 2024-10-11 09:23:33 +02:00
110 changed files with 3515 additions and 2418 deletions

View File

@ -1,167 +0,0 @@
name: PyPI 📦 Distribution
on:
push:
paths-ignore:
- ".gitignore"
- "docs/**"
- "README"
- "CREDITS.TXT"
- "COPYING_GLIB"
- "COPYING.LGPL2"
- "AUTHORS.TXT"
- "CHANGELOG"
- "COPYING"
pull_request:
jobs:
build:
runs-on: ${{ matrix.config.os }}
name: ${{ matrix.config.name }}
strategy:
fail-fast: false
matrix:
config:
- {
os: windows-2019,
arch: x64,
python-ver: '3.8',
name: 'win_amd64'
}
- {
os: windows-2019,
arch: x32,
python-ver: '3.8',
name: 'win32'
}
- {
os: ubuntu-latest,
arch: x64,
python-ver: '3.8',
name: 'musllinux'
}
- {
os: ubuntu-latest,
arch: x64,
python-ver: '3.8',
name: 'manylinux2014_x86_64'
}
- {
os: ubuntu-latest,
arch: x32,
python-ver: '3.8',
name: 'manylinux2014_i686'
}
- {
os: ubuntu-latest,
arch: aarch64,
python-ver: '3.8',
name: 'manylinux2014_aarch64'
}
- {
os: ubuntu-latest,
arch: x64,
python-ver: '3.8',
name: 'sdist'
}
- {
os: macos-12,
arch: x86_64,
python-ver: '3.8',
name: 'macos_x86_64'
}
- {
os: macos-14,
arch: arm64,
python-ver: '3.10',
name: 'macos_arm64'
}
steps:
- uses: actions/checkout@v4
- name: '🛠️ Set up Python'
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.config.python-ver }}
- name: '🛠️ Add msbuild to PATH'
if: contains(matrix.config.name, 'win')
uses: microsoft/setup-msbuild@v2
with:
vs-version: '16.5'
- name: '🛠️ Win MSVC 32 dev cmd setup'
if: contains(matrix.config.name, 'win32')
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x86
- name: '🛠️ Win MSVC 64 dev cmd setup'
if: contains(matrix.config.name, 'win_amd64')
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: '🛠️ Win build dependencies'
if: contains(matrix.config.name, 'win')
shell: bash
run: |
choco install ninja cmake
- name: '🛠️ macOS dependencies'
if: contains(matrix.config.name, 'macos')
run: |
brew install ninja
- name: '🛠️ pip dependencies'
run: |
pip install --upgrade setuptools wheel
- name: '🚧 Build distribution'
shell: bash
run: |
if [ ${{ matrix.config.name }} == 'win32' ]; then
cd bindings/python && python setup.py build -p win32 sdist bdist_wheel -p win32
rm dist/*.tar.gz
elif [ ${{ matrix.config.name }} == 'manylinux2014_i686' ]; then
docker run --rm -v `pwd`/:/work dockcross/manylinux2014-x86 > ./dockcross
chmod +x ./dockcross
./dockcross bindings/python/build_wheel.sh
elif [ ${{ matrix.config.name }} == 'manylinux2014_aarch64' ]; then
docker run --rm -v `pwd`/:/work dockcross/manylinux2014-aarch64 > ./dockcross
chmod +x ./dockcross
./dockcross bindings/python/build_wheel.sh --plat-name manylinux2014_aarch64
elif [ ${{ matrix.config.name }} == 'manylinux2014_x86_64' ]; then
docker run --rm -v `pwd`/:/work dockcross/manylinux2014-x64 > ./dockcross
chmod +x ./dockcross
./dockcross bindings/python/build_wheel.sh
elif [ ${{ matrix.config.name }} == 'musllinux' ]; then
docker run --rm -v `pwd`:/work -w /work python:3.7-alpine sh /work/bindings/python/musl_wheel.sh
elif [ ${{ matrix.config.name }} == 'sdist' ]; then
cd bindings/python && python setup.py sdist
elif [ ${{ matrix.config.name }} == 'macos_arm64' ]; then
cd bindings/python && _PYTHON_HOST_PLATFORM="macosx-11.0-arm64" ARCHFLAGS="-arch arm64" python setup.py bdist_wheel
else
cd bindings/python && python setup.py bdist_wheel
fi
- name: '📤 Upload artifact'
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.config.name }}
path: ${{ github.workspace }}/bindings/python/dist/*
publish:
needs: [build]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
path: dist
- name: '📦 Publish distribution to PyPI'
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.pypi_pass }}

View File

@ -258,7 +258,7 @@ jobs:
matrix:
config:
- {
os: macos-12, # x64
os: macos-13, # x64
arch: x64,
python-arch: x64,
python-ver: '3.8',
@ -270,7 +270,7 @@ jobs:
generators: 'Ninja'
}
- {
os: macos-12,
os: macos-13,
arch: x64,
python-arch: x64,
python-ver: '3.8',
@ -306,7 +306,7 @@ jobs:
generators: 'Ninja'
}
- {
os: macos-12,
os: macos-13,
arch: x86_64,
python-arch: x86_64,
python-ver: '3.8',

View File

@ -0,0 +1,333 @@
name: Build wheels with cibuildwheel
on:
push:
paths-ignore:
- ".gitignore"
- "AUTHORS.TXT"
- "COPYING"
- "COPYING.LGPL2"
- "COPYING_GLIB"
- "CREDITS.TXT"
- "ChangeLog"
- "README.md"
- "docs/**"
pull_request:
jobs:
# job to be executed for every push - testing purpose
build_wheels_python38_only:
name: Building on ${{ matrix.os }} - ${{ matrix.arch }} ${{ matrix.cibw_build }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# NOTE: aarch64 builds are super slow due to QEMU emulation. Making this to parallelize and speed up workflow
# TODO: Check wheels for i686. Segfault error during pytest execution...
# - { os: ubuntu-latest, arch: i686 }
# x86_64 - manylinux
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-manylinux' }
# x86_64 - musllinux
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp38-musllinux' }
# aarch64 - manylinux
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-manylinux' }
# aarch64 - musllinux
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp38-musllinux' }
- { os: macos-13, arch: x86_64, cibw_build: '' }
- { os: macos-14, arch: arm64, cibw_build: '' }
- { os: windows-2019, arch: AMD64, cibw_build: '' }
- { os: windows-2019, arch: x86, cibw_build: '' }
steps:
- uses: actions/checkout@v4
- name: '🛠️ Add msbuild to PATH'
if: runner.os == 'Windows'
uses: microsoft/setup-msbuild@v2
with:
vs-version: '16.5'
- name: '🛠️ Win build dependencies'
if: runner.os == 'Windows'
shell: bash
run: |
choco install ninja cmake
- name: '🛠️ macOS dependencies'
if: runner.os == 'macOS'
run: |
brew install ninja
# https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64
- uses: actions/setup-python@v5
if: runner.os == 'macOS' && runner.arch == 'ARM64'
with:
python-version: 3.8
- name: '🛠️ Win MSVC 32 dev cmd setup'
if: runner.os == 'Windows' && matrix.arch == 'x86'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x86
- name: '🛠️ Win MSVC 64 dev cmd setup'
if: runner.os == 'Windows' && matrix.arch == 'AMD64'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: '🛠️ Set up QEMU'
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v3
- name: '🚧 cibuildwheel run - Linux'
if: matrix.os == 'ubuntu-latest'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_BUILD: ${{ matrix.cibw_build }}*
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
with:
package-dir: bindings/python
output-dir: wheelhouse
- name: '🚧 cibuildwheel run - Windows'
if: matrix.os == 'windows-2019'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_BUILD: 'cp38*'
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
with:
package-dir: bindings/python
output-dir: wheelhouse
- name: '🚧 cibuildwheel run - MacOS x86_84'
if: matrix.os == 'macos-13'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_BUILD: 'cp38*'
CIBW_ENVIRONMENT: SYSTEM_VERSION_COMPAT=0
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
with:
package-dir: bindings/python
output-dir: wheelhouse
- name: '🚧 cibuildwheel run - MacOS arm64'
if: matrix.os == 'macos-14'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_BUILD: 'cp38*'
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
# https://github.com/pypa/cibuildwheel/pull/1169
CIBW_TEST_SKIP: "cp38-macosx_*:arm64"
with:
package-dir: bindings/python
output-dir: wheelhouse
# we re-tag cp38 wheel (just an old one) with py2 tag. Hacky but it works...
- name: '🚧 Python 2.7 wheels re-tagging - Windows'
if: matrix.os == 'windows-2019'
run: |
python -m pip install -U pip wheel && Get-ChildItem -Path wheelhouse/ -Filter *cp38*.whl | Foreach-Object {
python -m wheel tags --python-tag='py2' --abi-tag=none $_.FullName
}
- name: '🚧 Python 2.7 wheels re-tagging - Non-Windows'
if: matrix.os != 'windows-2019'
env:
PIP_BREAK_SYSTEM_PACKAGES: 1
run: |
python3 -m pip install -U pip wheel && python3 -m wheel tags --python-tag='py2' --abi-tag=none wheelhouse/*cp38*.whl
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
# Job to be executed to build all wheels for all platforms/architectures/python versions only for tag release
build_wheels_all_versions:
name: Building on ${{ matrix.os }} - ${{ matrix.arch }} ${{ matrix.cibw_build }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# NOTE: aarch64 builds are super slow due to QEMU emulation. Making this to parallelize and speed up workflow
# TODO: Check wheels for i686. Segfault error during pytest execution...
# - { os: ubuntu-latest, arch: i686 }
# x86_64 - manylinux
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-manylinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-manylinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-manylinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-manylinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-manylinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-manylinux' }
# x86_64 - musllinux
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp37-musllinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp39-musllinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp310-musllinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp311-musllinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp312-musllinux' }
- { os: ubuntu-latest, arch: x86_64, cibw_build: 'cp313-musllinux' }
# aarch64 - manylinux
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp37-manylinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp39-manylinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp310-manylinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp311-manylinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp312-manylinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: 'cp313-manylinux' }
# aarch64 - musllinux
- { os: ubuntu-latest, arch: aarch64, cibw_build: '37-musllinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: '39-musllinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: '310-musllinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: '311-musllinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: '312-musllinux' }
- { os: ubuntu-latest, arch: aarch64, cibw_build: '313-musllinux' }
- { os: macos-13, arch: x86_64, cibw_build: '' }
- { os: macos-14, arch: arm64, cibw_build: '' }
- { os: windows-2019, arch: AMD64, cibw_build: '' }
- { os: windows-2019, arch: x86, cibw_build: '' }
if: startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/checkout@v4
- name: '🛠️ Add msbuild to PATH'
if: runner.os == 'Windows'
uses: microsoft/setup-msbuild@v2
with:
vs-version: '16.5'
- name: '🛠️ Win build dependencies'
if: runner.os == 'Windows'
shell: bash
run: |
choco install ninja cmake
- name: '🛠️ macOS dependencies'
if: runner.os == 'macOS'
run: |
brew install ninja
- name: '🛠️ Win MSVC 32 dev cmd setup'
if: runner.os == 'Windows' && matrix.arch == 'x86'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x86
- name: '🛠️ Win MSVC 64 dev cmd setup'
if: runner.os == 'Windows' && matrix.arch == 'AMD64'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: '🛠️ Set up QEMU'
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v3
- name: '🚧 cibuildwheel run - Linux'
if: matrix.os == 'ubuntu-latest'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_BUILD: ${{ matrix.cibw_build }}*
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
with:
package-dir: bindings/python
output-dir: wheelhouse
- name: '🚧 cibuildwheel run - Windows'
if: matrix.os == 'windows-2019'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_SKIP: '*36* *38*'
CIBW_BUILD: 'cp*'
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
with:
package-dir: bindings/python
output-dir: wheelhouse
- name: '🚧 cibuildwheel run - MacOS x86_84'
if: matrix.os == 'macos-13'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_SKIP: '*36* *38*'
CIBW_BUILD: 'cp*'
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
with:
package-dir: bindings/python
output-dir: wheelhouse
- name: '🚧 cibuildwheel run - MacOS arm64'
if: matrix.os == 'macos-14'
uses: pypa/cibuildwheel@v2.21.2
env:
CIBW_BUILD_FRONTEND: build
CIBW_SKIP: '*36* *37* *38*'
CIBW_BUILD: 'cp*'
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {package}/tests
with:
package-dir: bindings/python
output-dir: wheelhouse
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
make_sdist:
name: Make SDist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Optional, use if you use setuptools_scm
submodules: true # Optional, use if you have submodules
- name: Build SDist
run: |
cd bindings/python
python3 -m pip install -U pip build
python3 -m build --sdist
- uses: actions/upload-artifact@v4
with:
path: bindings/python/dist/*.tar.gz
publish:
needs: [ build_wheels_python38_only, build_wheels_all_versions, make_sdist ]
environment: pypi
permissions:
id-token: write
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
path: dist
- name: Show downloaded artifacts
run: ls -laR dist
- name: '📦 Publish distribution to PyPI'
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.pypi_pass }}

10
.gitignore vendored
View File

@ -37,7 +37,7 @@ unicorn_*.lib
unicorn_*.exp
unicorn_*.dll
.gdb_history
*.tgz
*.zip
*.pyc
@ -90,4 +90,10 @@ packages/
cmocka/
zig-cache/
zig-out/
.cache
.cache
##################
## PyCharm Project
##################
.idea/

View File

@ -30,6 +30,24 @@ if(APPLE AND NOT CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "/usr/bin/cc")
endif()
# Source: https://github.com/capstone-engine/capstone/blob/next/CMakeLists.txt
# If building for OSX it's best to allow CMake to handle building both architectures
if(APPLE)
# The cibuildwheel on Github Actions sets this env variable
# with the architecture flags it wants to build for.
if(DEFINED ENV{ARCHFLAGS})
if("$ENV{ARCHFLAGS}" STREQUAL "-arch arm64 -arch x86_64")
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
elseif("$ENV{ARCHFLAGS}" STREQUAL "-arch arm64")
set(CMAKE_OSX_ARCHITECTURES "arm64")
elseif("$ENV{ARCHFLAGS}" STREQUAL "-arch x86_64")
set(CMAKE_OSX_ARCHITECTURES "x86_64")
endif()
else()
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
endif()
endif()
# Detect if unicorn is compiled as the top-level project
set(PROJECT_IS_TOP_LEVEL OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)

View File

@ -1,107 +0,0 @@
q
rr
r
bt
b tcg.c:3075
r
p ts
p *ts
bt
q
b translate.c:4810
r
q
r
b translate.c:4810
frame level 5
frame level 7
q
r
bt
q
r
bt
q
r
bt
b translate.c:4810
r
p/x s->pc
n
n
n
n
n
n
n
n
p/x aflag
n
n
n
p/x dflag
n
n
n
n
p/x f
n
s
n
n
n
n
n
n
n
n
n
n
n
n
n
n
n
q
r
bt
q
r
bt
frame level 7
p/x op
p op
p *op
q
b tcg_optimize
r
b tcg_optimize_x86_64
r
n
s
s
s
q
r
exit
b tcg_dump_ops
r
b tcg_dump_ops
b tcg_dump_ops_x86_64
r
b tcg_optimize_x86_64
r
b tcg_optimize_x86_64
r
b tcg_optimize_x86_64
r
b print_log
r
b print_log
r
f fprintf
q
b write
r
bt
q

View File

@ -1,4 +1,9 @@
recursive-include src *
recursive-include prebuilt *
include LICENSE.TXT
include README.TXT
graft unicorn/lib
graft unicorn/include
global-include *.a
global-include *.so.2
global-include *.*lib
global-include *.dll
global-include *.h

View File

@ -1,3 +1,21 @@
# Unicorn
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
based on [QEMU](http://qemu.org).
Unicorn offers some unparalleled features:
- Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, PowerPC, RISCV, SPARC, S390X, TriCore and X86 (16, 32, 64-bit)
- Clean/simple/lightweight/intuitive architecture-neutral API
- Implemented in pure C language, with bindings for Crystal, Clojure, Visual Basic, Perl, Rust, Ruby, Python, Java, .NET, Go, Delphi/Free Pascal, Haskell, Pharo, and Lua.
- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed)
- High performance via Just-In-Time compilation
- Support for fine-grained instrumentation at various levels
- Thread-safety by design
- Distributed under free software license GPLv2
Further information is available at http://www.unicorn-engine.org
# Python Bindings for Unicorn
Originally written by Nguyen Anh Quynh, polished and redesigned by elicn, maintained by all community contributors.

View File

@ -1,15 +0,0 @@
#!/bin/bash
set -e -x
cd bindings/python
# Compile wheels
python3.7 setup.py bdist_wheel $@
cd dist
# We can't repair an aarch64 wheel on x64 hosts
# https://github.com/pypa/auditwheel/issues/244
if [[ ! "$*" =~ "aarch64" ]];then
auditwheel repair *.whl
mv -f wheelhouse/*.whl .
fi

View File

@ -1,9 +0,0 @@
#!/bin/sh
# TODO: use cibuildwheel
apk update
apk add gcc make cmake pkgconfig linux-headers git musl-dev patchelf
python3 -m pip install -U pip setuptools auditwheel
cd bindings/python && python3 setup.py bdist_wheel && auditwheel repair dist/*.whl && mv -f wheelhouse/*.whl ./dist/

View File

@ -0,0 +1,40 @@
[build-system]
requires = ["setuptools", "build", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "unicorn"
version = "2.1.1"
requires-python = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*, != 3.4.*, != 3.5.*, != 3.6.*"
authors = [
{ name = "Nguyen Anh Quynh", email = "quynh@gmail.com" },
]
description = "Unicorn CPU emulator engine"
readme = "README.md"
keywords = ["emulation", "qemu", "unicorn"]
classifiers = [
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
]
[project.urls]
Homepage = "http://www.unicorn-engine.org"
Repository = "https://github.com/unicorn-engine/unicorn"
"Bug Tracker" = "https://github.com/unicorn-engine/unicorn/issues"
Changelog = "https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog"
[project.optional-dependencies]
test = [
"pytest",
"pytest-cov",
]
[tool.setuptools.packages.find]
include = ["unicorn*"]

View File

@ -1,31 +0,0 @@
#!/bin/sh
python3 ./sample_arm.py
echo "=========================="
python3 ./sample_armeb.py
echo "=========================="
python3 ./sample_arm64.py
echo "=========================="
python3 ./sample_arm64eb.py
echo "=========================="
python3 ./sample_m68k.py
echo "=========================="
python3 ./sample_mips.py
echo "=========================="
python3 ./sample_ppc.py
echo "=========================="
python3 ./sample_riscv.py
echo "=========================="
python3 ./sample_s390x.py
echo "=========================="
python3 ./sample_sparc.py
echo "=========================="
python3 ./sample_tricore.py
echo "=========================="
python3 ./sample_x86.py
echo "=========================="
python3 ./shellcode.py
echo "=========================="
python3 ./sample_ctl.py
echo "=========================="
python3 ./sample_network_auditing.py

View File

@ -1,2 +0,0 @@
[bdist_wheel]
universal=1

View File

@ -1,29 +1,19 @@
#!/usr/bin/env python
# Python binding for Unicorn engine. Nguyen Anh Quynh <aquynh@gmail.com>
from __future__ import print_function
import glob
import logging
import os
import subprocess
import shutil
import sys
import platform
import setuptools
import shutil
import subprocess
import sys
from setuptools import setup
from sysconfig import get_platform
from setuptools.command.build import build
from setuptools.command.sdist import sdist
from setuptools.command.bdist_egg import bdist_egg
log = logging.getLogger(__name__)
SYSTEM = sys.platform
# sys.maxint is 2**31 - 1 on both 32 and 64 bit mingw
IS_64BITS = platform.architecture()[0] == '64bit'
# are we building from the repository or from a source distribution?
ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
LIBS_DIR = os.path.join(ROOT_DIR, 'unicorn', 'lib')
@ -32,29 +22,28 @@ SRC_DIR = os.path.join(ROOT_DIR, 'src')
UC_DIR = SRC_DIR if os.path.exists(SRC_DIR) else os.path.join(ROOT_DIR, '../..')
BUILD_DIR = os.path.join(UC_DIR, 'build_python')
VERSION = "2.1.1"
if SYSTEM == 'darwin':
if sys.platform == 'darwin':
LIBRARY_FILE = "libunicorn.2.dylib"
STATIC_LIBRARY_FILE = "libunicorn.a"
elif SYSTEM in ('win32', 'cygwin'):
elif sys.platform in ('win32', 'cygwin'):
LIBRARY_FILE = "unicorn.dll"
STATIC_LIBRARY_FILE = "unicorn.lib"
else:
LIBRARY_FILE = "libunicorn.so.2"
STATIC_LIBRARY_FILE = "libunicorn.a"
def clean_bins():
shutil.rmtree(LIBS_DIR, ignore_errors=True)
shutil.rmtree(HEADERS_DIR, ignore_errors=True)
def copy_sources():
"""Copy the C sources into the source directory.
"""
Copy the C sources into the source directory.
This rearranges the source files under the python distribution
directory.
"""
src = []
shutil.rmtree(SRC_DIR, ignore_errors=True)
os.mkdir(SRC_DIR)
@ -67,12 +56,12 @@ def copy_sources():
shutil.copytree(os.path.join(ROOT_DIR, '../../glib_compat'), os.path.join(SRC_DIR, 'glib_compat/'))
try:
# remove site-specific configuration file
# might not exist
# remove site-specific configuration file, might not exist
os.remove(os.path.join(SRC_DIR, 'qemu/config-host.mak'))
except OSError:
pass
src = []
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.[ch]")))
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.mk")))
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../cmake/*.cmake")))
@ -87,6 +76,7 @@ def copy_sources():
log.info("%s -> %s" % (filename, outpath))
shutil.copy(filename, outpath)
def build_libraries():
"""
Prepare the unicorn directory for a binary distribution or installation.
@ -94,7 +84,6 @@ def build_libraries():
Will use a src/ dir if one exists in the current directory, otherwise assumes it's in the repo
"""
cwd = os.getcwd()
clean_bins()
os.mkdir(HEADERS_DIR)
os.mkdir(LIBS_DIR)
@ -102,158 +91,84 @@ def build_libraries():
# copy public headers
shutil.copytree(os.path.join(UC_DIR, 'include', 'unicorn'), os.path.join(HEADERS_DIR, 'unicorn'))
# check if a prebuilt library exists
# if so, use it instead of building
# check if a prebuilt library exists and if so, use it instead of building
if os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE)):
shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE), LIBS_DIR)
if STATIC_LIBRARY_FILE is not None and os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE)):
shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE), LIBS_DIR)
return
# otherwise, build!!
os.chdir(UC_DIR)
# otherwise, build
if not os.path.exists(BUILD_DIR):
os.mkdir(BUILD_DIR)
try:
subprocess.check_call(['msbuild', '/help'])
except:
has_msbuild = False
else:
has_msbuild = True
has_msbuild = shutil.which('msbuild') is not None
conf = 'Debug' if int(os.getenv('DEBUG', 0)) else 'Release'
if has_msbuild and SYSTEM == 'win32':
if has_msbuild and sys.platform == 'win32':
plat = 'Win32' if platform.architecture()[0] == '32bit' else 'x64'
conf = 'Debug' if os.getenv('DEBUG', '') else 'Release'
if not os.path.exists(BUILD_DIR):
os.mkdir(BUILD_DIR)
subprocess.check_call(['cmake', '-B', BUILD_DIR, '-G', "Visual Studio 16 2019", "-A", plat, "-DCMAKE_BUILD_TYPE=" + conf])
subprocess.check_call(['msbuild', 'unicorn.sln', '-m', '-p:Platform=' + plat, '-p:Configuration=' + conf], cwd=BUILD_DIR)
subprocess.check_call(['cmake', '-B', BUILD_DIR, '-G', "Visual Studio 16 2019", "-A", plat,
"-DCMAKE_BUILD_TYPE=" + conf], cwd=UC_DIR)
subprocess.check_call(['msbuild', 'unicorn.sln', '-m', '-p:Platform=' + plat, '-p:Configuration=' + conf],
cwd=BUILD_DIR)
obj_dir = os.path.join(BUILD_DIR, conf)
shutil.copy(os.path.join(obj_dir, LIBRARY_FILE), LIBS_DIR)
shutil.copy(os.path.join(BUILD_DIR, STATIC_LIBRARY_FILE), LIBS_DIR)
else:
# platform description refs at https://docs.python.org/2/library/sys.html#sys.platform
if not os.path.exists(BUILD_DIR):
os.mkdir(BUILD_DIR)
conf = 'Debug' if os.getenv('DEBUG', '') else 'Release'
cmake_args = ["cmake", '-B', BUILD_DIR, '-S', UC_DIR, "-DCMAKE_BUILD_TYPE=" + conf]
if os.getenv("TRACE", ""):
if os.getenv("TRACE"):
cmake_args += ["-DUNICORN_TRACER=on"]
subprocess.check_call(cmake_args)
os.chdir(BUILD_DIR)
subprocess.check_call(cmake_args, cwd=UC_DIR)
threads = os.getenv("THREADS", "4")
subprocess.check_call(["cmake", "--build", ".", "-j" + threads])
shutil.copy(LIBRARY_FILE, LIBS_DIR)
shutil.copy(STATIC_LIBRARY_FILE, LIBS_DIR)
subprocess.check_call(["cmake", "--build", ".", "-j" + threads], cwd=BUILD_DIR)
os.chdir(cwd)
shutil.copy(os.path.join(BUILD_DIR, LIBRARY_FILE), LIBS_DIR)
shutil.copy(os.path.join(BUILD_DIR, STATIC_LIBRARY_FILE), LIBS_DIR)
class custom_sdist(sdist):
class CustomSDist(sdist):
def run(self):
clean_bins()
copy_sources()
return sdist.run(self)
return super().run()
class custom_build(build):
class CustomBuild(build):
def run(self):
if 'LIBUNICORN_PATH' in os.environ:
log.info("Skipping building C extensions since LIBUNICORN_PATH is set")
else:
log.info("Building C extensions")
build_libraries()
return build.run(self)
return super().run()
class custom_bdist_egg(bdist_egg):
class CustomBDistEgg(bdist_egg):
def run(self):
self.run_command('build')
return bdist_egg.run(self)
return super().run()
def dummy_src():
return []
cmdclass = {}
cmdclass['build'] = custom_build
cmdclass['sdist'] = custom_sdist
cmdclass['bdist_egg'] = custom_bdist_egg
if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv:
idx = sys.argv.index('bdist_wheel') + 1
sys.argv.insert(idx, '--plat-name')
name = get_platform()
if 'linux' in name:
# linux_* platform tags are disallowed because the python ecosystem is fubar
# linux builds should be built in the centos 5 vm for maximum compatibility
# see https://github.com/pypa/manylinux
# see also https://github.com/angr/angr-dev/blob/master/bdist.sh
sys.argv.insert(idx + 1, 'manylinux1_' + platform.machine())
elif 'mingw' in name:
if IS_64BITS:
sys.argv.insert(idx + 1, 'win_amd64')
else:
sys.argv.insert(idx + 1, 'win32')
else:
# https://www.python.org/dev/peps/pep-0425/
sys.argv.insert(idx + 1, name.replace('.', '_').replace('-', '_'))
cmdclass = {'build': CustomBuild, 'sdist': CustomSDist, 'bdist_egg': CustomBDistEgg}
try:
from setuptools.command.develop import develop
class custom_develop(develop):
class CustomDevelop(develop):
def run(self):
log.info("Building C extensions")
build_libraries()
return develop.run(self)
return super().run()
cmdclass['develop'] = custom_develop
cmdclass['develop'] = CustomDevelop
except ImportError:
print("Proper 'develop' support unavailable.")
def join_all(src, files):
return tuple(os.path.join(src, f) for f in files)
long_desc = '''
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
based on [QEMU](http://qemu.org).
Unicorn offers some unparalleled features:
- Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, PowerPC, RISCV, SPARC, S390X, TriCore and X86 (16, 32, 64-bit)
- Clean/simple/lightweight/intuitive architecture-neutral API
- Implemented in pure C language, with bindings for Crystal, Clojure, Visual Basic, Perl, Rust, Ruby, Python, Java, .NET, Go, Delphi/Free Pascal, Haskell, Pharo, and Lua.
- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed)
- High performance via Just-In-Time compilation
- Support for fine-grained instrumentation at various levels
- Thread-safety by design
- Distributed under free software license GPLv2
Further information is available at http://www.unicorn-engine.org
'''
setup(
provides=['unicorn'],
packages=setuptools.find_packages(include=["unicorn", "unicorn.*"]),
name='unicorn',
version=VERSION,
author='Nguyen Anh Quynh',
author_email='aquynh@gmail.com',
description='Unicorn CPU emulator engine',
long_description=long_desc,
long_description_content_type="text/markdown",
url='http://www.unicorn-engine.org',
classifiers=[
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
],
requires=['ctypes'],
cmdclass=cmdclass,
zip_safe=False,
include_package_data=True,
is_pure=False,
package_data={
'unicorn': ['unicorn/py.typed', 'lib/*', 'include/unicorn/*']
}
has_ext_modules=lambda: True, # It's not a Pure Python wheel
)

View File

@ -6,22 +6,21 @@ from __future__ import print_function
from unicorn import *
from unicorn.arm_const import *
# code to be emulated
ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = b"\x83\xb0" # sub sp, #0xc
ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = b"\x83\xb0" # sub sp, #0xc
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM
@ -41,8 +40,8 @@ def test_arm():
mu.reg_write(UC_ARM_REG_R0, 0x1234)
mu.reg_write(UC_ARM_REG_R2, 0x6789)
mu.reg_write(UC_ARM_REG_R3, 0x3333)
mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) #All application flags turned on
mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) # All application flags turned on
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
@ -57,8 +56,8 @@ def test_arm():
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
print(">>> R0 = 0x%x" %r0)
print(">>> R1 = 0x%x" %r1)
print(">>> R0 = 0x%x" % r0)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
@ -93,11 +92,12 @@ def test_thumb():
print(">>> Emulation done. Below is the CPU context")
sp = mu.reg_read(UC_ARM_REG_SP)
print(">>> SP = 0x%x" %sp)
print(">>> SP = 0x%x" % sp)
except UcError as e:
print("ERROR: %s" % e)
def test_read_sctlr():
print("Read SCTLR")
try:
@ -118,6 +118,7 @@ def test_read_sctlr():
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_arm()
print("=" * 26)

View File

@ -6,25 +6,24 @@ from __future__ import print_function
from unicorn import *
from unicorn.arm64_const import *
# code to be emulated
ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13]
ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13]
# MSR code
ARM64_MRS_CODE = b"\x62\xd0\x3b\xd5" # mrs x2, tpidrro_el0
ARM64_MRS_CODE = b"\x62\xd0\x3b\xd5" # mrs x2, tpidrro_el0
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM64
@ -61,7 +60,7 @@ def test_arm64():
x11 = mu.reg_read(UC_ARM64_REG_X11)
x13 = mu.reg_read(UC_ARM64_REG_X13)
x15 = mu.reg_read(UC_ARM64_REG_X15)
print(">>> X15 = 0x%x" %x15)
print(">>> X15 = 0x%x" % x15)
except UcError as e:
print("ERROR: %s" % e)
@ -85,6 +84,7 @@ def test_arm64_read_sctlr():
except UcError as e:
print("ERROR: %s" % e)
def test_arm64_hook_mrs():
def _hook_mrs(uc, reg, cp_reg, _):
print(f">>> Hook MRS instruction: reg = 0x{reg:x}(UC_ARM64_REG_X2) cp_reg = {cp_reg}")
@ -116,6 +116,7 @@ def test_arm64_hook_mrs():
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_arm64()
print("=" * 26)

View File

@ -7,22 +7,21 @@ from __future__ import print_function
from unicorn import *
from unicorn.arm64_const import *
# code to be emulated
ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13]
ARM64_CODE = b"\xab\x05\x00\xb8\xaf\x05\x40\x38" # str x11, [x13]; ldrb x15, [x13]
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM64
@ -59,7 +58,7 @@ def test_arm64():
x11 = mu.reg_read(UC_ARM64_REG_X11)
x13 = mu.reg_read(UC_ARM64_REG_X13)
x15 = mu.reg_read(UC_ARM64_REG_X15)
print(">>> X15 = 0x%x" %x15)
print(">>> X15 = 0x%x" % x15)
except UcError as e:
print("ERROR: %s" % e)

View File

@ -5,22 +5,21 @@ from __future__ import print_function
from unicorn import *
from unicorn.arm_const import *
# code to be emulated
ARM_CODE = b"\xe3\xa0\x00\x37\xe0\x42\x10\x03" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = b"\xb0\x83" # sub sp, #0xc
ARM_CODE = b"\xe3\xa0\x00\x37\xe0\x42\x10\x03" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = b"\xb0\x83" # sub sp, #0xc
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM
@ -40,8 +39,8 @@ def test_arm():
mu.reg_write(UC_ARM_REG_R0, 0x1234)
mu.reg_write(UC_ARM_REG_R2, 0x6789)
mu.reg_write(UC_ARM_REG_R3, 0x3333)
mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) #All application flags turned on
mu.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) # All application flags turned on
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block)
@ -56,8 +55,8 @@ def test_arm():
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
print(">>> R0 = 0x%x" %r0)
print(">>> R1 = 0x%x" %r1)
print(">>> R0 = 0x%x" % r0)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
@ -92,7 +91,7 @@ def test_thumb():
print(">>> Emulation done. Below is the CPU context")
sp = mu.reg_read(UC_ARM_REG_SP)
print(">>> SP = 0x%x" %sp)
print(">>> SP = 0x%x" % sp)
except UcError as e:
print("ERROR: %s" % e)

View File

@ -6,6 +6,7 @@ from unicorn import *
from unicorn.x86_const import *
from datetime import datetime
def test_uc_ctl_read():
uc = Uc(UC_ARCH_X86, UC_MODE_32)
@ -21,6 +22,7 @@ def test_uc_ctl_read():
print(f">>> arch={arch} mode={mode} page size={page_size} timeout={timeout}")
def time_emulation(uc, start, end):
n = datetime.now()
@ -28,6 +30,7 @@ def time_emulation(uc, start, end):
return (datetime.now() - n).total_seconds() * 1e6
def test_uc_ctl_tb_cache():
# Initialize emulator in X86-32bit mode
uc = Uc(UC_ARCH_X86, UC_MODE_32)
@ -35,14 +38,14 @@ def test_uc_ctl_tb_cache():
# Fill the code buffer with NOP.
code = b"\x90" * 8 * 512
print("Controling the TB cache in a finer granularity by uc_ctl.")
uc.mem_map(addr, 0x10000)
# Write our code to the memory.
uc.mem_write(addr, code)
# Do emulation without any cache.
standard = time_emulation(uc, addr, addr + len(code))
@ -62,12 +65,15 @@ def test_uc_ctl_tb_cache():
print(f">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}")
def trace_new_edge(uc, cur, prev, data):
print(f">>> Getting a new edge from {hex(prev.pc + prev.size - 1)} to {hex(cur.pc)}")
def trace_tcg_sub(uc, address, arg1, arg2, size, data):
print(f">>> Get a tcg sub opcode at {hex(address)} with args: {arg1} and {arg2}")
def test_uc_ctl_exits():
uc = Uc(UC_ARCH_X86, UC_MODE_32)
addr = 0x1000
@ -114,9 +120,10 @@ def test_uc_ctl_exits():
print(f">>> eax = {hex(eax)} and ebx = {hex(ebx)} after the first emulation")
if __name__ == "__main__":
test_uc_ctl_read()
print("="*32)
print("=" * 32)
test_uc_ctl_tb_cache()
print("="*32)
test_uc_ctl_exits()
print("=" * 32)
test_uc_ctl_exits()

View File

@ -6,21 +6,20 @@ from __future__ import print_function
from unicorn import *
from unicorn.m68k_const import *
# code to be emulated
M68K_CODE = b"\x76\xed" # movq #-19, %d3
M68K_CODE = b"\x76\xed" # movq #-19, %d3
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test ARM

View File

@ -6,23 +6,22 @@ from __future__ import print_function
from unicorn import *
from unicorn.mips_const import *
# code to be emulated
MIPS_CODE_EB = b"\x34\x21\x34\x56" # ori $at, $at, 0x3456;
MIPS_CODE_EL = b"\x56\x34\x21\x34" # ori $at, $at, 0x3456;
MIPS_CODE_EB = b"\x34\x21\x34\x56" # ori $at, $at, 0x3456;
MIPS_CODE_EL = b"\x56\x34\x21\x34" # ori $at, $at, 0x3456;
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test MIPS EB
@ -54,7 +53,7 @@ def test_mips_eb():
print(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1)
print(">>> R1 = 0x%x" %r1)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
@ -89,7 +88,7 @@ def test_mips_el():
print(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1)
print(">>> R1 = 0x%x" %r1)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)

View File

@ -3,10 +3,11 @@
# Nguyen Tan Cong <shenlongbk@gmail.com>
from __future__ import print_function
from unicorn import *
from unicorn.x86_const import *
import pytest
import struct
import uuid
from unicorn import *
from unicorn.x86_const import *
SIZE_REG = 4
SOCKETCALL_MAX_ARGS = 3
@ -360,6 +361,7 @@ def hook_intr(uc, intno, user_data):
print_sockcall(msg)
@pytest.mark.parametrize("code", [X86_SEND_ETCPASSWD, X86_BIND_TCP, X86_REVERSE_TCP, X86_REVERSE_TCP_2])
# Test X86 32 bit
def test_i386(code):
global fd_chains

View File

@ -1,26 +1,24 @@
#!/usr/bin/env python
# Sample code for PPC of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
#
from __future__ import print_function
from unicorn import *
from unicorn.ppc_const import *
# code to be emulated
PPC_CODE = b"\x7F\x46\x1A\x14" # add r26, r6, r3
PPC_CODE = b"\x7F\x46\x1A\x14" # add r26, r6, r3
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test PPC
@ -62,4 +60,3 @@ def test_ppc():
if __name__ == '__main__':
test_ppc()

View File

@ -1,12 +1,10 @@
#!/usr/bin/env python
# Sample code for RISCV of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
#
from __future__ import print_function
from unicorn import *
from unicorn.riscv_const import *
'''
$ cstool riscv64 1305100093850502
0 13 05 10 00 addi a0, zero, 1
@ -15,17 +13,17 @@ $ cstool riscv64 1305100093850502
RISCV_CODE = b"\x13\x05\x10\x00\x93\x85\x05\x02"
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test RISCV
@ -59,8 +57,8 @@ def test_riscv():
a0 = mu.reg_read(UC_RISCV_REG_A0)
a1 = mu.reg_read(UC_RISCV_REG_A1)
print(">>> A0 = 0x%x" %a0)
print(">>> A1 = 0x%x" %a1)
print(">>> A0 = 0x%x" % a0)
print(">>> A1 = 0x%x" % a1)
except UcError as e:
print("ERROR: %s" % e)
@ -68,4 +66,3 @@ def test_riscv():
if __name__ == '__main__':
test_riscv()

View File

@ -8,17 +8,17 @@ from unicorn.s390x_const import *
S390X_CODE = b"\x18\x23"
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test RISCV
@ -60,4 +60,3 @@ def test_s390x():
if __name__ == '__main__':
test_s390x()

View File

@ -4,11 +4,12 @@
# KaiJern Lau <kj@theshepherdlab.io>
from __future__ import print_function
import pytest
from unicorn import *
from unicorn.x86_const import *
# Original shellcode from this example.
#X86_CODE32 = b"\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9\xb0\x04\xb3\x01\x59\xb2\x05\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x68\x65\x6c\x6c\x6f"
# X86_CODE32 = b"\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9\xb0\x04\xb3\x01\x59\xb2\x05\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x68\x65\x6c\x6c\x6f"
# Linux/x86 execve /bin/sh shellcode 23 bytes, from http://shell-storm.org/shellcode/files/shellcode-827.php
# 0: 31 c0 xor eax,eax
@ -44,20 +45,22 @@ X86_CODE64 = b"\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\
# memory address where emulation starts
ADDRESS = 0x1000000
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# read this instruction code from memory
tmp = uc.mem_read(address, size)
print("*** PC = %x *** :" %(address), end="")
print("*** PC = %x *** :" % (address), end="")
for i in tmp:
print(" %02x" %i, end="")
print(" %02x" % i, end="")
print("")
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
def read_string(uc, address):
ret = ""
@ -70,21 +73,22 @@ def read_string(uc, address):
read_bytes += 1
return ret
# callback for tracing Linux interrupt
def hook_intr(uc, intno, user_data):
# only handle Linux syscall
if intno != 0x80:
print("got interrupt %x ???" %intno)
print("got interrupt %x ???" % intno)
uc.emu_stop()
return
eax = uc.reg_read(UC_X86_REG_EAX)
eip = uc.reg_read(UC_X86_REG_EIP)
if eax == 1: # sys_exit
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(eip, intno, eax))
if eax == 1: # sys_exit
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" % (eip, intno, eax))
uc.emu_stop()
elif eax == 4: # sys_write
elif eax == 4: # sys_write
# ECX = buffer address
ecx = uc.reg_read(UC_X86_REG_ECX)
# EDX = buffer size
@ -92,49 +96,53 @@ def hook_intr(uc, intno, user_data):
try:
buf = uc.mem_read(ecx, edx)
print(">>> 0x%x: interrupt 0x%x, SYS_WRITE. buffer = 0x%x, size = %u, content = " \
%(eip, intno, ecx, edx), end="")
% (eip, intno, ecx, edx), end="")
for i in buf:
print("%c" %i, end="")
print("%c" % i, end="")
print("")
except UcError as e:
print(">>> 0x%x: interrupt 0x%x, SYS_WRITE. buffer = 0x%x, size = %u, content = <unknown>\n" \
%(eip, intno, ecx, edx))
elif eax == 11: # sys_write
% (eip, intno, ecx, edx))
elif eax == 11: # sys_write
ebx = uc.reg_read(UC_X86_REG_EBX)
filename = read_string(uc, ebx)
print(">>> SYS_EXECV filename=%s" % filename)
else:
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(eip, intno, eax))
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" % (eip, intno, eax))
def hook_syscall32(mu, user_data):
eax = mu.reg_read(UC_X86_REG_EAX)
print(">>> got SYSCALL with EAX = 0x%x" %(eax))
print(">>> got SYSCALL with EAX = 0x%x" % (eax))
mu.emu_stop()
def hook_syscall64(mu, user_data):
rax = mu.reg_read(UC_X86_REG_RAX)
rdi = mu.reg_read(UC_X86_REG_RDI)
print(">>> got SYSCALL with RAX = %d" %(rax))
if rax == 59: #sys_execve
print(">>> got SYSCALL with RAX = %d" % (rax))
if rax == 59: # sys_execve
filename = read_string(mu, rdi)
print(">>> SYS_EXECV filename=%s" % filename)
else:
rip = mu.reg_read(UC_X86_REG_RIP)
print(">>> Syscall Found at 0x%x: , RAX = 0x%x" %(rip, rax))
print(">>> Syscall Found at 0x%x: , RAX = 0x%x" % (rip, rax))
mu.emu_stop()
@pytest.mark.parametrize("mode,code",
[(UC_MODE_32, X86_CODE32_SELF), (UC_MODE_32, X86_CODE32), (UC_MODE_64, X86_CODE64)])
# Test X86 32 bit
def test_i386(mode, code):
if mode == UC_MODE_32:
print("Emulate x86_32 code")
elif mode == UC_MODE_64:
print("Emulate x86_64 code")
try:
# Initialize emulator
mu = Uc(UC_ARCH_X86, mode)
@ -171,9 +179,10 @@ def test_i386(mode, code):
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_i386(UC_MODE_32, X86_CODE32_SELF)
print("=" * 20)
test_i386(UC_MODE_32, X86_CODE32)
print("=" * 20)
test_i386(UC_MODE_64, X86_CODE64)
test_i386(UC_MODE_64, X86_CODE64)

View File

@ -6,21 +6,20 @@ from __future__ import print_function
from unicorn import *
from unicorn.sparc_const import *
# code to be emulated
SPARC_CODE = b"\x86\x00\x40\x02" # add %g1, %g2, %g3;
SPARC_CODE = b"\x86\x00\x40\x02" # add %g1, %g2, %g3;
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test SPARC
@ -28,7 +27,7 @@ def test_sparc():
print("Emulate SPARC code")
try:
# Initialize emulator in SPARC EB mode
mu = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN)
mu = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32 | UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
@ -54,7 +53,7 @@ def test_sparc():
print(">>> Emulation done. Below is the CPU context")
g3 = mu.reg_read(UC_SPARC_REG_G3)
print(">>> G3 = 0x%x" %g3)
print(">>> G3 = 0x%x" % g3)
except UcError as e:
print("ERROR: %s" % e)

View File

@ -1,26 +1,29 @@
#!/usr/bin/env python
'''
"""
Created for Unicorn Engine by Eric Poole <eric.poole@aptiv.com>, 2022
Copyright 2022 Aptiv
'''
"""
from __future__ import print_function
from unicorn import *
from unicorn.tricore_const import *
# code to be emulated
TRICORE_CODE = b"\x82\x11\xbb\x00\x00\x08" # mov d0, #0x1; mov.u d0, #0x8000
TRICORE_CODE = b"\x82\x11\xbb\x00\x00\x08" # mov d0, #0x1; mov.u d0, #0x8000
# memory address where emulation starts
ADDRESS = 0x10000
ADDRESS = 0x10000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# Test TriCore
def test_tricore():
@ -48,10 +51,11 @@ def test_tricore():
print(">>> Emulation done. Below is the CPU context")
r0 = mu.reg_read(UC_TRICORE_REG_D0)
print(">>> D0 = 0x%x" %r0)
print(">>> D0 = 0x%x" % r0)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_tricore()

View File

@ -2,21 +2,21 @@
# Sample code for X86 of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
from __future__ import print_function
import pickle
from unicorn import *
from unicorn.x86_const import *
import pickle
X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1
X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop
X86_CODE32_JUMP = b"\xeb\x02\x90\x90\x90\x90\x90\x90" # jmp 4; nop; nop; nop; nop; nop; nop
X86_CODE32_JMP_INVALID = b"\xe9\xe9\xee\xee\xee\x41\x4a" # JMP outside; INC ecx; DEC edx
X86_CODE32_MEM_READ = b"\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx
X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx
X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1
X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop
X86_CODE32_JUMP = b"\xeb\x02\x90\x90\x90\x90\x90\x90" # jmp 4; nop; nop; nop; nop; nop; nop
X86_CODE32_JMP_INVALID = b"\xe9\xe9\xee\xee\xee\x41\x4a" # JMP outside; INC ecx; DEC edx
X86_CODE32_MEM_READ = b"\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx
X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx
X86_CODE64 = b"\x41\xBC\x3B\xB0\x28\x2A\x49\x0F\xC9\x90\x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9\x4D\x29\xF4\x49\x81\xC9\xF6\x8A\xC6\x53\x4D\x87\xED\x48\x0F\xAD\xD2\x49\xF7\xD4\x48\xF7\xE1\x4D\x19\xC5\x4D\x89\xC5\x48\xF7\xD6\x41\xB8\x4F\x8D\x6B\x59\x4D\x87\xD0\x68\x6A\x1E\x09\x3C\x59"
X86_CODE32_INOUT = b"\x41\xE4\x3F\x4a\xE6\x46\x43" # INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx
X86_CODE64_SYSCALL = b'\x0f\x05' # SYSCALL
X86_CODE16 = b'\x00\x00' # add byte ptr [bx + si], al
X86_MMIO_CODE = b"\x89\x0d\x04\x00\x02\x00\x8b\x0d\x04\x00\x02\x00" # mov [0x20004], ecx; mov ecx, [0x20004]
X86_CODE32_INOUT = b"\x41\xE4\x3F\x4a\xE6\x46\x43" # INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx
X86_CODE64_SYSCALL = b'\x0f\x05' # SYSCALL
X86_CODE16 = b'\x00\x00' # add byte ptr [bx + si], al
X86_MMIO_CODE = b"\x89\x0d\x04\x00\x02\x00\x8b\x0d\x04\x00\x02\x00" # mov [0x20004], ecx; mov ecx, [0x20004]
# memory address where emulation starts
ADDRESS = 0x1000000
@ -24,28 +24,29 @@ ADDRESS = 0x1000000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
eflags = uc.reg_read(UC_X86_REG_EFLAGS)
print(">>> --- EFLAGS is 0x%x" %eflags)
print(">>> --- EFLAGS is 0x%x" % eflags)
def hook_code64(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
rip = uc.reg_read(UC_X86_REG_RIP)
print(">>> RIP is 0x%x" %rip)
print(">>> RIP is 0x%x" % rip)
# callback for tracing invalid memory access (READ or WRITE)
def hook_mem_invalid(uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE_UNMAPPED:
print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
%(address, size, value))
% (address, size, value))
# map this memory in with 2MB in size
uc.mem_map(0xaaaa0000, 2 * 1024*1024)
uc.mem_map(0xaaaa0000, 2 * 1024 * 1024)
# return True to indicate we want to continue emulation
return True
else:
@ -57,16 +58,16 @@ def hook_mem_invalid(uc, access, address, size, value, user_data):
def hook_mem_access(uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE:
print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
%(address, size, value))
else: # READ
% (address, size, value))
else: # READ
print(">>> Memory is being READ at 0x%x, data size = %u" \
%(address, size))
% (address, size))
# callback for IN instruction
def hook_in(uc, port, size, user_data):
eip = uc.reg_read(UC_X86_REG_EIP)
print("--- reading from port 0x%x, size: %u, address: 0x%x" %(port, size, eip))
print("--- reading from port 0x%x, size: %u, address: 0x%x" % (port, size, eip))
if size == 1:
# read 1 byte to AL
return 0xf1
@ -83,7 +84,7 @@ def hook_in(uc, port, size, user_data):
# callback for OUT instruction
def hook_out(uc, port, size, value, user_data):
eip = uc.reg_read(UC_X86_REG_EIP)
print("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x" %(port, size, value, eip))
print("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x" % (port, size, value, eip))
# confirm that value is indeed the value of AL/AX/EAX
v = 0
@ -97,7 +98,7 @@ def hook_out(uc, port, size, value, user_data):
# read 4 bytes in EAX
v = uc.reg_read(UC_X86_REG_EAX)
print("--- register value = 0x%x" %v)
print("--- register value = 0x%x" % v)
# Test X86 32 bit
@ -134,15 +135,15 @@ def test_i386():
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
r_xmm0 = mu.reg_read(UC_X86_REG_XMM0)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
print(">>> XMM0 = 0x%.32x" %r_xmm0)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
print(">>> XMM0 = 0x%.32x" % r_xmm0)
# read from memory
tmp = mu.mem_read(ADDRESS, 4)
print(">>> Read 4 bytes from [0x%x] = 0x" %(ADDRESS), end="")
print(">>> Read 4 bytes from [0x%x] = 0x" % (ADDRESS), end="")
for i in reversed(tmp):
print("%x" %(i), end="")
print("%x" % (i), end="")
print("")
except UcError as e:
@ -179,14 +180,14 @@ def test_i386_map_ptr():
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
# read from memory
tmp = mu.mem_read(ADDRESS, 4)
print(">>> Read 4 bytes from [0x%x] = 0x" %(ADDRESS), end="")
print(">>> Read 4 bytes from [0x%x] = 0x" % (ADDRESS), end="")
for i in reversed(tmp):
print("%x" %(i), end="")
print("%x" % (i), end="")
print("")
except UcError as e:
@ -226,12 +227,13 @@ def test_i386_invalid_mem_read():
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
except UcError as e:
print("ERROR: %s" % e)
def test_i386_jump():
print("Emulate i386 code with jump")
try:
@ -298,22 +300,22 @@ def test_i386_invalid_mem_write():
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
# read from memory
print(">>> Read 4 bytes from [0x%x] = 0x" %(0xaaaaaaaa), end="")
print(">>> Read 4 bytes from [0x%x] = 0x" % (0xaaaaaaaa), end="")
tmp = mu.mem_read(0xaaaaaaaa, 4)
for i in reversed(tmp):
if i != 0:
print("%x" %i, end="")
print("%x" % i, end="")
print("")
try:
tmp = mu.mem_read(0xffffffaa, 4)
print(">>> Read 4 bytes from [0x%x] = 0x" %(0xffffffaa), end="")
print(">>> Read 4 bytes from [0x%x] = 0x" % (0xffffffaa), end="")
for i in reversed(tmp):
print("%x" %i, end="")
print("%x" % i, end="")
print("")
except UcError as e:
@ -322,6 +324,7 @@ def test_i386_invalid_mem_write():
except UcError as e:
print("ERROR: %s" % e)
def test_i386_jump_invalid():
print("Emulate i386 code that jumps to invalid memory")
try:
@ -347,18 +350,19 @@ def test_i386_jump_invalid():
try:
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_JMP_INVALID))
except UcError as e:
print("Failed on uc_emu_start() with error returned 8: %s" %e)
print("Failed on uc_emu_start() with error returned 8: %s" % e)
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
except UcError as e:
print("ERROR %s" % e)
def test_i386_loop():
print("Emulate i386 code that loop forever")
try:
@ -375,18 +379,19 @@ def test_i386_loop():
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_LOOP), timeout=2*UC_SECOND_SCALE)
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_LOOP), timeout=2 * UC_SECOND_SCALE)
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> EDX = 0x%x" % r_edx)
except UcError as e:
print("ERROR: %s" % e)
# Test X86 32 bit with IN/OUT instruction
def test_i386_inout():
print("Emulate i386 code with IN/OUT instructions")
@ -422,8 +427,8 @@ def test_i386_inout():
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_eax = mu.reg_read(UC_X86_REG_EAX)
print(">>> EAX = 0x%x" %r_eax)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EAX = 0x%x" % r_eax)
print(">>> ECX = 0x%x" % r_ecx)
except UcError as e:
print("ERROR: %s" % e)
@ -446,10 +451,10 @@ def test_i386_context_save():
mu.reg_write(UC_X86_REG_EAX, 1)
print(">>> Running emulation for the first time")
mu.emu_start(address, address+1)
mu.emu_start(address, address + 1)
print(">>> Emulation done. Below is the CPU context")
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))
print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX)))
print(">>> Saving CPU context")
saved_context = mu.context_save()
@ -457,9 +462,9 @@ def test_i386_context_save():
pickled_saved_context = pickle.dumps(saved_context)
print(">>> Running emulation for the second time")
mu.emu_start(address, address+1)
mu.emu_start(address, address + 1)
print(">>> Emulation done. Below is the CPU context")
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))
print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX)))
print(">>> Unpickling CPU context")
saved_context = pickle.loads(pickled_saved_context)
@ -469,11 +474,12 @@ def test_i386_context_save():
print(">>> CPU context restored. Below is the CPU context")
mu.context_restore(saved_context)
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))
print(">>> EAX = 0x%x" % (mu.reg_read(UC_X86_REG_EAX)))
except UcError as e:
print("ERROR: %s" % e)
def test_x86_64():
print("Emulate x86_64 code")
try:
@ -509,13 +515,13 @@ def test_x86_64():
mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions in range [ADDRESS, ADDRESS+20]
mu.hook_add(UC_HOOK_CODE, hook_code64, None, ADDRESS, ADDRESS+20)
mu.hook_add(UC_HOOK_CODE, hook_code64, None, ADDRESS, ADDRESS + 20)
# tracing all memory READ & WRITE access
mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access)
mu.hook_add(UC_HOOK_MEM_READ, hook_mem_access)
# actually you can also use READ_WRITE to trace all memory access
#mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access)
# mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access)
try:
# emulate machine code in infinite time
@ -541,20 +547,20 @@ def test_x86_64():
r14 = mu.reg_read(UC_X86_REG_R14)
r15 = mu.reg_read(UC_X86_REG_R15)
print(">>> RAX = 0x%x" %rax)
print(">>> RBX = 0x%x" %rbx)
print(">>> RCX = 0x%x" %rcx)
print(">>> RDX = 0x%x" %rdx)
print(">>> RSI = 0x%x" %rsi)
print(">>> RDI = 0x%x" %rdi)
print(">>> R8 = 0x%x" %r8)
print(">>> R9 = 0x%x" %r9)
print(">>> R10 = 0x%x" %r10)
print(">>> R11 = 0x%x" %r11)
print(">>> R12 = 0x%x" %r12)
print(">>> R13 = 0x%x" %r13)
print(">>> R14 = 0x%x" %r14)
print(">>> R15 = 0x%x" %r15)
print(">>> RAX = 0x%x" % rax)
print(">>> RBX = 0x%x" % rbx)
print(">>> RCX = 0x%x" % rcx)
print(">>> RDX = 0x%x" % rdx)
print(">>> RSI = 0x%x" % rsi)
print(">>> RDI = 0x%x" % rdi)
print(">>> R8 = 0x%x" % r8)
print(">>> R9 = 0x%x" % r9)
print(">>> R10 = 0x%x" % r10)
print(">>> R11 = 0x%x" % r11)
print(">>> R12 = 0x%x" % r12)
print(">>> R13 = 0x%x" % r13)
print(">>> R14 = 0x%x" % r14)
print(">>> R15 = 0x%x" % r15)
except UcError as e:
@ -626,19 +632,22 @@ def test_x86_16():
print(">>> Emulation done. Below is the CPU context")
tmp = mu.mem_read(11, 1)
print(">>> Read 1 bytes from [0x%x] = 0x%x" %(11, tmp[0]))
print(">>> Read 1 bytes from [0x%x] = 0x%x" % (11, tmp[0]))
except UcError as e:
print("ERROR: %s" % e)
def mmio_read_cb(uc, offset, size, data):
print(f">>> Read IO memory at offset {hex(offset)} with {hex(size)} bytes and return 0x19260817")
return 0x19260817
def mmio_write_cb(uc, offset, size, value, data):
print(f">>> Write value {hex(value)} to IO memory at offset {hex(offset)} with {hex(size)} bytes")
def test_i386_mmio():
print("Test i386 IO memory")
try:
@ -664,6 +673,7 @@ def test_i386_mmio():
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_x86_16()
test_i386()

View File

@ -2,7 +2,7 @@
"""
# @author elicn
from typing import Any, Tuple
from typing import Tuple, Type
import ctypes
@ -10,7 +10,7 @@ import ctypes
from unicorn import arm_const as const
# newly introduced unicorn imports
from ..unicorn import Uc
from ..unicorn import Uc, check_maxbits
from .types import UcTupledReg, UcReg128
ARMCPReg = Tuple[int, int, int, int, int, int, int, int]
@ -40,46 +40,71 @@ class UcAArch32(Uc):
"""Unicorn subclass for ARM architecture.
"""
REG_RANGE_CP = (const.UC_ARM_REG_CP_REG,)
REG_RANGE_Q = range(const.UC_ARM_REG_Q0, const.UC_ARM_REG_Q15 + 1)
@staticmethod
def __select_reg_class(reg_id: int):
"""Select class for special architectural registers.
@classmethod
def _select_reg_class(cls, reg_id: int) -> Type:
"""Select the appropriate class for the specified architectural register.
"""
reg_class = (
(UcAArch32.REG_RANGE_Q, UcReg128),
(UcAArch32.REG_RANGE_CP, UcRegCP),
(UcAArch32.REG_RANGE_Q, UcReg128)
)
return next((cls for rng, cls in reg_class if reg_id in rng), None)
return next((c for rng, c in reg_class if reg_id in rng), cls._DEFAULT_REGTYPE)
def reg_read(self, reg_id: int, aux: Any = None):
# select register class for special cases
reg_cls = UcAArch32.__select_reg_class(reg_id)
# to learn more about accessing aarch32 coprocessor registers, refer to:
# https://developer.arm.com/documentation/ddi0601/latest/AArch32-Registers
if reg_cls is None:
if reg_id == const.UC_ARM_REG_CP_REG:
return self._reg_read(reg_id, UcRegCP, *aux)
def cpr_read(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool) -> int:
"""Read a coprocessor register value.
else:
# fallback to default reading method
return super().reg_read(reg_id, aux)
Args:
coproc : coprocessor to access, value varies between 0 and 15
opc1 : opcode 1, value varies between 0 and 7
crn : coprocessor register to access (CRn), value varies between 0 and 15
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
opc2 : opcode 2, value varies between 0 and 7
el : the exception level the coprocessor register belongs to, value varies between 0 and 3
is_64 : indicates whether this is a 64-bit register
return self._reg_read(reg_id, reg_cls)
Returns: value of coprocessor register
"""
def reg_write(self, reg_id: int, value) -> None:
# select register class for special cases
reg_cls = UcAArch32.__select_reg_class(reg_id)
assert check_maxbits(coproc, 4)
assert check_maxbits(opc1, 3)
assert check_maxbits(crn, 4)
assert check_maxbits(crm, 4)
assert check_maxbits(opc2, 3)
assert check_maxbits(el, 2) # note that unicorn currently supports only EL0 and EL1
if reg_cls is None:
if reg_id == const.UC_ARM_REG_CP_REG:
self._reg_write(reg_id, UcRegCP, value)
return self.reg_read(const.UC_ARM_REG_CP_REG, (coproc, int(is_64), el, crn, crm, opc1, opc2))
else:
# fallback to default writing method
super().reg_write(reg_id, value)
def cpr_write(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool, value: int) -> None:
"""Write a coprocessor register value.
Args:
coproc : coprocessor to access, value varies between 0 and 15
opc1 : opcode 1, value varies between 0 and 7
crn : coprocessor register to access (CRn), value varies between 0 and 15
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
opc2 : opcode 2, value varies between 0 and 7
el : the exception level the coprocessor register belongs to, value varies between 0 and 3
is_64 : indicates whether this is a 64-bit register
value : value to write
"""
assert check_maxbits(coproc, 4)
assert check_maxbits(opc1, 3)
assert check_maxbits(crn, 4)
assert check_maxbits(crm, 4)
assert check_maxbits(opc2, 3)
assert check_maxbits(el, 2) # note that unicorn currently supports only EL0 and EL1
self.reg_write(const.UC_ARM_REG_CP_REG, (coproc, int(is_64), el, crn, crm, opc1, opc2, value))
else:
self._reg_write(reg_id, reg_cls, value)
__all__ = ['UcRegCP', 'UcAArch32']

View File

@ -2,7 +2,7 @@
"""
# @author elicn
from typing import Any, Callable, NamedTuple, Tuple
from typing import Any, Callable, NamedTuple, Tuple, Type
import ctypes
@ -11,7 +11,7 @@ from unicorn import arm64_const as const
from unicorn.unicorn_const import UC_ERR_ARG, UC_HOOK_INSN
# newly introduced unicorn imports
from ..unicorn import Uc, UcError, uccallback
from ..unicorn import Uc, UcError, uccallback, check_maxbits
from .types import uc_engine, UcTupledReg, UcReg128
ARM64CPReg = Tuple[int, int, int, int, int, int]
@ -41,6 +41,8 @@ class UcAArch64(Uc):
"""Unicorn subclass for ARM64 architecture.
"""
REG_RANGE_CP = (const.UC_ARM64_REG_CP_REG,)
REG_RANGE_Q = range(const.UC_ARM64_REG_Q0, const.UC_ARM64_REG_Q31 + 1)
REG_RANGE_V = range(const.UC_ARM64_REG_V0, const.UC_ARM64_REG_V31 + 1)
@ -85,45 +87,62 @@ class UcAArch64(Uc):
return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn)
@staticmethod
def __select_reg_class(reg_id: int):
"""Select class for special architectural registers.
@classmethod
def _select_reg_class(cls, reg_id: int) -> Type:
"""Select the appropriate class for the specified architectural register.
"""
reg_class = (
(UcAArch64.REG_RANGE_Q, UcReg128),
(UcAArch64.REG_RANGE_V, UcReg128)
(UcAArch64.REG_RANGE_CP, UcRegCP64),
(UcAArch64.REG_RANGE_Q, UcReg128),
(UcAArch64.REG_RANGE_V, UcReg128)
)
return next((cls for rng, cls in reg_class if reg_id in rng), None)
return next((c for rng, c in reg_class if reg_id in rng), cls._DEFAULT_REGTYPE)
def reg_read(self, reg_id: int, aux: Any = None):
# select register class for special cases
reg_cls = UcAArch64.__select_reg_class(reg_id)
# to learn more about accessing aarch64 coprocessor registers, refer to:
# https://developer.arm.com/documentation/ddi0601/latest/AArch64-Registers
if reg_cls is None:
if reg_id == const.UC_ARM64_REG_CP_REG:
return self._reg_read(reg_id, UcRegCP64, *aux)
def cpr_read(self, op0: int, op1: int, crn: int, crm: int, op2: int) -> int:
"""Read a coprocessor register value.
else:
# fallback to default reading method
return super().reg_read(reg_id, aux)
Args:
op0 : opcode 0, value varies between 0 and 3
op1 : opcode 1, value varies between 0 and 7
crn : coprocessor register to access (CRn), value varies between 0 and 15
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
op2 : opcode 2, value varies between 0 and 7
return self._reg_read(reg_id, reg_cls)
Returns: value of coprocessor register
"""
def reg_write(self, reg_id: int, value) -> None:
# select register class for special cases
reg_cls = UcAArch64.__select_reg_class(reg_id)
assert check_maxbits(op0, 2)
assert check_maxbits(op1, 3)
assert check_maxbits(crn, 4)
assert check_maxbits(crm, 4)
assert check_maxbits(op2, 3)
if reg_cls is None:
if reg_id == const.UC_ARM64_REG_CP_REG:
self._reg_write(reg_id, UcRegCP64, value)
return self.reg_read(const.UC_ARM64_REG_CP_REG, (crn, crm, op0, op1, op2))
else:
# fallback to default writing method
super().reg_write(reg_id, value)
def cpr_write(self, op0: int, op1: int, crn: int, crm: int, op2: int, value: int) -> None:
"""Write a coprocessor register value.
Args:
op0 : opcode 0, value varies between 0 and 3
op1 : opcode 1, value varies between 0 and 7
crn : coprocessor register to access (CRn), value varies between 0 and 15
crm : additional coprocessor register to access (CRm), value varies between 0 and 15
op2 : opcode 2, value varies between 0 and 7
value : value to write
"""
assert check_maxbits(op0, 2)
assert check_maxbits(op1, 3)
assert check_maxbits(crn, 4)
assert check_maxbits(crm, 4)
assert check_maxbits(op2, 3)
self.reg_write(const.UC_ARM64_REG_CP_REG, (crn, crm, op0, op1, op2, value))
else:
self._reg_write(reg_id, reg_cls, value)
__all__ = ['UcRegCP64', 'UcAArch64']

View File

@ -2,7 +2,7 @@
"""
# @author elicn
from typing import Any, Callable, Sequence, Tuple
from typing import Any, Callable, Tuple, Type
import ctypes
@ -64,6 +64,8 @@ class UcIntel(Uc):
"""Unicorn subclass for Intel architecture.
"""
REG_RANGE_MSR = (const.UC_X86_REG_MSR,)
REG_RANGE_MMR = (
const.UC_X86_REG_IDTR,
const.UC_X86_REG_GDTR,
@ -127,12 +129,13 @@ class UcIntel(Uc):
return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn)
@staticmethod
def __select_reg_class(reg_id: int):
"""Select class for special architectural registers.
@classmethod
def _select_reg_class(cls, reg_id: int) -> Type:
"""Select the appropriate class for the specified architectural register.
"""
reg_class = (
(UcIntel.REG_RANGE_MSR, UcRegMSR),
(UcIntel.REG_RANGE_MMR, UcRegMMR),
(UcIntel.REG_RANGE_FP, UcRegFPR),
(UcIntel.REG_RANGE_XMM, UcReg128),
@ -140,54 +143,28 @@ class UcIntel(Uc):
(UcIntel.REG_RANGE_ZMM, UcReg512)
)
return next((cls for rng, cls in reg_class if reg_id in rng), None)
def reg_read(self, reg_id: int, aux: Any = None):
# select register class for special cases
reg_cls = UcIntel.__select_reg_class(reg_id)
if reg_cls is None:
# backward compatibility: msr read through reg_read
if reg_id == const.UC_X86_REG_MSR:
if type(aux) is not int:
raise UcError(UC_ERR_ARG)
value = self.msr_read(aux)
else:
value = super().reg_read(reg_id, aux)
else:
value = self._reg_read(reg_id, reg_cls)
return value
def reg_write(self, reg_id: int, value) -> None:
# select register class for special cases
reg_cls = UcIntel.__select_reg_class(reg_id)
if reg_cls is None:
# backward compatibility: msr write through reg_write
if reg_id == const.UC_X86_REG_MSR:
if type(value) is not tuple or len(value) != 2:
raise UcError(UC_ERR_ARG)
self.msr_write(*value)
return
super().reg_write(reg_id, value)
else:
self._reg_write(reg_id, reg_cls, value)
return next((c for rng, c in reg_class if reg_id in rng), cls._DEFAULT_REGTYPE)
def msr_read(self, msr_id: int) -> int:
return self._reg_read(const.UC_X86_REG_MSR, UcRegMSR, msr_id)
"""Read a model-specific register.
Args:
msr_id: MSR index
Returns: MSR value
"""
return self.reg_read(const.UC_X86_REG_MSR, msr_id)
def msr_write(self, msr_id: int, value: int) -> None:
self._reg_write(const.UC_X86_REG_MSR, UcRegMSR, (msr_id, value))
"""Write to a model-specific register.
def reg_read_batch(self, reg_ids: Sequence[int]) -> Tuple:
reg_types = [UcIntel.__select_reg_class(rid) or self._DEFAULT_REGTYPE for rid in reg_ids]
Args:
msr_id: MSR index
value: new MSR value
"""
return self._reg_read_batch(reg_ids, reg_types)
self.reg_write(const.UC_X86_REG_MSR, (msr_id, value))
__all__ = ['UcRegMMR', 'UcRegMSR', 'UcRegFPR', 'UcIntel']

View File

@ -3,7 +3,7 @@
# @author elicn
from abc import abstractmethod
from typing import Generic, Tuple, TypeVar
from typing import Any, Generic, Tuple, TypeVar
import ctypes
@ -24,7 +24,7 @@ class UcReg(ctypes.Structure):
@property
@abstractmethod
def value(self):
def value(self) -> Any:
"""Get register value.
"""
@ -32,7 +32,7 @@ class UcReg(ctypes.Structure):
@classmethod
@abstractmethod
def from_value(cls, value):
def from_value(cls, value) -> 'UcReg':
"""Create a register instance from a given value.
"""
@ -52,7 +52,11 @@ class UcTupledReg(UcReg, Generic[VT]):
@classmethod
def from_value(cls, value: VT):
assert type(value) is tuple and len(value) == len(cls._fields_)
if not isinstance(value, tuple):
raise TypeError(f'got {type(value).__name__} while expecting a tuple')
if len(value) != len(cls._fields_):
raise TypeError(f'got {len(value)} elements while expecting {len(cls._fields_)}')
return cls(*value)
@ -72,7 +76,8 @@ class UcLargeReg(UcReg):
@classmethod
def from_value(cls, value: int):
assert type(value) is int
if not isinstance(value, int):
raise TypeError(f'got {type(value).__name__} while expecting an integer')
mask = (1 << 64) - 1
size = cls._fields_[0][1]._length_

View File

@ -3,32 +3,45 @@ based on Nguyen Anh Quynnh's work
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Mapping, MutableMapping, Optional, Sequence, Tuple, Type, TypeVar
from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, Iterable, Iterator, Optional, Sequence, Tuple, Type, TypeVar, Union
import ctypes
import functools
import weakref
from unicorn import unicorn_const as uc
from .arch.types import uc_err, uc_engine, uc_context, uc_hook_h, UcReg
from .arch.types import uc_err, uc_engine, uc_context, uc_hook_h, UcReg, VT
# __version__ = f'{uc.UC_VERSION_MAJOR}.{uc.UC_VERSION_MINOR}.{uc.UC_VERSION_PATCH}'
MemRegionStruct = Tuple[int, int, int]
TBStruct = Tuple[int, int, int]
class _uc_mem_region(ctypes.Structure):
class UcTupledStruct(ctypes.Structure, Generic[VT]):
"""A base class for structures that may be converted to tuples representing
their fields values.
"""
@property
def value(self) -> VT:
"""Convert structure into a tuple containing its fields values. This method
name is used to maintain consistency with other ctypes types.
"""
return tuple(getattr(self, fname) for fname, *_ in self.__class__._fields_) # type: ignore
class _uc_mem_region(UcTupledStruct[MemRegionStruct]):
_fields_ = (
('begin', ctypes.c_uint64),
('end', ctypes.c_uint64),
('perms', ctypes.c_uint32),
)
@property
def value(self) -> Tuple[int, int, int]:
return tuple(getattr(self, fname) for fname, *_ in self._fields_)
class uc_tb(ctypes.Structure):
""""TranslationBlock
class uc_tb(UcTupledStruct[TBStruct]):
""""Translation Block
"""
_fields_ = (
@ -43,7 +56,6 @@ def __load_uc_lib() -> ctypes.CDLL:
import inspect
import os
import sys
loaded_dlls = set()
@ -91,26 +103,19 @@ def __load_uc_lib() -> ctypes.CDLL:
# - we can get the path to the local libraries by parsing our filename
# - global load
# - python's lib directory
canonicals = []
try:
if sys.version_info.minor >= 12:
from importlib import resources
canonicals.append(
resources.files("unicorn") / 'lib'
)
except ImportError:
try:
import pkg_resources
canonicals.append(
pkg_resources.resource_filename("unicorn", 'lib')
)
except ImportError:
# maybe importlib_resources, but ignore for now
pass
canonicals = resources.files('unicorn') / 'lib'
else:
import pkg_resources
canonicals = pkg_resources.resource_filename('unicorn', 'lib')
lib_locations = [
os.getenv('LIBUNICORN_PATH'),
] + canonicals + [
canonicals,
PurePath(inspect.getfile(__load_uc_lib)).parent / 'lib',
'',
r'/usr/local/lib' if sys.platform == 'darwin' else r'/usr/lib64',
@ -162,39 +167,59 @@ def __set_lib_prototypes(lib: ctypes.CDLL) -> None:
func.restype = restype
func.argtypes = argtypes
__set_prototype('uc_version', ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
__set_prototype('uc_arch_supported', ctypes.c_bool, ctypes.c_int)
__set_prototype('uc_open', uc_err, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(uc_engine))
# declare a few ctypes aliases for brevity
char = ctypes.c_char
s32 = ctypes.c_int32
u32 = ctypes.c_uint32
u64 = ctypes.c_uint64
size_t = ctypes.c_size_t
void_p = ctypes.c_void_p
PTR = ctypes.POINTER
__set_prototype('uc_arch_supported', ctypes.c_bool, s32)
__set_prototype('uc_close', uc_err, uc_engine)
__set_prototype('uc_strerror', ctypes.c_char_p, uc_err)
__set_prototype('uc_errno', uc_err, uc_engine)
__set_prototype('uc_reg_read', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p)
__set_prototype('uc_reg_write', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p)
__set_prototype('uc_reg_read_batch', uc_err, uc_engine, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int)
__set_prototype('uc_mem_read', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t)
__set_prototype('uc_mem_write', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t)
__set_prototype('uc_emu_start', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t)
__set_prototype('uc_emu_stop', uc_err, uc_engine)
__set_prototype('uc_hook_del', uc_err, uc_engine, uc_hook_h)
__set_prototype('uc_mmio_map', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
__set_prototype('uc_mem_map', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32)
__set_prototype('uc_mem_map_ptr', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p)
__set_prototype('uc_mem_unmap', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t)
__set_prototype('uc_mem_protect', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32)
__set_prototype('uc_query', uc_err, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t))
__set_prototype('uc_context_alloc', uc_err, uc_engine, ctypes.POINTER(uc_context))
__set_prototype('uc_free', uc_err, ctypes.c_void_p)
__set_prototype('uc_context_save', uc_err, uc_engine, uc_context)
__set_prototype('uc_context_restore', uc_err, uc_engine, uc_context)
__set_prototype('uc_context_size', ctypes.c_size_t, uc_engine)
__set_prototype('uc_context_reg_read', uc_err, uc_context, ctypes.c_int, ctypes.c_void_p)
__set_prototype('uc_context_reg_write', uc_err, uc_context, ctypes.c_int, ctypes.c_void_p)
__set_prototype('uc_context_reg_read_batch', uc_err, uc_context, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int)
__set_prototype('uc_context_alloc', uc_err, uc_engine, PTR(uc_context))
__set_prototype('uc_context_free', uc_err, uc_context)
__set_prototype('uc_mem_regions', uc_err, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32))
# https://bugs.python.org/issue42880
__set_prototype('uc_hook_add', uc_err, uc_engine, ctypes.POINTER(uc_hook_h), ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64)
__set_prototype('uc_ctl', uc_err, uc_engine, ctypes.c_int)
__set_prototype('uc_context_reg_read', uc_err, uc_context, s32, void_p)
__set_prototype('uc_context_reg_read_batch', uc_err, uc_context, PTR(s32), PTR(void_p), s32)
__set_prototype('uc_context_reg_write', uc_err, uc_context, s32, void_p)
__set_prototype('uc_context_reg_write_batch', uc_err, uc_context, PTR(s32), void_p, s32)
__set_prototype('uc_context_restore', uc_err, uc_engine, uc_context)
__set_prototype('uc_context_save', uc_err, uc_engine, uc_context)
__set_prototype('uc_context_size', size_t, uc_engine)
__set_prototype('uc_ctl', uc_err, uc_engine, s32)
__set_prototype('uc_emu_start', uc_err, uc_engine, u64, u64, u64, size_t)
__set_prototype('uc_emu_stop', uc_err, uc_engine)
__set_prototype('uc_errno', uc_err, uc_engine)
__set_prototype('uc_free', uc_err, void_p)
__set_prototype('uc_hook_add', uc_err, uc_engine, PTR(uc_hook_h), s32, void_p, void_p, u64, u64)
__set_prototype('uc_hook_del', uc_err, uc_engine, uc_hook_h)
__set_prototype('uc_mem_map', uc_err, uc_engine, u64, size_t, u32)
__set_prototype('uc_mem_map_ptr', uc_err, uc_engine, u64, size_t, u32, void_p)
__set_prototype('uc_mem_protect', uc_err, uc_engine, u64, size_t, u32)
__set_prototype('uc_mem_read', uc_err, uc_engine, u64, PTR(char), size_t)
__set_prototype('uc_mem_regions', uc_err, uc_engine, PTR(PTR(_uc_mem_region)), PTR(u32))
__set_prototype('uc_mem_unmap', uc_err, uc_engine, u64, size_t)
__set_prototype('uc_mem_write', uc_err, uc_engine, u64, PTR(char), size_t)
__set_prototype('uc_mmio_map', uc_err, uc_engine, u64, size_t, void_p, void_p, void_p, void_p)
__set_prototype('uc_open', uc_err, u32, u32, PTR(uc_engine))
__set_prototype('uc_query', uc_err, uc_engine, u32, PTR(size_t))
__set_prototype('uc_reg_read', uc_err, uc_engine, s32, void_p)
__set_prototype('uc_reg_read_batch', uc_err, uc_engine, PTR(s32), PTR(void_p), s32)
__set_prototype('uc_reg_write', uc_err, uc_engine, s32, void_p)
__set_prototype('uc_reg_write_batch', uc_err, uc_engine, PTR(s32), PTR(void_p), s32)
__set_prototype('uc_strerror', ctypes.c_char_p, uc_err)
__set_prototype('uc_version', u32, PTR(s32), PTR(s32))
# TODO:
# __set_prototype('uc_context_reg_read2', uc_err, uc_context, s32, void_p, PTR(size_t))
# __set_prototype('uc_context_reg_read_batch2', uc_err, uc_context, PTR(s32), PTR(void_p), PTR(size_t), s32)
# __set_prototype('uc_context_reg_write2', uc_err, uc_context, s32, void_p, PTR(size_t))
# __set_prototype('uc_context_reg_write_batch2', uc_err, uc_context, PTR(s32), PTR(void_p), PTR(size_t), s32)
# __set_prototype('uc_reg_read2', uc_err, uc_engine, s32, void_p, PTR(size_t))
# __set_prototype('uc_reg_read_batch2', uc_err, uc_engine, PTR(s32), PTR(void_p), PTR(size_t), s32)
# __set_prototype('uc_reg_write2', uc_err, uc_engine, s32, void_p, PTR(size_t))
# __set_prototype('uc_reg_write_batch2', uc_err, uc_engine, PTR(s32), PTR(void_p), PTR(size_t), s32)
uclib = __load_uc_lib()
@ -215,6 +240,19 @@ MMIO_READ_CFUNC = ctypes.CFUNCTYPE(ctypes.c_uint64, uc_engine, ctypes.c_uint64,
MMIO_WRITE_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_uint, ctypes.c_uint64, ctypes.c_void_p)
def check_maxbits(value: int, nbits: int) -> bool:
"""Verify that a certain value may be represented with at most `nbits` bits.
Args:
value : numeric value to check
nbits : max number of bits allowed
Returns: `True` if `value` is represented by at most `nbits` bits, `False` otherwise
"""
return value & ~((1 << nbits) - 1) == 0
class UcError(Exception):
"""Unicorn base exception.
@ -310,7 +348,7 @@ def uccallback(uc: Uc, functype: Type[_CFP]):
def decorate(func) -> _CFP:
@functools.wraps(func)
def wrapper(handle: Uc, *args, **kwargs):
def wrapper(handle: int, *args, **kwargs):
try:
return func(uc, *args, **kwargs)
except Exception as e:
@ -334,47 +372,62 @@ class RegStateManager:
_DEFAULT_REGTYPE = ctypes.c_uint64
@classmethod
def _select_reg_class(cls, reg_id: int) -> Type:
"""An architecture-specific method that returns the appropriate
value type for a specified reg identifier. All rich `Uc` subclasses
are expected to implement their own
"""
return cls._DEFAULT_REGTYPE
def _do_reg_read(self, reg_id: int, reg_obj) -> int:
"""Private register read implementation.
"""Low level register read implementation.
Must be implemented by the mixin object
"""
raise NotImplementedError
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
"""Private register write implementation.
"""Low level register write implementation.
Must be implemented by the mixin object
"""
raise NotImplementedError
def _do_reg_read_batch(self, reglist, vallist, count) -> int:
"""Private batch register read implementation.
"""Low level batch register read implementation.
Must be implemented by the mixin object
"""
raise NotImplementedError
def _do_reg_write_batch(self, reglist, count) -> int:
"""Private batch register write implementation.
def _do_reg_write_batch(self, reglist, vallist, count) -> int:
"""Low level batch register write implementation.
Must be implemented by the mixin object
"""
raise NotImplementedError
@staticmethod
def __get_reg_read_arg(regtype: Type, *args):
return regtype(*args)
def __get_reg_read_arg(reg_type: Type, aux: Any):
if aux is None:
return reg_type()
if isinstance(aux, tuple):
return reg_type(*aux)
return reg_type(aux)
@staticmethod
def __get_reg_write_arg(regtype: Type, value):
return regtype.from_value(value) if issubclass(regtype, UcReg) else regtype(value)
def __get_reg_write_arg(reg_type: Type, value: Any):
return reg_type.from_value(value) if issubclass(reg_type, UcReg) else reg_type(value)
def _reg_read(self, reg_id: int, regtype: Type, *args):
def _reg_read(self, reg_id: int, reg_type: Type, aux: Any):
"""Register read helper method.
"""
reg = self.__get_reg_read_arg(regtype, *args)
reg = self.__get_reg_read_arg(reg_type, aux)
status = self._do_reg_read(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
@ -382,25 +435,32 @@ class RegStateManager:
return reg.value
def _reg_write(self, reg_id: int, regtype: Type, value) -> None:
def _reg_write(self, reg_id: int, reg_type: Type, value: Any) -> None:
"""Register write helper method.
"""
reg = self.__get_reg_write_arg(regtype, value)
reg = self.__get_reg_write_arg(reg_type, value)
status = self._do_reg_write(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status, reg_id)
def _reg_read_batch(self, reg_ids: Sequence[int], reg_types: Sequence[Type]) -> Tuple:
def _reg_read_batch(self, read_seq: Sequence[Tuple[int, Type, Any]]) -> Tuple:
"""Batch register read helper method.
Args:
read_seq: a sequence of 3-tuples containing register identifier, returned
value type and auxiliary data (or `None` if aux is not required)
Returns: a tuple of values
"""
assert len(reg_ids) == len(reg_types)
count = len(read_seq)
reg_ids = (rid for rid, _, _ in read_seq)
reg_info = ((rtype, aux) for _, rtype, aux in read_seq)
count = len(reg_ids)
reg_list = (ctypes.c_int * count)(*reg_ids)
val_list = [rtype() for rtype in reg_types]
val_list = [self.__get_reg_read_arg(rtype, aux) for rtype, aux in reg_info]
ptr_list = (ctypes.c_void_p * count)(*(ctypes.c_void_p(ctypes.addressof(elem)) for elem in val_list))
status = self._do_reg_read_batch(reg_list, ptr_list, ctypes.c_int(count))
@ -410,6 +470,30 @@ class RegStateManager:
return tuple(v.value for v in val_list)
def _reg_write_batch(self, write_seq: Sequence[Tuple[int, Type, Any]]) -> None:
"""Batch register write helper method.
Args:
write_seq: a sequence of 3-tuples containing register identifier, value type and value
"""
count = len(write_seq)
reg_ids = (rid for rid, _, _ in write_seq)
reg_info = ((rtype, aux) for _, rtype, aux in write_seq)
reg_list = (ctypes.c_int * count)(*reg_ids)
val_list = [self.__get_reg_write_arg(rtype, rval) for rtype, rval in reg_info]
ptr_list = (ctypes.c_void_p * count)(*(ctypes.c_void_p(ctypes.addressof(elem)) for elem in val_list))
status = self._do_reg_write_batch(reg_list, ptr_list, ctypes.c_int(count))
if status != uc.UC_ERR_OK:
raise UcError(status)
#########################
# External facing API #
#########################
def reg_read(self, reg_id: int, aux: Any = None):
"""Read architectural register value.
@ -422,7 +506,9 @@ class RegStateManager:
Raises: `UcError` in case of invalid register id or auxiliary data
"""
return self._reg_read(reg_id, self._DEFAULT_REGTYPE)
reg_type = self._select_reg_class(reg_id)
return self._reg_read(reg_id, reg_type, aux)
def reg_write(self, reg_id: int, value) -> None:
"""Write to architectural register.
@ -434,34 +520,50 @@ class RegStateManager:
Raises: `UcError` in case of invalid register id or value format
"""
self._reg_write(reg_id, self._DEFAULT_REGTYPE, value)
reg_type = self._select_reg_class(reg_id)
def reg_read_batch(self, reg_ids: Sequence[int]) -> Tuple:
"""Read a sequence of architectural registers.
self._reg_write(reg_id, reg_type, value)
def reg_read_batch(self, reg_data: Sequence[Union[int, Tuple[int, Any]]]) -> Tuple:
"""Read a sequence of architectural registers. This provides with faster means to
read multiple registers.
Args:
reg_ids: a sequence of register identifiers (architecture-specific enumeration)
reg_ids : a sequence of register identifiers (architecture-specific enumeration)
aux : a mapping of reg identifiers and auxiliary data, in case it is required
Returns: a tuple of registers values (register-specific format)
Raises: `UcError` in case of invalid register id
Raises: `UcError` in case of an invalid register id, or an invalid aux data for a
register that requires it
"""
reg_types = [self._DEFAULT_REGTYPE for _ in range(len(reg_ids))]
def __seq_tuple(elem: Union[int, Tuple[int, Any]]) -> Tuple[int, Type, Any]:
reg_id, reg_aux = elem if isinstance(elem, tuple) else (elem, None)
reg_type = self._select_reg_class(reg_id)
return self._reg_read_batch(reg_ids, reg_types)
return (reg_id, reg_type, reg_aux)
def reg_write_batch(self, reg_info: Sequence[Tuple[int, Any]]) -> None:
"""Write a sequece of architectural registers.
return self._reg_read_batch([__seq_tuple(elem) for elem in reg_data])
def reg_write_batch(self, reg_data: Sequence[Tuple[int, Any]]) -> None:
"""Write a sequece of architectural registers. This provides with faster means to
write multiple registers.
Args:
regs_info: a sequence of tuples consisting of register identifiers and values
reg_data: a sequence of register identifiers matched with their designated values
Raises: `UcError` in case of invalid register id or value format
"""
# TODO
...
def __seq_tuple(elem: Tuple[int, Any]) -> Tuple[int, Type, Any]:
reg_id, reg_val = elem
reg_type = self._select_reg_class(reg_id)
return (reg_id, reg_type, reg_val)
self._reg_write_batch([__seq_tuple(elem) for elem in reg_data])
def ucsubclass(cls):
"""Uc subclass decorator.
@ -496,7 +598,10 @@ def ucsubclass(cls):
return seq[:i] + tuple([repl]) + seq[i + 1:]
def __new_uc_subclass(cls, arch: int, mode: int):
def __new_uc_subclass(cls, arch: int, mode: int, *args, **kwargs):
# this method has to contain *args and **kwargs to allow Uc subclasses
# to use additional arguments in their constructors
# resolve the appropriate Uc subclass
subcls = Uc.__new__(cls, arch, mode)
@ -591,8 +696,8 @@ class Uc(RegStateManager):
# we have to keep a reference to the callbacks so they do not get gc-ed
# see: https://docs.python.org/3/library/ctypes.html#callback-functions
self._callbacks: MutableMapping[int, ctypes._FuncPointer] = {}
self._mmio_callbacks: MutableMapping[Tuple[int, int], Tuple[Optional[MMIO_READ_CFUNC], Optional[MMIO_WRITE_CFUNC]]] = {}
self._callbacks: Dict[int, ctypes._FuncPointer] = {}
self._mmio_callbacks: Dict[Tuple[int, int], Tuple[Optional[MMIO_READ_CFUNC], Optional[MMIO_WRITE_CFUNC]]] = {}
self._hook_exception: Optional[Exception] = None
@ -617,6 +722,15 @@ class Uc(RegStateManager):
if status != uc.UC_ERR_OK:
raise UcError(status)
@property
def errno(self) -> int:
"""Get last error number.
Returns: error number (see: UC_ERR_*)
"""
return uclib.uc_errno(self._uch)
###########################
# Emulation controllers #
###########################
@ -680,6 +794,13 @@ class Uc(RegStateManager):
return uclib.uc_reg_read_batch(self._uch, reglist, vallist, count)
def _do_reg_write_batch(self, reglist, vallist, count) -> int:
"""Private batch register write implementation.
Do not call directly.
"""
return uclib.uc_reg_write_batch(self._uch, reglist, vallist, count)
###########################
# Memory management #
###########################
@ -767,20 +888,33 @@ class Uc(RegStateManager):
raise UcError(status)
def mmio_map(self, address: int, size: int,
read_cb: Optional[UC_MMIO_READ_TYPE], user_data_read: Any,
write_cb: Optional[UC_MMIO_WRITE_TYPE], user_data_write: Any) -> None:
read_cb: Optional[UC_MMIO_READ_TYPE], read_ud: Any,
write_cb: Optional[UC_MMIO_WRITE_TYPE], write_ud: Any) -> None:
"""Map an MMIO range. This method binds a memory range to read and write accessors
to simulate a hardware device. Unicorn does not allocate memory to back this range.
Args:
address : range base address
size : range size (in bytes)
read_cb : read callback to invoke upon read access. if not specified, reads \
from the mmio range will be silently dropped
read_ud : optinal context object to pass on to the read callback
write_cb : write callback to invoke unpon a write access. if not specified, writes \
to the mmio range will be silently dropped
write_ud : optinal context object to pass on to the write callback
"""
@uccallback(self, MMIO_READ_CFUNC)
def __mmio_map_read_cb(uc: Uc, offset: int, size: int, key: int) -> int:
assert read_cb is not None
return read_cb(uc, offset, size, user_data_read)
return read_cb(uc, offset, size, read_ud)
@uccallback(self, MMIO_WRITE_CFUNC)
def __mmio_map_write_cb(uc: Uc, offset: int, size: int, value: int, key: int) -> None:
assert write_cb is not None
write_cb(uc, offset, size, value, user_data_write)
write_cb(uc, offset, size, value, write_ud)
read_cb_fptr = read_cb and __mmio_map_read_cb
write_cb_fptr = write_cb and __mmio_map_write_cb
@ -860,7 +994,7 @@ class Uc(RegStateManager):
def __do_hook_add(self, htype: int, fptr: ctypes._FuncPointer, begin: int, end: int, *args: ctypes.c_int) -> int:
handle = uc_hook_h()
# TODO: we do not need a callback counter to reference the callback and user data anymore,
# we do not need a callback counter to reference the callback and user data anymore,
# so just pass a dummy value. that value will become the unused 'key' argument
dummy = 0
@ -905,7 +1039,7 @@ class Uc(RegStateManager):
def __hook_intr_cb(uc: Uc, intno: int, key: int):
callback(uc, intno, user_data)
return __hook_intr_cb,
return (__hook_intr_cb,)
def __hook_insn():
# each arch is expected to overload hook_add and implement this handler on their own.
@ -918,35 +1052,35 @@ class Uc(RegStateManager):
def __hook_code_cb(uc: Uc, address: int, size: int, key: int):
callback(uc, address, size, user_data)
return __hook_code_cb,
return (__hook_code_cb,)
def __hook_invalid_mem():
@uccallback(self, HOOK_MEM_INVALID_CFUNC)
def __hook_mem_invalid_cb(uc: Uc, access: int, address: int, size: int, value: int, key: int) -> bool:
return callback(uc, access, address, size, value, user_data)
return __hook_mem_invalid_cb,
return (__hook_mem_invalid_cb,)
def __hook_mem():
@uccallback(self, HOOK_MEM_ACCESS_CFUNC)
def __hook_mem_access_cb(uc: Uc, access: int, address: int, size: int, value: int, key: int) -> None:
callback(uc, access, address, size, value, user_data)
return __hook_mem_access_cb,
return (__hook_mem_access_cb,)
def __hook_invalid_insn():
@uccallback(self, HOOK_INSN_INVALID_CFUNC)
def __hook_insn_invalid_cb(uc: Uc, key: int) -> bool:
return callback(uc, user_data)
return __hook_insn_invalid_cb,
return (__hook_insn_invalid_cb,)
def __hook_edge_gen():
@uccallback(self, HOOK_EDGE_GEN_CFUNC)
def __hook_edge_gen_cb(uc: Uc, cur: ctypes._Pointer[uc_tb], prev: ctypes._Pointer[uc_tb], key: int):
callback(uc, cur.contents, prev.contents, user_data)
return __hook_edge_gen_cb,
return (__hook_edge_gen_cb,)
def __hook_tcg_opcode():
@uccallback(self, HOOK_TCG_OPCODE_CFUNC)
@ -956,9 +1090,9 @@ class Uc(RegStateManager):
opcode = ctypes.c_uint64(aux1)
flags = ctypes.c_uint64(aux2)
return __hook_tcg_op_cb, opcode, flags
return (__hook_tcg_op_cb, opcode, flags)
handlers: Mapping[int, Callable[[], Tuple]] = {
handlers: Dict[int, Callable[[], Tuple]] = {
uc.UC_HOOK_INTR : __hook_intr,
uc.UC_HOOK_INSN : __hook_insn,
uc.UC_HOOK_CODE : __hook_code,
@ -1034,6 +1168,11 @@ class Uc(RegStateManager):
return result.value
def context_save(self) -> UcContext:
"""Save Unicorn instance internal context.
Returns: unicorn context instance
"""
context = UcContext(self._uch, self._arch, self._mode)
status = uclib.uc_context_save(self._uch, context.context)
@ -1043,12 +1182,24 @@ class Uc(RegStateManager):
return context
def context_update(self, context: UcContext) -> None:
"""Update Unicorn instance internal context.
Args:
context : unicorn context instance to copy data from
"""
status = uclib.uc_context_save(self._uch, context.context)
if status != uc.UC_ERR_OK:
raise UcError(status)
def context_restore(self, context: UcContext) -> None:
"""Overwrite Unicorn instance internal context.
Args:
context : unicorn context instance to copy data from
"""
status = uclib.uc_context_restore(self._uch, context.context)
if status != uc.UC_ERR_OK:
@ -1056,8 +1207,8 @@ class Uc(RegStateManager):
@staticmethod
def __ctl_encode(ctl: int, op: int, nargs: int) -> int:
assert nargs and (nargs & ~0b1111) == 0, f'nargs must not exceed value of 15 (got {nargs})'
assert op and (op & ~0b11) == 0, f'op must not exceed value of 3 (got {op})'
assert check_maxbits(nargs, 4), f'nargs must not exceed value of 15 (got {nargs})'
assert op and check_maxbits(op, 2), f'op must not exceed value of 3 (got {op})'
return (op << 30) | (nargs << 26) | ctl
@ -1093,91 +1244,214 @@ class Uc(RegStateManager):
self.ctl(ctl, uc.UC_CTL_IO_READ_WRITE, carg0, ctypes.byref(carg1))
return carg1
return carg1.value
def ctl_get_mode(self) -> int:
"""Retrieve current processor mode.
Returns: current mode (see UC_MODE_* constants)
"""
return self.__ctl_r(uc.UC_CTL_UC_MODE,
(ctypes.c_int, None)
)
def ctl_get_page_size(self) -> int:
"""Retrieve target page size.
Returns: page size in bytes
"""
return self.__ctl_r(uc.UC_CTL_UC_PAGE_SIZE,
(ctypes.c_uint32, None)
)
def ctl_set_page_size(self, val: int) -> None:
"""Set target page size.
Args:
val: page size to set (in bytes)
Raises: `UcError` in any of the following cases:
- Unicorn architecture is not ARM
- Unicorn has already completed its initialization
- Page size is not a power of 2
"""
self.__ctl_w(uc.UC_CTL_UC_PAGE_SIZE,
(ctypes.c_uint32, val)
)
def ctl_get_arch(self) -> int:
"""Retrieve target architecture.
Returns: current architecture (see UC_ARCH_* constants)
"""
return self.__ctl_r(uc.UC_CTL_UC_ARCH,
(ctypes.c_int, None)
)
def ctl_get_timeout(self) -> int:
"""Retrieve emulation timeout.
Returns: timeout value set on emulation start
"""
return self.__ctl_r(uc.UC_CTL_UC_TIMEOUT,
(ctypes.c_uint64, None)
)
def ctl_exits_enabled(self, val: bool) -> None:
def ctl_exits_enabled(self, enable: bool) -> None:
"""Instruct Unicorn whether to respect emulation exit points or ignore them.
Args:
enable: `True` to enable exit points, `False` to ignore them
"""
self.__ctl_w(uc.UC_CTL_UC_USE_EXITS,
(ctypes.c_int, val)
(ctypes.c_int, enable)
)
def ctl_get_exits_cnt(self) -> int:
"""Retrieve emulation exit points count.
Returns: number of emulation exit points
Raises: `UcErro` if Unicorn is set to ignore exits
"""
return self.__ctl_r(uc.UC_CTL_UC_EXITS_CNT,
(ctypes.c_size_t, None)
)
def ctl_get_exits(self) -> Sequence[int]:
"""Retrieve emulation exit points.
Returns: a tuple of all emulation exit points
Raises: `UcErro` if Unicorn is set to ignore exits
"""
count = self.ctl_get_exits_cnt()
arr = (ctypes.c_uint64 * count)()
self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_READ, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(count))
return tuple(i for i in arr)
return tuple(arr)
def ctl_set_exits(self, exits: Sequence[int]) -> None:
arr = (ctypes.c_uint64 * len(exits))()
"""Set emulation exit points.
for idx, exit in enumerate(exits):
arr[idx] = exit
Args:
exits: a list of emulation exit points to set
self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_WRITE, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(len(exits)))
Raises: `UcErro` if Unicorn is set to ignore exits
"""
arr = (ctypes.c_uint64 * len(exits))(*exits)
self.ctl(uc.UC_CTL_UC_EXITS, uc.UC_CTL_IO_WRITE, ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(len(arr)))
def ctl_get_cpu_model(self) -> int:
"""Retrieve target processor model.
Returns: target cpu model (see UC_CPU_* constants)
"""
return self.__ctl_r(uc.UC_CTL_CPU_MODEL,
(ctypes.c_int, None)
)
def ctl_set_cpu_model(self, val: int) -> None:
def ctl_set_cpu_model(self, model: int) -> None:
"""Set target processor model.
Args:
model: cpu model to set (see UC_CPU_* constants)
Raises: `UcError` in any of the following cases:
- `model` is not a valid cpu model
- Requested cpu model is incompatible with current mode
- Unicorn has already completed its initialization
"""
self.__ctl_w(uc.UC_CTL_CPU_MODEL,
(ctypes.c_int, val)
(ctypes.c_int, model)
)
def ctl_remove_cache(self, addr: int, end: int) -> None:
def ctl_remove_cache(self, lbound: int, ubound: int) -> None:
"""Invalidate translation cache for a specified region.
Args:
lbound: region lower bound
ubound: region upper bound
Raises: `UcError` in case the provided range bounds are invalid
"""
self.__ctl_w(uc.UC_CTL_TB_REMOVE_CACHE,
(ctypes.c_uint64, addr),
(ctypes.c_uint64, end)
(ctypes.c_uint64, lbound),
(ctypes.c_uint64, ubound)
)
def ctl_request_cache(self, addr: int):
def ctl_request_cache(self, addr: int) -> TBStruct:
"""Get translation cache info for a specified address.
Args:
addr: address to get its translation cache info
Returns: a 3-tuple containing the base address, instructions count and
size of the translation block containing the specified address
"""
return self.__ctl_wr(uc.UC_CTL_TB_REQUEST_CACHE,
(ctypes.c_uint64, addr),
(uc_tb, None)
)
def ctl_flush_tb(self) -> None:
"""Flush the entire translation cache.
"""
self.__ctl_w(uc.UC_CTL_TB_FLUSH)
def ctl_tlb_mode(self, mode: int) -> None:
def ctl_set_tlb_mode(self, mode: int) -> None:
"""Set TLB mode.
Args:
mode: tlb mode to use (see UC_TLB_* constants)
"""
self.__ctl_w(uc.UC_CTL_TLB_TYPE,
(ctypes.c_uint, mode)
)
def ctl_get_tcg_buffer_size(self) -> int:
"""Retrieve TCG buffer size.
Returns: buffer size (in bytes)
"""
return self.__ctl_r(uc.UC_CTL_TCG_BUFFER_SIZE,
(ctypes.c_uint32, None)
)
def ctl_set_tcg_buffer_size(self, size: int) -> None:
"""Set TCG buffer size.
Args:
size: new size to set
"""
self.__ctl_w(uc.UC_CTL_TCG_BUFFER_SIZE,
(ctypes.c_uint32, size)
)
class UcContext(RegStateManager):
"""Unicorn internal context.
"""
def __init__(self, h, arch: int, mode: int):
self._context = uc_context()
self._size = uclib.uc_context_size(h)
@ -1193,18 +1467,31 @@ class UcContext(RegStateManager):
@property
def context(self):
"""Underlying context data.
Normally this property should not be accessed directly.
"""
return self._context
@property
def size(self) -> int:
"""Underlying context data size.
"""
return self._size
@property
def arch(self) -> int:
"""Get emulated architecture (see UC_ARCH_* constants).
"""
return self._arch
@property
def mode(self) -> int:
"""Get emulated processor mode (see UC_MODE_* constants).
"""
return self._mode
# RegStateManager mixin method implementation
@ -1227,11 +1514,17 @@ class UcContext(RegStateManager):
return uclib.uc_context_reg_read_batch(self._context, reglist, vallist, count)
def _do_reg_write_batch(self, reglist, vallist, count) -> int:
"""Private batch register write implementation.
"""
return uclib.uc_context_reg_write_batch(self._context, reglist, vallist, count)
# Make UcContext picklable
def __getstate__(self):
def __getstate__(self) -> Tuple[bytes, int, int, int]:
return bytes(self), self.size, self.arch, self.mode
def __setstate__(self, state) -> None:
def __setstate__(self, state: Tuple[bytes, int, int, int]) -> None:
context, size, arch, mode = state
self._context = ctypes.cast(ctypes.create_string_buffer(context, size), uc_context)
@ -1243,7 +1536,7 @@ class UcContext(RegStateManager):
self._to_free = False
def __bytes__(self) -> bytes:
return ctypes.string_at(self.context, self.size)
return ctypes.string_at(self._context, self._size)
def __del__(self) -> None:
# We need this property since we shouldn't free it if the object is constructed from pickled bytes.
@ -1256,5 +1549,3 @@ UC_MMIO_WRITE_TYPE = Callable[[Uc, int, int, int, Any], None]
__all__ = ['Uc', 'UcContext', 'ucsubclass', 'UcError', 'uc_version', 'version_bind', 'uc_arch_supported', 'debug']

View File

@ -333,7 +333,7 @@ gchar *g_array_free (GArray *farray,
/* if others are holding a reference, preserve the wrapper but do free/return the data */
//if (!g_atomic_ref_count_dec (&array->ref_count))
flags |= PRESERVE_WRAPPER;
// flags |= PRESERVE_WRAPPER;
return array_free (array, flags);
}

View File

@ -65,7 +65,7 @@ typedef size_t uc_hook;
#define UNICORN_DEPRECATED __declspec(deprecated)
#else
#pragma message( \
"WARNING: You need to implement UNICORN_DEPRECATED for this compiler")
"WARNING: You need to implement UNICORN_DEPRECATED for this compiler")
#define UNICORN_DEPRECATED
#endif

View File

@ -1600,12 +1600,25 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi,
if (!HOOK_BOUND_CHECK(hook, paddr))
continue;
JIT_CALLBACK_GUARD(((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, paddr, size, 0, hook->user_data));
// the last callback may already asked to stop emulation
if (uc->stop_request)
break;
}
/* Unicorn: Previous callbacks may invalidate TLB, reload everything.
This may have impact on performance but generally fine.
A better approach is not always invalidating tlb but this
might cause more chaos regarding re-entry (nested uc_emu_start).
*/
if (tlb_entry_is_empty(entry)) {
tlb_fill(env_cpu(env), addr, size,
access_type, mmu_idx, retaddr);
index = tlb_index(env, mmu_idx, addr);
entry = tlb_entry(env, mmu_idx, addr);
tlb_addr = code_read ? entry->addr_code : entry->addr_read;
tlb_addr &= ~TLB_INVALID_MASK;
}
// callback on non-readable memory
if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable
handled = false;

View File

@ -223,6 +223,10 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
CHECK_REG_TYPE(uint64_t);
*(uint64_t *)value = env->pc;
break;
case UC_ARM64_REG_WSP:
CHECK_REG_TYPE(uint32_t);
*(uint32_t *)value = READ_DWORD(env->xregs[31]);
break;
case UC_ARM64_REG_SP:
CHECK_REG_TYPE(uint64_t);
*(uint64_t *)value = env->xregs[31];
@ -263,9 +267,18 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
CHECK_REG_TYPE(uint32_t);
*(uint32_t *)value = vfp_get_fpsr(env);
break;
case UC_ARM64_REG_XZR:
CHECK_REG_TYPE(uint64_t);
*(uint64_t *)value = 0;
break;
case UC_ARM64_REG_WZR:
CHECK_REG_TYPE(uint32_t);
*(uint32_t *)value = 0;
break;
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -355,6 +368,10 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
env->pc = *(uint64_t *)value;
*setpc = 1;
break;
case UC_ARM64_REG_WSP:
CHECK_REG_TYPE(uint32_t);
WRITE_DWORD(env->xregs[31], (*(uint32_t *)value));
break;
case UC_ARM64_REG_SP:
CHECK_REG_TYPE(uint64_t);
env->xregs[31] = *(uint64_t *)value;
@ -396,9 +413,18 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
CHECK_REG_TYPE(uint32_t);
vfp_set_fpsr(env, *(uint32_t *)value);
break;
case UC_ARM64_REG_XZR:
CHECK_REG_TYPE(uint64_t);
// no-ops actually
break;
case UC_ARM64_REG_WZR:
CHECK_REG_TYPE(uint32_t);
// no-ops actually
break;
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -10,6 +10,8 @@
#include "uc_priv.h"
#include "unicorn_common.h"
#include "unicorn.h"
#include "stdio.h"
#include "inttypes.h"
ARMCPU *cpu_arm_init(struct uc_struct *uc);
@ -353,6 +355,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -553,6 +556,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -1397,7 +1397,7 @@ typedef struct CPUX86State {
SegmentCache gdt; /* only base and limit are used */
SegmentCache idt; /* only base and limit are used */
target_ulong cr[5]; /* NOTE: cr1 is unused */
target_ulong cr[9]; /* NOTE: cr1, cr5-cr7 are not used */
int32_t a20_mask;
BNDReg bnd_regs[4];
@ -1432,11 +1432,15 @@ typedef struct CPUX86State {
ZMMReg xmm_t0;
MMXReg mmx_t0;
XMMReg ymmh_regs[CPU_NB_REGS];
/*
* YMM is not supported by QEMU at all
* As of qemu 5.0.1, ymmh_regs is nowhere used.
*/
XMMReg ymmh_regs[CPU_NB_REGS]; /* currently not in use */
uint64_t opmask_regs[NB_OPMASK_REGS];
YMMReg zmmh_regs[CPU_NB_REGS];
ZMMReg hi16_zmm_regs[CPU_NB_REGS];
YMMReg zmmh_regs[CPU_NB_REGS]; /* currently not in use */
ZMMReg hi16_zmm_regs[CPU_NB_REGS]; /* currently not in use */
/* sysenter registers */
uint32_t sysenter_cs;

View File

@ -285,9 +285,9 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
case UC_X86_REG_XMM5:
case UC_X86_REG_XMM6:
case UC_X86_REG_XMM7: {
CHECK_REG_TYPE(float64[2]);
float64 *dst = (float64 *)value;
ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0];
CHECK_REG_TYPE(uint64_t[2]);
uint64_t *dst = (uint64_t *)value;
const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0];
dst[0] = reg->ZMM_Q(0);
dst[1] = reg->ZMM_Q(1);
return ret;
@ -311,23 +311,14 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
case UC_X86_REG_YMM4:
case UC_X86_REG_YMM5:
case UC_X86_REG_YMM6:
case UC_X86_REG_YMM7:
case UC_X86_REG_YMM8:
case UC_X86_REG_YMM9:
case UC_X86_REG_YMM10:
case UC_X86_REG_YMM11:
case UC_X86_REG_YMM12:
case UC_X86_REG_YMM13:
case UC_X86_REG_YMM14:
case UC_X86_REG_YMM15: {
CHECK_REG_TYPE(float64[4]);
float64 *dst = (float64 *)value;
ZMMReg *lo_reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_YMM0];
XMMReg *hi_reg = &env->ymmh_regs[regid - UC_X86_REG_YMM0];
dst[0] = lo_reg->ZMM_Q(0);
dst[1] = lo_reg->ZMM_Q(1);
dst[2] = hi_reg->_d[0];
dst[3] = hi_reg->_d[1];
case UC_X86_REG_YMM7: {
CHECK_REG_TYPE(uint64_t[4]);
uint64_t *dst = (uint64_t *)value;
const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0];
dst[0] = reg->ZMM_Q(0);
dst[1] = reg->ZMM_Q(1);
dst[2] = reg->ZMM_Q(2);
dst[3] = reg->ZMM_Q(3);
return ret;
}
@ -594,6 +585,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
case UC_X86_REG_CR2:
case UC_X86_REG_CR3:
case UC_X86_REG_CR4:
case UC_X86_REG_CR8:
CHECK_REG_TYPE(int64_t);
*(int64_t *)value = env->cr[regid - UC_X86_REG_CR0];
break;
@ -967,14 +959,108 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
case UC_X86_REG_XMM12:
case UC_X86_REG_XMM13:
case UC_X86_REG_XMM14:
case UC_X86_REG_XMM15: {
CHECK_REG_TYPE(float64[2]);
float64 *dst = (float64 *)value;
ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0];
case UC_X86_REG_XMM15:
case UC_X86_REG_XMM16:
case UC_X86_REG_XMM17:
case UC_X86_REG_XMM18:
case UC_X86_REG_XMM19:
case UC_X86_REG_XMM20:
case UC_X86_REG_XMM21:
case UC_X86_REG_XMM22:
case UC_X86_REG_XMM23:
case UC_X86_REG_XMM24:
case UC_X86_REG_XMM25:
case UC_X86_REG_XMM26:
case UC_X86_REG_XMM27:
case UC_X86_REG_XMM28:
case UC_X86_REG_XMM29:
case UC_X86_REG_XMM30:
case UC_X86_REG_XMM31: {
CHECK_REG_TYPE(uint64_t[2]);
uint64_t *dst = (uint64_t *)value;
const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_XMM0];
dst[0] = reg->ZMM_Q(0);
dst[1] = reg->ZMM_Q(1);
break;
}
case UC_X86_REG_YMM8:
case UC_X86_REG_YMM9:
case UC_X86_REG_YMM10:
case UC_X86_REG_YMM11:
case UC_X86_REG_YMM12:
case UC_X86_REG_YMM13:
case UC_X86_REG_YMM14:
case UC_X86_REG_YMM15:
case UC_X86_REG_YMM16:
case UC_X86_REG_YMM17:
case UC_X86_REG_YMM18:
case UC_X86_REG_YMM19:
case UC_X86_REG_YMM20:
case UC_X86_REG_YMM21:
case UC_X86_REG_YMM22:
case UC_X86_REG_YMM23:
case UC_X86_REG_YMM24:
case UC_X86_REG_YMM25:
case UC_X86_REG_YMM26:
case UC_X86_REG_YMM27:
case UC_X86_REG_YMM28:
case UC_X86_REG_YMM29:
case UC_X86_REG_YMM30:
case UC_X86_REG_YMM31: {
CHECK_REG_TYPE(uint64_t[4]);
uint64_t *dst = (uint64_t *)value;
const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_YMM0];
dst[0] = reg->ZMM_Q(0);
dst[1] = reg->ZMM_Q(1);
dst[2] = reg->ZMM_Q(2);
dst[3] = reg->ZMM_Q(3);
break;
}
case UC_X86_REG_ZMM0:
case UC_X86_REG_ZMM1:
case UC_X86_REG_ZMM2:
case UC_X86_REG_ZMM3:
case UC_X86_REG_ZMM4:
case UC_X86_REG_ZMM5:
case UC_X86_REG_ZMM6:
case UC_X86_REG_ZMM7:
case UC_X86_REG_ZMM8:
case UC_X86_REG_ZMM9:
case UC_X86_REG_ZMM10:
case UC_X86_REG_ZMM11:
case UC_X86_REG_ZMM12:
case UC_X86_REG_ZMM13:
case UC_X86_REG_ZMM14:
case UC_X86_REG_ZMM15:
case UC_X86_REG_ZMM16:
case UC_X86_REG_ZMM17:
case UC_X86_REG_ZMM18:
case UC_X86_REG_ZMM19:
case UC_X86_REG_ZMM20:
case UC_X86_REG_ZMM21:
case UC_X86_REG_ZMM22:
case UC_X86_REG_ZMM23:
case UC_X86_REG_ZMM24:
case UC_X86_REG_ZMM25:
case UC_X86_REG_ZMM26:
case UC_X86_REG_ZMM27:
case UC_X86_REG_ZMM28:
case UC_X86_REG_ZMM29:
case UC_X86_REG_ZMM30:
case UC_X86_REG_ZMM31: {
CHECK_REG_TYPE(uint64_t[8]);
uint64_t *dst = (uint64_t *)value;
const ZMMReg *const reg = &env->xmm_regs[regid - UC_X86_REG_ZMM0];
dst[0] = reg->ZMM_Q(0);
dst[1] = reg->ZMM_Q(1);
dst[2] = reg->ZMM_Q(2);
dst[3] = reg->ZMM_Q(3);
dst[4] = reg->ZMM_Q(4);
dst[5] = reg->ZMM_Q(5);
dst[6] = reg->ZMM_Q(6);
dst[7] = reg->ZMM_Q(7);
break;
}
case UC_X86_REG_FS_BASE:
CHECK_REG_TYPE(uint64_t);
*(uint64_t *)value = (uint64_t)env->segs[R_FS].base;
@ -988,6 +1074,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
#endif
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -1045,9 +1132,9 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
case UC_X86_REG_XMM5:
case UC_X86_REG_XMM6:
case UC_X86_REG_XMM7: {
CHECK_REG_TYPE(float64[2]);
float64 *src = (float64 *)value;
ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0];
CHECK_REG_TYPE(uint64_t[2]);
const uint64_t *src = (const uint64_t *)value;
ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_XMM0];
reg->ZMM_Q(0) = src[0];
reg->ZMM_Q(1) = src[1];
return ret;
@ -1071,25 +1158,14 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
case UC_X86_REG_YMM4:
case UC_X86_REG_YMM5:
case UC_X86_REG_YMM6:
case UC_X86_REG_YMM7:
case UC_X86_REG_YMM8:
case UC_X86_REG_YMM9:
case UC_X86_REG_YMM10:
case UC_X86_REG_YMM11:
case UC_X86_REG_YMM12:
case UC_X86_REG_YMM13:
case UC_X86_REG_YMM14:
case UC_X86_REG_YMM15: {
CHECK_REG_TYPE(float64[4]);
float64 *src = (float64 *)value;
ZMMReg *lo_reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_YMM0];
XMMReg *hi_reg = &env->ymmh_regs[regid - UC_X86_REG_YMM0];
lo_reg->ZMM_Q(0) = src[0];
lo_reg->ZMM_Q(1) = src[1];
// YMM is not supported by QEMU at all
// As of qemu 5.0.1, ymmh_regs is nowhere used.
hi_reg->_d[0] = src[2];
hi_reg->_d[1] = src[3];
case UC_X86_REG_YMM7: {
CHECK_REG_TYPE(uint64_t[4]);
const uint64_t *src = (const uint64_t *)value;
ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_YMM0];
reg->ZMM_Q(0) = src[0];
reg->ZMM_Q(1) = src[1];
reg->ZMM_Q(2) = src[2];
reg->ZMM_Q(3) = src[3];
return ret;
}
@ -1155,6 +1231,8 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
goto write_cr;
case UC_X86_REG_CR1:
case UC_X86_REG_CR2:
CHECK_REG_TYPE(uint32_t);
goto write_cr;
case UC_X86_REG_CR3:
CHECK_REG_TYPE(uint32_t);
cpu_x86_update_cr3(env, *(uint32_t *)value);
@ -1395,6 +1473,8 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
goto write_cr64;
case UC_X86_REG_CR1:
case UC_X86_REG_CR2:
CHECK_REG_TYPE(uint64_t);
goto write_cr64;
case UC_X86_REG_CR3:
CHECK_REG_TYPE(uint64_t);
cpu_x86_update_cr3(env, *(uint32_t *)value);
@ -1402,6 +1482,9 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
case UC_X86_REG_CR4:
CHECK_REG_TYPE(uint64_t);
cpu_x86_update_cr4(env, *(uint32_t *)value);
goto write_cr64;
case UC_X86_REG_CR8:
CHECK_REG_TYPE(uint64_t);
write_cr64:
env->cr[regid - UC_X86_REG_CR0] = *(uint64_t *)value;
break;
@ -1786,14 +1869,108 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
case UC_X86_REG_XMM12:
case UC_X86_REG_XMM13:
case UC_X86_REG_XMM14:
case UC_X86_REG_XMM15: {
CHECK_REG_TYPE(float64[2]);
float64 *src = (float64 *)value;
ZMMReg *reg = (ZMMReg *)&env->xmm_regs[regid - UC_X86_REG_XMM0];
case UC_X86_REG_XMM15:
case UC_X86_REG_XMM16:
case UC_X86_REG_XMM17:
case UC_X86_REG_XMM18:
case UC_X86_REG_XMM19:
case UC_X86_REG_XMM20:
case UC_X86_REG_XMM21:
case UC_X86_REG_XMM22:
case UC_X86_REG_XMM23:
case UC_X86_REG_XMM24:
case UC_X86_REG_XMM25:
case UC_X86_REG_XMM26:
case UC_X86_REG_XMM27:
case UC_X86_REG_XMM28:
case UC_X86_REG_XMM29:
case UC_X86_REG_XMM30:
case UC_X86_REG_XMM31: {
CHECK_REG_TYPE(uint64_t[2]);
const uint64_t *src = (const uint64_t *)value;
ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_XMM0];
reg->ZMM_Q(0) = src[0];
reg->ZMM_Q(1) = src[1];
break;
}
case UC_X86_REG_YMM8:
case UC_X86_REG_YMM9:
case UC_X86_REG_YMM10:
case UC_X86_REG_YMM11:
case UC_X86_REG_YMM12:
case UC_X86_REG_YMM13:
case UC_X86_REG_YMM14:
case UC_X86_REG_YMM15:
case UC_X86_REG_YMM16:
case UC_X86_REG_YMM17:
case UC_X86_REG_YMM18:
case UC_X86_REG_YMM19:
case UC_X86_REG_YMM20:
case UC_X86_REG_YMM21:
case UC_X86_REG_YMM22:
case UC_X86_REG_YMM23:
case UC_X86_REG_YMM24:
case UC_X86_REG_YMM25:
case UC_X86_REG_YMM26:
case UC_X86_REG_YMM27:
case UC_X86_REG_YMM28:
case UC_X86_REG_YMM29:
case UC_X86_REG_YMM30:
case UC_X86_REG_YMM31: {
CHECK_REG_TYPE(uint64_t[4]);
const uint64_t *src = (const uint64_t *)value;
ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_YMM0];
reg->ZMM_Q(0) = src[0];
reg->ZMM_Q(1) = src[1];
reg->ZMM_Q(2) = src[2];
reg->ZMM_Q(3) = src[3];
break;
}
case UC_X86_REG_ZMM0:
case UC_X86_REG_ZMM1:
case UC_X86_REG_ZMM2:
case UC_X86_REG_ZMM3:
case UC_X86_REG_ZMM4:
case UC_X86_REG_ZMM5:
case UC_X86_REG_ZMM6:
case UC_X86_REG_ZMM7:
case UC_X86_REG_ZMM8:
case UC_X86_REG_ZMM9:
case UC_X86_REG_ZMM10:
case UC_X86_REG_ZMM11:
case UC_X86_REG_ZMM12:
case UC_X86_REG_ZMM13:
case UC_X86_REG_ZMM14:
case UC_X86_REG_ZMM15:
case UC_X86_REG_ZMM16:
case UC_X86_REG_ZMM17:
case UC_X86_REG_ZMM18:
case UC_X86_REG_ZMM19:
case UC_X86_REG_ZMM20:
case UC_X86_REG_ZMM21:
case UC_X86_REG_ZMM22:
case UC_X86_REG_ZMM23:
case UC_X86_REG_ZMM24:
case UC_X86_REG_ZMM25:
case UC_X86_REG_ZMM26:
case UC_X86_REG_ZMM27:
case UC_X86_REG_ZMM28:
case UC_X86_REG_ZMM29:
case UC_X86_REG_ZMM30:
case UC_X86_REG_ZMM31: {
CHECK_REG_TYPE(uint64_t[8]);
const uint64_t *src = (const uint64_t *)value;
ZMMReg *reg = &env->xmm_regs[regid - UC_X86_REG_ZMM0];
reg->ZMM_Q(0) = src[0];
reg->ZMM_Q(1) = src[1];
reg->ZMM_Q(2) = src[2];
reg->ZMM_Q(3) = src[3];
reg->ZMM_Q(4) = src[4];
reg->ZMM_Q(5) = src[5];
reg->ZMM_Q(6) = src[6];
reg->ZMM_Q(7) = src[7];
break;
}
case UC_X86_REG_FS_BASE:
CHECK_REG_TYPE(uint64_t);
env->segs[R_FS].base = *(uint64_t *)value;
@ -1807,6 +1984,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
#endif
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -77,6 +77,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -109,6 +110,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -100,6 +100,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -149,6 +150,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -204,6 +204,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -265,6 +266,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -126,6 +126,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -176,6 +177,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -80,7 +80,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
break;
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -113,6 +113,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
break;
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -78,6 +78,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
CHECK_REG_TYPE(uint32_t);
*(uint32_t *)value = env->regwptr[8 + regid - UC_SPARC_REG_L0];
} else if (regid >= UC_SPARC_REG_I0 && regid <= UC_SPARC_REG_I7) {
CHECK_REG_TYPE(uint32_t);
*(uint32_t *)value = env->regwptr[16 + regid - UC_SPARC_REG_I0];
} else {
switch (regid) {
@ -90,6 +91,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -125,6 +127,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -138,6 +138,7 @@ uc_err reg_read(void *_env, int mode, unsigned int regid, void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}
@ -241,6 +242,7 @@ uc_err reg_write(void *_env, int mode, unsigned int regid, const void *value,
}
}
CHECK_RET_DEPRECATE(ret, regid);
return ret;
}

View File

@ -154,4 +154,18 @@ static inline void uc_common_init(struct uc_struct* uc)
ret = UC_ERR_OK; \
} while(0)
#define CHECK_RET_DEPRECATE(ret, regid) do { \
if (ret == UC_ERR_ARG && !getenv("UC_IGNORE_REG_BREAK")) { \
fprintf(stderr, \
"WARNING: Your register accessing on id %"PRIu32" is deprecated " \
"and will get UC_ERR_ARG in the future release (2.2.0) because " \
"the accessing is either no-op or not defined. If you believe " \
"the register should be implemented or there is a bug, please " \
"submit an issue to https://github.com/unicorn-engine/unicorn. " \
"Set UC_IGNORE_REG_BREAK=1 to ignore this warning.\n", \
regid); \
ret = UC_ERR_OK; \
} \
} while (0)
#endif

View File

@ -1,93 +1,111 @@
from __future__ import print_function
from unicorn import *
from unicorn.arm_const import *
import regress
# code to be emulated
'''
ins = {
0x00008cd4: """
push {r11}
add r11, sp, #0
mov r3, pc
mov r0, r3
sub sp, r11, #0
pop {r11}
bx lr
""",
0x00008cf0: """
push {r11}
add r11, sp, #0
push {r6}
add r6, pc, $1
bx r6
.code 16
mov r3, pc
add r3, $0x4
push {r3}
pop {pc}
.code 32
pop {r6}
mov r0, r3
sub sp, r11, #0
pop {r11}
bx lr
""",
0x00008d20: """
push {r11}
add r11, sp, #0
mov r3, lr
mov r0, r3
sub sp, r11, #0
pop {r11}
bx lr
""",
0x00008d68: "bl 0x8cd4\n"
"mov r4, r0\n"
"bl 0x8cf0\n"
"mov r3, r0\n"
"add r4, r4, r3\n"
"bl 0x8d20\n"
"mov r3, r0\n"
"add r2, r4, r3",
}
'''
from unicorn import *
from unicorn.arm_const import *
MAIN_ADDRESS = 0x8d68
ADDRESS = MAIN_ADDRESS & ~(0x1000 - 1)
STACK_ADDR = ADDRESS + 0x1000
class BxTwiceTest(regress.RegressTest):
def runTest(self):
ADDRESS = 0x8000
MAIN_ADDRESS = 0x8d68
STACK_ADDR = ADDRESS + 0x1000
# code to be emulated
code = {
0x8cf0: '\x04\xb0-\xe5\x00\xb0\x8d\xe2\x04`-\xe5\x01`\x8f\xe2\x16\xff/\xe1{F\x03\xf1\x04\x03\x08\xb4\x00\xbd\x00\x00\x04`\x9d\xe4\x03\x00\xa0\xe1\x00\xd0K\xe2\x04\xb0\x9d\xe4\x1e\xff/\xe1',
0x8d20: '\x04\xb0-\xe5\x00\xb0\x8d\xe2\x0e0\xa0\xe1\x03\x00\xa0\xe1\x00\xd0K\xe2\x04\xb0\x9d\xe4\x1e\xff/\xe1',
0x8cd4: '\x04\xb0-\xe5\x00\xb0\x8d\xe2\x0f0\xa0\xe1\x03\x00\xa0\xe1\x00\xd0K\xe2\x04\xb0\x9d\xe4\x1e\xff/\xe1',
0x8d68: '\xd9\xff\xff\xeb\x00@\xa0\xe1\xde\xff\xff\xeb\x000\xa0\xe1\x03@\x84\xe0\xe7\xff\xff\xeb\x000\xa0\xe1\x03 \x84\xe0'
0x8cd4: (
b'\x04\xb0\x2d\xe5' # 8cd4 push {r11}
b'\x00\xb0\x8d\xe2' # 8cd8 add r11, sp, #0
b'\x0f\x30\xa0\xe1' # 8cdc mov r3, pc
b'\x03\x00\xa0\xe1' # 8ce0 mov r0, r3
b'\x00\xd0\x4b\xe2' # 8ce4 sub sp, r11, #0
b'\x04\xb0\x9d\xe4' # 8ce8 pop {r11}
b'\x1e\xff\x2f\xe1' # 8cec bx lr
),
0x8cf0: (
b'\x04\xb0\x2d\xe5' # 8cf0 push {r11}
b'\x00\xb0\x8d\xe2' # 8cf4 add r11, sp, #0
b'\x04\x60\x2d\xe5' # 8cf8 push {r6}
b'\x01\x60\x8f\xe2' # 8cfc add r6, pc, $1
b'\x16\xff\x2f\xe1' # 8d00 bx r6
# .thumb
b'\x7b\x46' # 8d04 mov r3, pc
b'\x03\xf1\x08\x03' # 8d06 add r3, $0x8 # elicn: used to be $0x4 but it kept failing
b'\x08\xb4' # 8d0a push {r3}
b'\x00\xbd' # 8d0c pop {pc}
b'\x00\x00' # 8d0e (alignment)
# .arm
b'\x04\x60\x9d\xe4' # 8d10 pop {r6}
b'\x03\x00\xa0\xe1' # 8d14 mov r0, r3
b'\x00\xd0\x4b\xe2' # 8d18 sub sp, r11, #0
b'\x04\xb0\x9d\xe4' # 8d1c pop {r11}
b'\x1e\xff\x2f\xe1' # 8d20 bx lr
),
0x8d24: ( # elicn: used to be 0x8d20 but it caused this block to overlap with the previous one
b'\x04\xb0\x2d\xe5' # 8d24 push {r11}
b'\x00\xb0\x8d\xe2' # 8d28 add r11, sp, #0
b'\x0e\x30\xa0\xe1' # 8d2c mov r3, lr
b'\x03\x00\xa0\xe1' # 8d20 mov r0, r3
b'\x00\xd0\x4b\xe2' # 8d34 sub sp, r11, #0
b'\x04\xb0\x9d\xe4' # 8d38 pop {r11}
b'\x1e\xff\x2f\xe1' # 8d3c bx lr
),
0x8d68: (
b'\xd9\xff\xff\xeb' # 8d68 bl 0x8cd4 <-- MAIN_ADDRESS
b'\x00\x40\xa0\xe1' # 8d6c mov r4, r0
b'\xde\xff\xff\xeb' # 8d70 bl 0x8cf0
b'\x00\x30\xa0\xe1' # 8d74 mov r3, r0
b'\x03\x40\x84\xe0' # 8d78 add r4, r4, r3
b'\xe8\xff\xff\xeb' # 8d7c bl 0x8d24
b'\x00\x30\xa0\xe1' # 8d80 mov r3, r0
b'\x03\x20\x84\xe0' # 8d84 add r2, r4, r3
)
}
try:
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# write machine code to be emulated to memory
for addr, c in code.items():
print("Writing chunk to 0x{:x}".format(addr))
mu.mem_write(addr, c)
mu.mem_map(ADDRESS, 0x1000)
# initialize machine registers
mu.reg_write(UC_ARM_REG_SP, STACK_ADDR)
# write machine code to be emulated to memory
for addr, c in code.items():
regress.logger.debug("Writing %d bytes to %#x", len(c), addr)
mu.mem_write(addr, c)
print("Starting emulation")
# initialize machine registers
mu.reg_write(UC_ARM_REG_PC, MAIN_ADDRESS)
mu.reg_write(UC_ARM_REG_SP, STACK_ADDR)
# emulate code in infinite time & unlimited instructions
mu.emu_start(MAIN_ADDRESS, MAIN_ADDRESS + len(code[MAIN_ADDRESS]))
regress.logger.debug("Starting emulation")
print("Emulation done")
# trace code only if we are debugging it
if regress.logger.isEnabledFor(regress.logging.DEBUG):
def __hook_code(uc, addr, size, _):
cpsr, r0, r3, r4, r6 = uc.reg_read_batch((
UC_ARM_REG_CPSR,
UC_ARM_REG_R0,
UC_ARM_REG_R3,
UC_ARM_REG_R4,
UC_ARM_REG_R6
))
r2 = mu.reg_read(UC_ARM_REG_R2)
print(">>> r2: 0x{:08x}".format(r2))
is_thumb = (cpsr >> 5) & 0b1
except UcError as e:
self.fail("ERROR: %s" % e)
opcode = uc.mem_read(addr, size).hex()
regress.logger.debug(
"%-2s PC = %#06x | opcode = %-8s [R0 = %#06x, R3 = %#06x, R4 = %#07x, R6 = %#06x]",
"T" if is_thumb else "", addr, opcode, r0, r3, r4, r6
)
mu.hook_add(UC_HOOK_CODE, __hook_code)
mu.emu_start(MAIN_ADDRESS, MAIN_ADDRESS + len(code[MAIN_ADDRESS]))
regress.logger.debug("Emulation done")
self.assertEqual(0x8ce4 + 0x8d10 + 0x8d80, mu.reg_read(UC_ARM_REG_R2))
if __name__ == '__main__':
regress.main()

View File

@ -1,28 +1,30 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.arm_const import *
import regress
class BxHang(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
uc.mem_map(0x1000, 0x1000)
uc.mem_write(0x1000, '1eff2f010000a0e1'.decode('hex')) # bxeq lr; mov r0, r0
uc.mem_write(0x1000, b'\x1e\xff\x2f\x01\x00\x00\xa0\xe1') # bxeq lr; mov r0, r0
uc.count = 0
def hook_block(uc, addr, *args):
print 'enter block 0x%04x' % addr
regress.logger.debug('enter block %#06x', addr)
uc.count += 1
uc.reg_write(UC_ARM_REG_LR, 0x1004)
uc.hook_add(UC_HOOK_BLOCK, hook_block)
print 'block should only run once'
regress.logger.debug('block should only run once')
uc.emu_start(0x1000, 0x1004)
self.assertEqual(uc.count, 1)
if __name__ == '__main__':
regress.main()

View File

@ -1,44 +1,41 @@
#!/usr/bin/python
# coding=utf8
# Added by Peter Mackay, relating to issue 571
# "ARM NEON/VFP support seems to exist but is disabled by default"
# https://github.com/unicorn-engine/unicorn/issues/571
import regress
from unicorn import *
from unicorn.arm_const import *
import regress
CODE = (
b'\x11\xEE\x50\x1F' # MRC p15, #0, r1, c1, c0, #2
b'\x41\xF4\x70\x01' # ORR r1, r1, #(0xf << 20)
b'\x01\xEE\x50\x1F' # MCR p15, #0, r1, c1, c0, #2
b'\x4F\xF0\x00\x01' # MOV r1, #0
b'\x07\xEE\x95\x1F' # MCR p15, #0, r1, c7, c5, #4
b'\x4F\xF0\x80\x40' # MOV r0,#0x40000000
b'\xE8\xEE\x10\x0A' # FMXR FPEXC, r0
b'\x2d\xed\x02\x8b' # vpush {d8}
)
BASE = 0x1000
class FpVfpDisabled(regress.RegressTest):
def runTest(self):
# MRC p15, #0, r1, c1, c0, #2
# ORR r1, r1, #(0xf << 20)
# MCR p15, #0, r1, c1, c0, #2
# MOV r1, #0
# MCR p15, #0, r1, c7, c5, #4
# MOV r0,#0x40000000
# FMXR FPEXC, r0
code = '11EE501F'
code += '41F47001'
code += '01EE501F'
code += '4FF00001'
code += '07EE951F'
code += '4FF08040'
code += 'E8EE100A'
# vpush {d8}
code += '2ded028b'
address = 0x1000
mem_size = 0x1000
code_bytes = code.decode('hex')
uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
uc.mem_map(address, mem_size)
uc.mem_write(address, code_bytes)
uc.reg_write(UC_ARM_REG_SP, address + mem_size)
uc.emu_start(address + 1, address + len(code_bytes))
uc.mem_map(BASE, mem_size)
uc.mem_write(BASE, CODE)
uc.reg_write(UC_ARM_REG_SP, BASE + mem_size - 4)
uc.emu_start(BASE + 1, BASE + len(CODE))
if __name__ == '__main__':
regress.main()

View File

@ -3,39 +3,44 @@
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
#
import regress
from __future__ import print_function
from unicorn import *
from unicorn.arm_const import *
# code to be emulated
ARM_CODE = "\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = "\x83\xb0" # sub sp, #0xc
ARM_CODE = (
b"\x37\x00\xa0\xe3" # mov r0, #0x37
b"\x03\x10\x42\xe0" # sub r1, r2, r3
)
THUMB_CODE = b"\x83\xb0" # sub sp, #0xc
# memory address where emulation starts
ADDRESS = 0xF0000000
ADDRESS = 0xF0000000
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
regress.logger.debug(">>> Tracing basic block at %#x, block size = %#x", address, size)
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size))
regress.logger.debug(">>> Tracing instruction at %#x, instruction size = %u", address, size)
# Test ARM
def test_arm():
print("Emulate ARM code")
try:
class TestInitInputCrash(regress.RegressTest):
def test_arm(self):
regress.logger.debug("Emulate ARM code")
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
mem_size = 2 * (1024 * 1024)
mu.mem_map(ADDRESS, mem_size)
stack_address = ADDRESS + mem_size
stack_size = stack_address # >>> here huge memory size
mu.mem_map(stack_address, stack_size)
@ -58,20 +63,16 @@ def test_arm():
mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
regress.logger.debug(">>> Emulation done. Below is the CPU context")
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
print(">>> R0 = 0x%x" %r0)
print(">>> R1 = 0x%x" %r1)
regress.logger.debug(">>> R0 = %#x", r0)
regress.logger.debug(">>> R1 = %#x", r1)
except UcError as e:
print("ERROR: %s" % e)
def test_thumb(self):
regress.logger.debug("Emulate THUMB code")
def test_thumb():
print("Emulate THUMB code")
try:
# Initialize emulator in thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
@ -91,19 +92,14 @@ def test_thumb():
mu.hook_add(UC_HOOK_CODE, hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(THUMB_CODE))
mu.emu_start(ADDRESS | 0b1, ADDRESS + len(THUMB_CODE))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
regress.logger.debug(">>> Emulation done. Below is the CPU context")
sp = mu.reg_read(UC_ARM_REG_SP)
print(">>> SP = 0x%x" %sp)
except UcError as e:
print("ERROR: %s" % e)
regress.logger.debug(">>> SP = %#x", sp)
if __name__ == '__main__':
test_arm()
print("=" * 20)
test_thumb()
regress.main()

View File

@ -1,52 +1,76 @@
import regress
from unicorn import *
from unicorn.arm_const import *
# .text:0001F894 ADD PC, PC, R3
# .text:0001F898 ; ---------------------------------------------------------------------------
# .text:0001F898 VLD1.8 {D0}, [R1]!
# .text:0001F89C VST1.8 {D0}, [R12]!
# .text:0001F8A0 VLD1.8 {D0}, [R1]!
# .text:0001F8A4 VST1.8 {D0}, [R12]!
# .text:0001F8A8 VLD1.8 {D0}, [R1]!
# .text:0001F8AC VST1.8 {D0}, [R12]!
# .text:0001F8B0 VLD1.8 {D0}, [R1]!
# .text:0001F8B4 VST1.8 {D0}, [R12]!
# .text:0001F8B8 VLD1.8 {D0}, [R1]!
# .text:0001F8BC VST1.8 {D0}, [R12]!
# .text:0001F8C0 VLD1.8 {D0}, [R1]!
# .text:0001F8C4 VST1.8 {D0}, [R12]!
# .text:0001F8C8 VLD1.8 {D0}, [R1]!
# .text:0001F8CC VST1.8 {D0}, [R12]!
# .text:0001F8D0 TST R2, #4
# .text:0001F8D4 LDRNE R3, [R1],#4
# .text:0001F8D8 STRNE R3, [R12],#4
# .text:0001F8DC MOVS R2, R2,LSL#31
# .text:0001F8E0 LDRHCS R3, [R1],#2
# .text:0001F8E4 LDRBNE R1, [R1]
# .text:0001F8E8 STRHCS R3, [R12],#2
# .text:0001F8EC STRBNE R1, [R12]
shellcode = [0x3, 0xf0, 0x8f, 0xe0, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0x4, 0x0, 0x12, 0xe3, 0x4, 0x30, 0x91, 0x14, 0x4, 0x30, 0x8c, 0x14, 0x82, 0x2f, 0xb0, 0xe1, 0xb2, 0x30, 0xd1, 0x20, 0x0, 0x10, 0xd1, 0x15, 0xb2, 0x30, 0xcc, 0x20, 0x0, 0x10, 0xcc, 0x15]
base = 0x1F894
from_address = 0x1000
to_address = 0x2000
cplen = 8
bs = b"c8"*cplen
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
uc.mem_map(from_address, 0x1000)
uc.mem_map(to_address, 0x1000)
uc.mem_map(0x1F000, 0x1000)
uc.mem_write(from_address, bs)
uc.mem_write(base, bytes(shellcode))
uc.reg_write(UC_ARM_REG_R12, to_address)
uc.reg_write(UC_ARM_REG_R1, from_address)
uc.reg_write(UC_ARM_REG_R2, cplen)
uc.reg_write(UC_ARM_REG_R3, 0x24)
# enable_vfp
uc.reg_write(UC_ARM_REG_C1_C0_2, uc.reg_read(UC_ARM_REG_C1_C0_2) | (0xf << 20))
uc.reg_write(UC_ARM_REG_FPEXC, 0x40000000)
SHELLCODE = bytes.fromhex(
'03 f0 8f e0' # 0001F894 ADD PC, PC, R3
'0d 07 21 f4' # 0001F898 VLD1.8 {D0}, [R1]!
'0d 07 0c f4' # 0001F89C VST1.8 {D0}, [R12]!
'0d 07 21 f4' # 0001F8A0 VLD1.8 {D0}, [R1]!
'0d 07 0c f4' # 0001F8A4 VST1.8 {D0}, [R12]!
'0d 07 21 f4' # 0001F8A8 VLD1.8 {D0}, [R1]!
'0d 07 0c f4' # 0001F8AC VST1.8 {D0}, [R12]!
'0d 07 21 f4' # 0001F8B0 VLD1.8 {D0}, [R1]!
'0d 07 0c f4' # 0001F8B4 VST1.8 {D0}, [R12]!
'0d 07 21 f4' # 0001F8B8 VLD1.8 {D0}, [R1]!
'0d 07 0c f4' # 0001F8BC VST1.8 {D0}, [R12]!
'0d 07 21 f4' # 0001F8C0 VLD1.8 {D0}, [R1]!
'0d 07 0c f4' # 0001F8C4 VST1.8 {D0}, [R12]!
'0d 07 21 f4' # 0001F8C8 VLD1.8 {D0}, [R1]!
'0d 07 0c f4' # 0001F8CC VST1.8 {D0}, [R12]!
'04 00 12 e3' # 0001F8D0 TST R2, #4
'04 30 91 14' # 0001F8D4 LDRNE R3, [R1],#4
'04 30 8c 14' # 0001F8D8 STRNE R3, [R12],#4
'82 2f b0 e1' # 0001F8DC MOVS R2, R2,LSL#31
'b2 30 d1 20' # 0001F8E0 LDRHCS R3, [R1],#2
'00 10 d1 15' # 0001F8E4 LDRBNE R1, [R1]
'b2 30 cc 20' # 0001F8E8 STRHCS R3, [R12],#2
'00 10 cc 15' # 0001F8EC STRBNE R1, [R12]
)
uc.emu_start(base, base+len(shellcode))
fr = uc.mem_read(from_address, len(bs))
to = uc.mem_read(to_address, len(bs))
print(f"memcpy result:\nfrom: {bytes(fr)}\nto: {bytes(to)}")
BASE = 0x1F894
COPY_SRC = 0x1000
COPY_DST = 0x2000
COPY_LEN = 8
DATA = b'c8' * COPY_LEN
class ArmMemcpy(regress.RegressTest):
def test_arm_memcpy(self):
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
uc.mem_map(COPY_SRC, 0x1000)
uc.mem_map(COPY_DST, 0x1000)
uc.mem_map(BASE & ~(0x1000 - 1), 0x1000)
uc.mem_write(COPY_SRC, DATA)
uc.mem_write(BASE, bytes(SHELLCODE))
uc.reg_write_batch((
(UC_ARM_REG_R12, COPY_DST),
(UC_ARM_REG_R1, COPY_SRC),
(UC_ARM_REG_R2, COPY_LEN),
(UC_ARM_REG_R3, 0x24)
))
# enable_vfp
# coproc=15, is64=0, sec=0, CRn=1, CRm=0, opc1=0, opc2=2
CPACR = (15, 0, 0, 1, 0, 0, 2)
cpacr = uc.reg_read(UC_ARM_REG_CP_REG, CPACR)
uc.reg_write(UC_ARM_REG_CP_REG, CPACR + (cpacr | (0b11 << 20) | (0b11 << 22),))
uc.reg_write(UC_ARM_REG_FPEXC, (0b1 << 30))
uc.emu_start(BASE, BASE + len(SHELLCODE))
src = uc.mem_read(COPY_SRC, len(DATA))
dst = uc.mem_read(COPY_DST, len(DATA))
self.assertEqual(DATA, src)
self.assertEqual(DATA, dst)
if __name__ == '__main__':
regress.main()

View File

@ -1,23 +1,24 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.arm_const import *
import regress
class MovHang(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
uc.mem_map(0x1000, 0x1000)
uc.mem_write(0x1000, '00c000e3'.decode('hex')) # movw r12, #0
uc.mem_write(0x1000, b'\x00\xc0\x00\xe3') # movw r12, #0
def hook_block(uc, addr, *args):
print 'enter block 0x%04x' % addr
regress.logger.debug('enter block 0x%#06x', addr)
uc.count += 1
uc.reg_write(UC_ARM_REG_R12, 0x123)
self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x123)
self.assertEqual(0x123, uc.reg_read(UC_ARM_REG_R12))
uc.hook_add(UC_HOOK_BLOCK, hook_block)
uc.count = 0
@ -25,8 +26,9 @@ class MovHang(regress.RegressTest):
#print 'block should only run once'
uc.emu_start(0x1000, 0x1004, timeout=500)
self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x0)
self.assertEquals(uc.count, 1)
self.assertEqual(0x0, uc.reg_read(UC_ARM_REG_R12))
self.assertEqual(uc.count, 1)
if __name__ == '__main__':
regress.main()

View File

@ -1,18 +1,24 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.arm_const import *
import regress
class VldrPcInsn(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
uc.mem_map(0x1000, 0x1000)
uc.mem_write(0x1000, 'ed9f8a3d'.decode('hex')) # vldr s16, [pc, #244]
# this will raise invalid insn
uc.emu_start(0x1000, 0x1004)
uc.mem_write(0x1000, b'\xed\x9f\x8a\x3d') # vldr s16, [pc, #244]
with self.assertRaises(UcError) as ex:
uc.emu_start(0x1000, 0x1004)
self.assertEqual(UC_ERR_INSN_INVALID, ex.exception.errno)
if __name__ == '__main__':
regress.main()

View File

@ -1,15 +1,28 @@
import regress
from unicorn import *
from unicorn.arm_const import *
# ADD R0, R10, R0;
# B L0;
# L0:
# ADD R0, R10, R0; <--- we stop at here, the first instruction of the next TB.
code = b'\x00\x00\x8a\xe0\xff\xff\xff\xea\x00\x00\x8a\xe0'
address = 0x1000
CODE = (
b'\x00\x00\x8a\xe0' # ADD R0, R10, R0
b'\xff\xff\xff\xea' # B L0
b'\x00\x00\x8a\xe0' # L0: ADD R0, R10, R0 <-- we stop here, the first instruction of the next TB
)
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
mu.mem_map(address, 0x1000)
mu.mem_write(address, code)
mu.emu_start(address, address + len(code) - 4)
BASE = 0x1000
class TestARMFirstInsn(regress.RegressTest):
def runTest(self):
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, CODE)
mu.emu_start(BASE, BASE + len(CODE) - 4)
if __name__ == '__main__':
regress.main()

View File

@ -1,29 +1,33 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.x86_const import *
import regress
class Hang(regress.RegressTest):
def runTest(self):
PAGE_SIZE = 0x5000
CODE_ADDR = 0x400000
RSP_ADDR = 0x200000
binary1 = "\xCA\x24\x5D" # retf 0x5d24
CODE = b"\xCA\x24\x5D" # retf 0x5d24
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(CODE_ADDR, PAGE_SIZE)
mu.mem_map(RSP_ADDR, PAGE_SIZE)
mu.mem_write(CODE_ADDR, binary1)
mu.mem_write(CODE_ADDR, CODE)
mu.reg_write(UC_X86_REG_RSP, RSP_ADDR)
try:
self.assertEqual(mu.emu_start(CODE_ADDR, CODE_ADDR + PAGE_SIZE, 0), UC_ERR_FETCH_INVALID)
except UcError as e:
print("ERROR: %s" % e)
# make sure we bump into an exception
with self.assertRaises(UcError) as raisedEx:
mu.emu_start(CODE_ADDR, CODE_ADDR + PAGE_SIZE)
# make sure it is an exception with the errno we expect
self.assertEqual(raisedEx.exception.errno, UC_ERR_READ_UNMAPPED)
if __name__ == '__main__':

View File

@ -3,88 +3,85 @@
# reg_write() can't modify PC from within trace callbacks
# issue #210
from __future__ import print_function
import regress
from unicorn import *
from unicorn.arm_const import *
import regress
BASE_ADDRESS = 0x10000000
THUMB_CODE = b"\x83\xb0" * 5 # sub sp, #0xc
TARGET_PC = 0xffffffff
# sub sp, #0xc
THUMB_CODE = "\x83\xb0" * 5
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" % (address, size))
mu = user_data
print(">>> Setting PC to 0xffffffff")
mu.reg_write(UC_ARM_REG_PC, 0xffffffff)
# callback for tracing basic blocks
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
mu = user_data
print(">>> Setting PC to 0xffffffff")
mu.reg_write(UC_ARM_REG_PC, 0xffffffff)
class CallBackPCTest(regress.RegressTest):
def test_instruction_trace(self):
try:
# initialize emulator in ARM's Thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
# initialize emulator in ARM's Thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
# map some memory
mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024)
# map some memory
mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(BASE_ADDRESS, THUMB_CODE)
# write machine code to be emulated to memory
mu.mem_write(BASE_ADDRESS, THUMB_CODE)
# setup stack
mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024)
# setup stack
mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, user_data=mu)
def __hook_callback(uc, address, size, user_data):
regress.logger.debug(">>> Tracing instruction at 0x%x, instruction size = %u", address, size)
regress.logger.debug(">>> Setting PC to %#x", TARGET_PC)
# emulate one instruction
uc.reg_write(UC_ARM_REG_PC, TARGET_PC)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, __hook_callback)
# emulate one instruction. since pc was modified to point to an unmapped
# area, this is expected to fail
with self.assertRaises(UcError) as raisedEx:
mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(THUMB_CODE), count=1)
# the instruction trace callback set PC to 0xffffffff, so at this
# point, the PC value should be 0xffffffff.
pc = mu.reg_read(UC_ARM_REG_PC)
self.assertEqual(pc, 0xffffffff, "PC not set to 0xffffffff by instruction trace callback")
self.assertEqual(UC_ERR_FETCH_UNMAPPED, raisedEx.exception.errno)
except UcError as e:
self.assertFalse(0, "ERROR: %s" % e)
# the block callback set PC to 0xffffffff, so at this point, the PC
# value should be 0xffffffff.
self.assertEqual(TARGET_PC, mu.reg_read(UC_ARM_REG_PC) | 0b1)
def test_block_trace(self):
try:
# initialize emulator in ARM's Thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
# initialize emulator in ARM's Thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
# map some memory
mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024)
# map some memory
mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(BASE_ADDRESS, THUMB_CODE)
# write machine code to be emulated to memory
mu.mem_write(BASE_ADDRESS, THUMB_CODE)
# setup stack
mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024)
# setup stack
mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024)
# trace blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block, user_data=mu)
def __hook_callback(uc, address, size, user_data):
regress.logger.debug(">>> Tracing basic block at 0x%x, block size = 0x%x", address, size)
regress.logger.debug(">>> Setting PC to %#x", TARGET_PC)
# emulate one instruction
uc.reg_write(UC_ARM_REG_PC, TARGET_PC)
# trace blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, __hook_callback)
# emulate one instruction. since pc was modified to point to an unmapped
# area, this is expected to fail
with self.assertRaises(UcError) as raisedEx:
mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(THUMB_CODE), count=1)
# the block callback set PC to 0xffffffff, so at this point, the PC
# value should be 0xffffffff.
pc = mu.reg_read(UC_ARM_REG_PC)
self.assertEqual(pc, 0xffffffff, "PC not set to 0xffffffff by block callback")
self.assertEqual(UC_ERR_FETCH_UNMAPPED, raisedEx.exception.errno)
# the block callback set PC to 0xffffffff, so at this point, the PC
# value should be 0xffffffff.
self.assertEqual(TARGET_PC, mu.reg_read(UC_ARM_REG_PC) | 0b1)
except UcError as e:
self.assertFalse(0, "ERROR: %s" % e)
if __name__ == '__main__':
regress.main()

177
tests/regress/core_ctl.py Normal file
View File

@ -0,0 +1,177 @@
#!/usr/bin/env python
import regress
from unicorn import *
from unicorn.arm_const import *
from unicorn.x86_const import *
# count down from maxint to zero
_VALID_CODE = (
b'\x31\xc9' # xor ecx, ecx
b'\x49' # _top: dec ecx
b'\xf3\x90' # pause
b'\xe3\xfb' # jecxz _top
b'\xf4' # _end: hlt
)
_INVALID_CODE = (
b'\xff\xff' # (invalid)
)
CODE = _VALID_CODE + _INVALID_CODE
BASE = 0x100000
class TestCtl(regress.RegressTest):
def test_mode(self):
uc = Uc(UC_ARCH_ARM, UC_MODE_BIG_ENDIAN)
# changing cpu model to one that supports EB
uc.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_M0)
# changing cpu model to one that does not support EB; this is expected to fail
with self.assertRaises(UcError) as ex:
uc.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_A8)
self.assertEqual(UC_ERR_ARG, ex.exception.errno)
# make sure we stay with the configuration we set beforehand
self.assertEqual(UC_ARCH_ARM, uc.ctl_get_arch())
self.assertEqual(UC_MODE_BIG_ENDIAN, uc.ctl_get_mode())
self.assertEqual(UC_CPU_ARM_CORTEX_M0, uc.ctl_get_cpu_model())
def test_page_size(self):
SIZE_4KB = 4 * 1024 ** 1
SIZE_2MB = 2 * 1024 ** 2
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# set page size to 2 MiB; this should work
uc.ctl_set_page_size(SIZE_2MB)
# BUG! was it set properly?
# self.assertEqual(SIZE_2MB, uc.ctl_get_page_size())
# set a page size which is not a power of 2
with self.assertRaises(UcError) as ex:
uc.ctl_set_page_size(SIZE_2MB + 0xbad)
self.assertEqual(UC_ERR_ARG, ex.exception.errno)
# BUG! are we still with the valid value?
# self.assertEqual(SIZE_2MB, uc.ctl_get_page_size())
# force uc to complete its initialization by triggering a random api
uc.ctl_flush_tb()
# set a page size after uc has completed its initialization
with self.assertRaises(UcError) as ex:
uc.ctl_set_page_size(SIZE_4KB)
self.assertEqual(UC_ERR_ARG, ex.exception.errno)
# BUG! are we still with the valid value?
# self.assertEqual(SIZE_2MB, uc.ctl_get_page_size())
def test_timeout(self):
MILLIS_1S = 1000
uc = Uc(UC_ARCH_X86, UC_MODE_32)
uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)
# start a long emulation bound by timeout
uc.emu_start(BASE, BASE + len(CODE), timeout=MILLIS_1S)
# was timeout set properly? uc returns timeout in nanoseconds
self.assertEqual(MILLIS_1S * 1000, uc.ctl_get_timeout())
# no way we made it all the way down to zero
self.assertNotEqual(0, uc.reg_read(UC_X86_REG_ECX))
def test_exits(self):
WRONG_EXIT = BASE + len(CODE)
GOOD_EXIT = BASE + len(_VALID_CODE)
uc = Uc(UC_ARCH_X86, UC_MODE_32)
uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)
def __hook_code(uc, *args):
ecx = uc.reg_read(UC_X86_REG_ECX)
# 16 down to the count
if ecx == 0xfffffff0:
# fast-forward the counter and let it run for another 16 iterations
uc.reg_write(UC_X86_REG_ECX, 0x10)
uc.hook_add(UC_HOOK_CODE, __hook_code)
# enable exits
uc.ctl_exits_enabled(True)
# fix the exit point so we don't reach invalid code
uc.ctl_set_exits([GOOD_EXIT])
# start emulation, setting a wrong stopping point that will get us to invalid code
# since we enabled exits, the stopping point should be ignored
uc.emu_start(BASE, WRONG_EXIT, count=1)
# only one exit point was set
self.assertEqual(1, uc.ctl_get_exits_cnt())
# only one exit point, and it is the wrong one
self.assertSequenceEqual([GOOD_EXIT], uc.ctl_get_exits(), int)
# resume execution; rely on code hook to fast-forward the emulation
# stopping point is ignored due to enabled exits
uc.emu_start(BASE, WRONG_EXIT)
# did we exit on the right place?
# not failing on an invalid instruction is another good indication for that
self.assertEqual(GOOD_EXIT, uc.reg_read(UC_X86_REG_EIP))
def test_tlb_mode(self):
MAPPING_LO = 0x0000000001000000
MAPPING_HI = 0x0010000000000000
NOPSLED = b'\x90' * 8
uc = Uc(UC_ARCH_X86, UC_MODE_64)
uc.mem_map(MAPPING_LO, 0x1000)
uc.mem_write(MAPPING_LO, NOPSLED)
uc.emu_start(MAPPING_LO, MAPPING_LO + len(NOPSLED))
# # we should be fine here
# uc.emu_start(BASE, BASE + len(_VALID_CODE), count=1)
uc.mem_map(MAPPING_HI, 0x1000)
uc.mem_write(MAPPING_HI, NOPSLED)
# this should prevents us from mapping to high addresses
uc.ctl_tlb_mode(UC_TLB_CPU)
# this should fail
with self.assertRaises(UcError) as ex:
uc.emu_start(MAPPING_HI, MAPPING_HI + len(NOPSLED))
self.assertEqual(UC_ERR_FETCH_UNMAPPED, ex.exception.errno)
# ------------------------------------------------------
# this should allow us mapping to high addresses
uc.ctl_tlb_mode(UC_TLB_VIRTUAL)
# this should ok now
uc.emu_start(MAPPING_HI, MAPPING_HI + len(NOPSLED))
if __name__ == '__main__':
regress.main()

View File

@ -1,6 +1,5 @@
#!/usr/bin/python
from __future__ import print_function
import binascii
import regress
@ -8,21 +7,18 @@ from unicorn import *
from unicorn.x86_const import *
CODE = binascii.unhexlify(b"".join([
b"8B 74 01 28", # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000
b"03 F0", # add esi, eax 0x1004
b"8D 45 FC", # lea eax, dword ptr [ebp - 4] 0x1006
b"50", # push eax 0x1009
b"6A 40", # push 0x40 0x100A
b"6A 10", # push 0x10 0x100C
b"56", # push esi 0x100E
b"FF 15 20 20 00 10" # call some address 0x100F
]).replace(" ", ""))
CODE = binascii.unhexlify((
"8B 74 01 28" # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000
"03 F0" # add esi, eax 0x1004
"8D 45 FC" # lea eax, dword ptr [ebp - 4] 0x1006
"50" # push eax 0x1009
"6A 40" # push 0x40 0x100A
"6A 10" # push 0x10 0x100C
"56" # push esi 0x100E
).replace(' ', ''))
def showpc(mu):
pc = mu.reg_read(UC_X86_REG_EIP)
print("pc: 0x%x" % (pc))
BASE = 0x1000
STACK = 0x4000
class HookCodeStopEmuTest(regress.RegressTest):
@ -30,38 +26,32 @@ class HookCodeStopEmuTest(regress.RegressTest):
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# base of CODE
mu.mem_map(0x1000, 0x1000)
mu.mem_write(0x1000, CODE)
mu.reg_write(UC_X86_REG_EIP, 0x1000)
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, CODE)
# base of STACK
mu.mem_map(0x4000, 0x4000)
mu.mem_write(0x4000, "\x00" * 0x4000)
mu.reg_write(UC_X86_REG_ESP, 0x6000)
mu.reg_write(UC_X86_REG_EBP, 0x6000)
mu.mem_map(STACK, 0x1000)
mu.mem_write(STACK, b"\x00" * 0x1000)
mu.reg_write(UC_X86_REG_EIP, BASE)
mu.reg_write(UC_X86_REG_ESP, STACK + 0x1000 - 8)
mu.reg_write(UC_X86_REG_EBP, STACK + 0x1000 - 8)
mu.reg_write(UC_X86_REG_ECX, 0x0)
mu.reg_write(UC_X86_REG_EAX, 0x0)
def _hook(_, access, address, length, value, context):
pc = mu.reg_read(UC_X86_REG_EIP)
print("mem unmapped: pc: %x access: %x address: %x length: %x value: %x" % (
pc, access, address, length, value))
mu.emu_stop()
return True
mu.hook_add(UC_HOOK_MEM_UNMAPPED, _hook)
# we only expect the following instruction to execute,
# and it will fail, because it accesses unmapped memory.
# mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000
mu.emu_start(0x1000, 0x100F)
showpc(mu)
with self.assertRaises(UcError) as ex:
mu.emu_start(BASE, BASE + len(CODE), count=1)
self.assertEqual(UC_ERR_READ_UNMAPPED, ex.exception.errno)
regress.logger.debug("pc: %#x", mu.reg_read(UC_X86_REG_EIP))
# now, we want to reuse the emulator, and keep executing
# from the next instruction
mu.reg_write(UC_X86_REG_EIP, 0x1004)
self.assertEqual(0x1004, mu.reg_read(UC_X86_REG_EIP))
# we expect the following instructions to execute
# add esi, eax 0x1004
@ -70,10 +60,9 @@ class HookCodeStopEmuTest(regress.RegressTest):
# push 0x40 0x100A
# push 0x10 0x100C
# push esi 0x100E
#
# currently, a UC_ERR_READ_UNMAPPED exception is raised here
mu.emu_start(0x1004, 0x100F)
showpc(mu)
mu.emu_start(BASE + 0x4, BASE + len(CODE))
regress.logger.debug("pc: %#x", mu.reg_read(UC_X86_REG_EIP))
if __name__ == '__main__':

View File

@ -7,9 +7,19 @@ are included in the bindings by the script for autogenerating mappings for
constants.
"""
import regress
import unicorn
try:
unicorn.UC_HOOK_MEM_UNMAPPED
except AttributeError:
assert(False and "Definition for UC_HOOK_MEM_UNMAPPED not generated")
class TestTypedefs(regress.RegressTest):
def runTest(self):
prop = 'UC_HOOK_MEM_UNMAPPED'
try:
getattr(unicorn, prop)
except AttributeError:
self.fail("Definition for %s not generated" % prop)
if __name__ == '__main__':
regress.main()

View File

@ -1,69 +1,62 @@
#!/usr/bin/python
from unicorn import *
from unicorn.x86_const import *
from capstone import *
import regress
ESP = 0x2000
PAGE_SIZE = 2 * 1024 * 1024
from unicorn import *
from unicorn.x86_const import *
from capstone import Cs, CS_ARCH_X86, CS_ARCH_X86, CS_MODE_64, CS_MODE_32
# mov [esp], DWORD 0x37f
# fldcw [esp]
# fnop
# fnstenv [esp + 8]
# pop ecx
CODE = b'\xc7\x04\x24\x7f\x03\x00\x00\xd9\x2c\x24\xd9\xd0\xd9\x74\x24\x08\x59'
class SimpleEngine:
def __init__(self):
self.capmd = Cs(CS_ARCH_X86, CS_MODE_32)
CODE = (
b'\xc7\x04\x24\x7f\x03\x00\x00' # mov DWORD PTR [rsp],0x37f
b'\xd9\x2c\x24' # fldcw WORD PTR [rsp]
b'\xd9\xd0' # fnop
b'\xd9\x74\x24\x08' # fnstenv [rsp+0x8]
b'\x59' # pop rcx
)
def disas_single(self, data):
for i in self.capmd.disasm(data, 16):
print("\t%s\t%s" % (i.mnemonic, i.op_str))
break
BASE = 0x00000000
STACK = 0x00000f00
disasm = SimpleEngine()
def hook_code(uc, addr, size, user_data):
mem = uc.mem_read(addr, size)
print(" 0x%X:" % (addr)),
disasm.disas_single(bytes(mem))
cs = user_data
data = uc.mem_read(addr, size)
mnem, ops = next((insn.mnemonic, insn.op_str) for insn in cs.disasm(data, addr))
regress.logger.debug("0x%x: %-12s %-24s", addr, mnem, ops)
class FpuIP(regress.RegressTest):
def mem_reader(self, mu, addr, size, expected):
tmp = mu.mem_read(addr, size)
for out, exp in zip(tmp, expected):
self.assertEqual(exp, out)
def test_32(self):
mu = Uc(UC_ARCH_X86, UC_MODE_32)
cs = Cs(CS_ARCH_X86, CS_MODE_32)
mu.mem_map(0x0, PAGE_SIZE)
mu.mem_write(0x4000, CODE)
mu.reg_write(UC_X86_REG_ESP, ESP)
mu.hook_add(UC_HOOK_CODE, hook_code)
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, CODE)
mu.reg_write(UC_X86_REG_ESP, STACK)
mu.hook_add(UC_HOOK_CODE, hook_code, cs)
mu.emu_start(0x4000, 0, 0, 5)
esp = mu.reg_read(UC_X86_REG_ESP)
self.assertEqual(0x2004, esp)
expected = [0x0, 0x0, 0xa, 0x40]
self.mem_reader(mu, esp + 14, 4, expected)
mu.emu_start(BASE, BASE + len(CODE), count=5)
self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8))
self.assertSequenceEqual(b'\x55\x55\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 16, 8))
def test_64(self):
mu = Uc(UC_ARCH_X86, UC_MODE_64)
cs = Cs(CS_ARCH_X86, CS_MODE_64)
mu.mem_map(0x0, PAGE_SIZE)
mu.mem_write(0x4000, CODE)
mu.reg_write(UC_X86_REG_ESP, ESP)
mu.hook_add(UC_HOOK_CODE, hook_code)
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, CODE)
mu.reg_write(UC_X86_REG_RSP, STACK)
mu.hook_add(UC_HOOK_CODE, hook_code, cs)
mu.emu_start(BASE, BASE + len(CODE), count=5)
self.assertSequenceEqual(b'\x7f\x03\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 8, 8))
self.assertSequenceEqual(b'\x55\x55\x00\x00\x00\x00\x00\x00', mu.mem_read(STACK + 16, 8))
mu.emu_start(0x4000, 0, 0, 5)
rsp = mu.reg_read(UC_X86_REG_RSP)
self.assertEqual(0x2012, rsp + 10)
expected = [0x0, 0x0, 0xa, 0x40, 0x0, 0x0, 0x0, 0x0]
self.mem_reader(mu, rsp + 10, 4, expected)
if __name__ == '__main__':
regress.main()

View File

@ -1,38 +1,40 @@
#!/usr/bin/python
from unicorn import *
from unicorn.x86_const import *
import regress
ESP = 0x2000
PAGE_SIZE = 1 * 1024 * 1024
from unicorn import *
from unicorn.x86_const import *
CODE = (
b'\x9b\xd9\x3c\x24' # fstcw WORD PTR [esp]
b'\x59' # pop ecx
)
BASE = 0x00000000
STACK = 0x00000f00
# wait
# fnstcw word ptr [esp]
# pop ecx
CODE = b'\x9B\xD9\x3C\x24\x59'
def hook_mem_write(uc, access, address, size, value, user_data):
print("mem WRITE: 0x%x, data size = %u, data value = 0x%x" % (address, size, value))
regress.logger.debug("mem WRITE to: %#x, size = %u, value = %#x", address, size, value)
return True
class FpuWrite(regress.RegressTest):
def mem_reader(self, mu, addr, size, expected):
tmp = mu.mem_read(addr, size)
for i, e in zip(tmp, expected):
self.assertEquals(e, i)
def runTest(self):
mu = Uc(UC_ARCH_X86, UC_MODE_32)
mu.mem_map(0, PAGE_SIZE)
mu.mem_write(0, CODE)
mu.reg_write(UC_X86_REG_ESP, ESP)
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, CODE)
mu.reg_write(UC_X86_REG_ESP, STACK)
mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write)
mu.emu_start(0x0, 5, 0, 2)
esp = mu.reg_read(UC_X86_REG_ESP)
self.mem_reader(mu, esp, 10, [0] * 10)
mu.emu_start(BASE, BASE + len(CODE), count=2)
self.assertSequenceEqual(b'\x00' * 2, mu.mem_read(STACK, 2))
if __name__ == '__main__':
regress.main()

View File

@ -1,39 +1,71 @@
#!/usr/bin/python
from __future__ import print_function
import binascii
import regress
from unicorn import *
from unicorn.x86_const import *
import regress
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
tmp = uc.mem_read(address, size)
print("[0x%x] =" %(address), end="")
for i in tmp:
print(" %02x" %i, end="")
print("")
# invalid instruction?
if size == 0xf1f1f1f1:
return
regress.logger.debug("[%#x] = %s" , address, binascii.hexlify(uc.mem_read(address, size)))
# callback for tracing Linux interrupt
def hook_intr(uc, intno, user_data):
# only handle Linux syscall
rip = uc.reg_read(UC_X86_REG_RIP)
if intno != 0x80:
print("=== 0x%x: got interrupt %x, quit" %(rip, intno))
uc.emu_stop()
return
eax = uc.reg_read(UC_X86_REG_EAX)
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(rip, intno, eax))
regress.logger.debug("[%#x]: got interrupt %#x", rip, intno)
regress.logger.debug(" EAX = %#010x", uc.reg_read(UC_X86_REG_EAX))
regress.logger.debug(" EBX = %#010x", uc.reg_read(UC_X86_REG_EBX))
regress.logger.debug(" ECX = %#010x", uc.reg_read(UC_X86_REG_ECX))
regress.logger.debug(" EDX = %#010x", uc.reg_read(UC_X86_REG_EDX))
uc.emu_stop()
class Hang(regress.RegressTest):
def runTest(self):
binary1 = b'\xeb\x1c\x5a\x89\xd6\x8b\x02\x66\x3d\xca\x7d\x75\x06\x66\x05\x03\x03\x89\x02\xfe\xc2\x3d\x41\x41\x41\x41\x75\xe9\xff\xe6\xe8\xdf\xff\xff\xff\x31\xd2\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xca\x7d\x41\x41\x41\x41\x41\x41\x41\x41'
# self modifying shellcode execve('/bin/sh')
shellcode = (
b'\xeb\x1c' # 00: jmp 0x1e
b'\x5a' # 02: pop rdx
b'\x89\xd6' # 03: mov esi, edx
b'\x8b\x02' # 05: mov eax, [rdx]
b'\x66\x3d\xca\x7d' # 07: cmp ax, 0x7dca
b'\x75\x06' # 0b: jne 0x13
b'\x66\x05\x03\x03' # 0d: add ax,0x303
b'\x89\x02' # 11: mov [rdx], eax
b'\xfe\xc2' # 13: inc dl
b'\x3d\x41\x41\x41\x41' # 15: cmp eax, 0x41414141
b'\x75\xe9' # 1a: jne 0x5
b'\xff\xe6' # 1c: jmp rsi
b'\xe8\xdf\xff\xff\xff' # 1e: call 0x2
b'\x31\xd2' # 23: xor edx, edx
b'\x6a\x0b' # 25: push 0xb
b'\x58' # 27: pop rax
b'\x99' # 28: cdq
b'\x52' # 29: push rdx
b'\x68\x2f\x2f\x73\x68' # 2a: push 0x68732f2f
b'\x68\x2f\x62\x69\x6e' # 2f: push 0x6e69622f
b'\x89\xe3' # 34: mov ebx, esp
b'\x52' # 36: push rdx
b'\x53' # 37: push rbx
b'\x89\xe1' # 38: mov ecx, esp
b'\xca\x7d\x41\x41\x41\x41\x41\x41\x41\x41' # 3a: .db ca 7d 41 41 41 41 41 41 41 41
)
address = 0x00000000
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(0, 2 * 1024 * 1024)
mu.mem_map(address, 0x1000)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code)
@ -42,16 +74,15 @@ class Hang(regress.RegressTest):
mu.hook_add(UC_HOOK_INTR, hook_intr)
# setup stack
mu.reg_write(UC_X86_REG_RSP, 1024 * 1024)
# fill in memory with 0xCC (software breakpoint int 3)
for i in xrange(1 * 1024):
mu.mem_write(0 + i, b'\xcc')
mu.reg_write(UC_X86_REG_RSP, 0x1000 - 8)
# write machine code to be emulated to memory
mu.mem_write(0, binary1)
mu.mem_write(address, shellcode)
regress.logger.debug('Starting emulation')
mu.emu_start(address, address + len(shellcode))
self.assertEqual(mu.emu_start(0, len(binary1)), None)
if __name__ == '__main__':
regress.main()

66
tests/regress/high_mem.py Normal file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env python3
import regress
from unicorn import Uc, UcError, UC_ARCH_X86, UC_MODE_64
from unicorn.unicorn_const import UC_TLB_VIRTUAL, UC_TLB_CPU, UC_ERR_FETCH_UNMAPPED
MAX_INTEL_INSN_SIZE = 15
class TestMem(regress.RegressTest):
# 0x0008fffffffff000 : mappings till this point work ok
# 0x0010000000000000 : mappings beyond this point will fail if tlb is not set to virtual
def setUp(self):
self.uc = Uc(UC_ARCH_X86, UC_MODE_64)
def map_code_page(self, address, payload):
regress.logger.debug('attempting to map code at %#018x', address)
self.uc.mem_map(address, 0x1000)
self.uc.mem_write(address, payload)
def test_virt_high_mapping(self):
"""Mapping memory at high addresses should work when TLB mode
is set to VIRTUAL.
"""
base = 0x0010000000000000
self.uc.ctl_tlb_mode(UC_TLB_VIRTUAL)
for i in range(12):
code = base << i
self.map_code_page(code, b'\xf4')
try:
self.uc.emu_start(code, code + MAX_INTEL_INSN_SIZE, count=1)
except UcError:
self.fail('high mapping failed at %#018x' % code)
def test_cpu_high_mapping(self):
"""Mapping memory at high addresses should work fail TLB mode
is set to CPU (default).
"""
base = 0x0010000000000000
self.uc.ctl_tlb_mode(UC_TLB_CPU)
for i in range(12):
code = base << i
self.map_code_page(code, b'\xf4')
with self.assertRaises(UcError) as ex:
self.uc.emu_start(code, code + MAX_INTEL_INSN_SIZE, count=1)
self.assertEqual(UC_ERR_FETCH_UNMAPPED, ex.exception.errno)
if __name__ == '__main__':
regress.main()

View File

@ -2,15 +2,21 @@
"""https://github.com/unicorn-engine/unicorn/issues/165"""
import unicorn
import regress
from unicorn import *
def hook_mem_read_unmapped(mu, access, address, size, value, user_data):
pass
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
class TestHook(regress.RegressTest):
def test_excessive_hooks(self):
mu = Uc(UC_ARCH_X86, UC_MODE_32)
try:
for x in range(0, 1000):
mu.hook_add(unicorn.UC_HOOK_MEM_READ_UNMAPPED, hook_mem_read_unmapped, None)
except unicorn.UcError as e:
print("ERROR: %s" % e)
for _ in range(1337):
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, hook_mem_read_unmapped)
if __name__ == '__main__':
regress.main()

View File

@ -11,38 +11,41 @@ from unicorn.x86_const import *
ADDRESS = 0x8048000
STACK_ADDRESS = 0xffff000
STACK_SIZE = 4096
'''
31 DB xor ebx, ebx
53 push ebx
43 inc ebx
53 push ebx
6A 02 push 2
6A 66 push 66h
58 pop eax
89 E1 mov ecx, esp
CD 80 int 80h
'''
CODE = "\x31\xDB\x53\x43\x53\x6A\x02\x6A\x66\x58\x89\xE1\xCD\x80"
CODE = (
b'\x31\xDB' # xor ebx, ebx
b'\x53' # push ebx
b'\x43' # inc ebx
b'\x53' # push ebx
b'\x6A\x02' # push 2
b'\x6A\x66' # push 66h
b'\x58' # pop eax
b'\x89\xE1' # mov ecx, esp
b'\xCD\x80' # int 80h
)
EP = ADDRESS + 0x54
def hook_code(mu, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size))
class HookCodeAddDelTest(regress.RegressTest):
def runTest(self):
emu = Uc(UC_ARCH_X86, UC_MODE_32)
emu.mem_map(ADDRESS, 0x1000)
emu.mem_write(EP, CODE)
regress.logger.debug(">>> Tracing instruction at %#x, instruction size = %u", address, size)
class HookCodeAddDelTest(regress.RegressTest):
def runTest(self):
emu = Uc(UC_ARCH_X86, UC_MODE_32)
emu.mem_map(ADDRESS, 0x1000)
emu.mem_write(EP, CODE)
emu.mem_map(STACK_ADDRESS, STACK_SIZE)
emu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + STACK_SIZE)
# UC_HOOK_CODE hook will work even after deletion
i = emu.hook_add(UC_HOOK_CODE, hook_code, None)
emu.hook_del(i)
emu.emu_start(EP, EP + len(CODE), count = 3)
regress.logger.debug("EIP: %#x", emu.reg_read(UC_X86_REG_EIP))
emu.mem_map(STACK_ADDRESS, STACK_SIZE)
emu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + STACK_SIZE)
# UC_HOOK_CODE hook will work even after deletion
i = emu.hook_add(UC_HOOK_CODE, hook_code, None)
emu.hook_del(i)
emu.emu_start(EP, EP + len(CODE), count = 3)
print("EIP: 0x%x" % emu.reg_read(UC_X86_REG_EIP))
if __name__ == '__main__':
regress.main()
regress.main()

View File

@ -1,90 +1,77 @@
#!/usr/bin/python
from __future__ import print_function
import binascii
import regress
from unicorn import *
from unicorn.x86_const import *
CODE = binascii.unhexlify(b"".join([
b"48c7c003000000", # mov rax, 3 mapped: 0x1000
b"0f05", # syscall mapped: 0x1007
b"48c7c700400000", # mov rdi, 0x4000 mapped: 0x1009
b"488907", # mov [rdi], rdx mapped: 0x1010
b"488b07", # mov rdx, [rdi] mapped: 0x1013
b"4883c201", # add rdx, 1 mapped: 0x1016
]))
CODE = (
b'\x48\xc7\xc0\x03\x00\x00\x00' # 0x1000: mov rax, 3
b'\x0f\x05' # 0x1007: syscall
b'\x48\xc7\xc7\x00\x40\x00\x00' # 0x1009: mov rdi, 0x4000
b'\x48\x89\x07' # 0x1010: mov [rdi], rdx
b'\x48\x8b\x07' # 0x1013: mov rdx, [rdi]
b'\x48\x83\xc2\x01' # 0x1016: add rdx, 1
)
BASE = 0x00001000
SCRATCH = 0x00004000
class SingleStepper:
def __init__(self, emu, test):
self._emu = emu
self._hit_count = 0
self._test = test
def __init__(self, uc, test):
self.uc = uc
self.hits = 0
self.test = test
def _stop_hook(self, uc, address, *args, **kwargs):
if self._hit_count == 0:
self._hit_count += 1
else:
self._test.assertEqual(1, self._hit_count, "HOOK_CODE invoked too many times")
self.hits += 1
if self.hits > 1:
self.test.assertEqual(2, self.hits, "HOOK_CODE invoked too many times")
uc.emu_stop()
def step(self):
self._hit_count = 0
h = self._emu.hook_add(UC_HOOK_CODE, self._stop_hook)
self.hits = 0
h = self.uc.hook_add(UC_HOOK_CODE, self._stop_hook)
try:
pc = self._emu.reg_read(UC_X86_REG_RIP)
self._emu.emu_start(pc, pc+0x20)
pc = self.uc.reg_read(UC_X86_REG_RIP)
self.uc.emu_start(pc, pc + 0x20)
finally:
self._emu.hook_del(h)
self.uc.hook_del(h)
def showpc(mu):
pc = mu.reg_read(UC_X86_REG_RIP)
print("pc: 0x%x" % (pc))
regress.logger.debug("pc: %#x", mu.reg_read(UC_X86_REG_RIP))
class HookCodeStopEmuTest(regress.RegressTest):
def test_hook_code_stop_emu(self):
try:
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu = Uc(UC_ARCH_X86, UC_MODE_64)
# base of CODE
mu.mem_map(0x1000, 0x1000)
mu.mem_write(0x1000, CODE)
# base of CODE
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, CODE)
# scratch, used by CODE
mu.mem_map(0x4000, 0x1000)
# scratch, used by CODE
mu.mem_map(SCRATCH, 0x1000)
mu.reg_write(UC_X86_REG_RDX, 0x1)
mu.reg_write(UC_X86_REG_RIP, 0x1000)
mu.reg_write(UC_X86_REG_RDX, 0x1)
mu.reg_write(UC_X86_REG_RIP, BASE)
# 0x1000: 48c7c003000000 mov rax, 3
# 0x1007: 0f05 syscall
# 0x1009: 48c7c700400000 mov rdi, 0x4000
# 0x1010: 488907 mov [rdi], rdx
# 0x1013: 488b07 mov rdx, [rdi]
# 0x1016: 4883c201 add rdx, 1
stepper = SingleStepper(mu, self)
showpc(mu)
self.assertEqual(0x1000, mu.reg_read(UC_X86_REG_RIP), "Unexpected PC")
stepper = SingleStepper(mu, self)
showpc(mu)
self.assertEqual(BASE + 0x0, mu.reg_read(UC_X86_REG_RIP), "Unexpected starting PC")
stepper.step()
showpc(mu)
self.assertEqual(BASE + 0x7, mu.reg_read(UC_X86_REG_RIP), "Emulator failed to stop after one instruction")
stepper.step()
showpc(mu)
self.assertEqual(0x1007, mu.reg_read(UC_X86_REG_RIP),
"Emulator failed to stop after one instruction")
stepper.step()
showpc(mu)
self.assertEqual(0x1009, mu.reg_read(UC_X86_REG_RIP),
"Emulator failed to stop after one instruction")
except UcError as e:
self.assertFalse(0, "ERROR: %s" % e)
stepper.step()
showpc(mu)
self.assertEqual(BASE + 0x9, mu.reg_read(UC_X86_REG_RIP), "Emulator failed to stop after one instruction")
if __name__ == '__main__':

View File

@ -1,30 +1,41 @@
#!/usr/bin/python
from unicorn import *
from unicorn.x86_const import *
import regress
PAGE_SIZE = 4 * 1024
from unicorn import *
from unicorn.x86_const import *
PAGE_SIZE = 0x1000
ACCESS_ADDR = 0x1000
# mov eax, [0x1000]
# mov eax, [0x1000]
CODE = b'\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00'
CODE = (
b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000]
b'\xA1\x00\x10\x00\x00' # mov eax, [0x1000]
)
BASE = 0x00000000
def hook_mem_read(uc, access, address, size, value, data):
print("Reading at " + str(address))
regress.logger.debug("Reading at %#x", address)
# BUG: unicorn will segfault when calling "uc.mem_write" to write to a location that was mapped only as UC_PROT_READ
uc.mem_write(address, CODE)
class REP(regress.RegressTest):
def test_rep(self):
class REP(regress.RegressTest):
@regress.unittest.skip('writing to a UC_PROT_READ area will segfault Unicorn')
def runTest(self):
mu = Uc(UC_ARCH_X86, UC_MODE_32)
mu.mem_map(0, PAGE_SIZE)
mu.mem_write(0, CODE)
mu.mem_map(BASE, PAGE_SIZE)
mu.mem_write(BASE, CODE)
mu.mem_map(ACCESS_ADDR, PAGE_SIZE, UC_PROT_READ)
mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read, begin = ACCESS_ADDR, end = ACCESS_ADDR + PAGE_SIZE)
mu.emu_start(0, len(CODE))
mu.emu_start(BASE, BASE + len(CODE))
self.assertEqual(0x001000a1, mu.reg_read(UC_X86_REG_EAX))
if __name__ == '__main__':
regress.main()

View File

@ -1,10 +1,17 @@
#!/usr/bin/python
# By Mariano Graziano
import regress
import struct
import sys
from unicorn import *
from unicorn.x86_const import *
import regress, struct
if sys.version_info.major == 2:
range = xrange
mu = 0
@ -15,54 +22,61 @@ class Init(regress.RegressTest):
#print "[+] Emulating IP: %x SP: %x - Counter: %x" % (ip, sp, counter)
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(0x1000000, 2 * 1024 * 1024)
mu.mem_write(0x1000000, "\x90")
mu.mem_write(0x1000000, b"\x90")
mu.mem_map(0x8000000, 8 * 1024 * 1024)
mu.reg_write(UC_X86_REG_RSP, sp)
content = self.generate_value(counter)
mu.mem_write(sp, content)
self.set_hooks()
def generate_value(self, counter):
start = 0xffff880026f02000
offset = counter * 8
address = start + offset
return struct.pack("<Q", address)
def set_hooks(self):
global mu
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid)
mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, self.hook_mem_fetch_unmapped)
def hook_mem_invalid(self, uc, access, address, size, value, user_data):
global mu
print "[ HOOK_MEM_INVALID - Address: %s ]" % hex(address)
regress.logger.debug("[ HOOK_MEM_INVALID - Address: 0x%x ]", address)
if access == UC_MEM_WRITE_UNMAPPED:
print ">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" %(address, size, value)
regress.logger.debug(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x", address, size, value)
address_page = address & 0xFFFFFFFFFFFFF000
mu.mem_map(address_page, 2 * 1024 * 1024)
mu.mem_write(address, str(value))
return True
else:
return False
def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data):
global mu
print "[ HOOK_MEM_FETCH - Address: %s ]" % hex(address).strip("L")
print "[ mem_fetch_unmapped: faulting address at %s ]" % hex(address).strip("L")
mu.mem_write(0x1000003, "\x90")
regress.logger.debug("[ HOOK_MEM_FETCH - Address: 0x%x ]", address)
regress.logger.debug("[ mem_fetch_unmapped: faulting address at 0x%x ]", address)
mu.mem_write(0x1000003, b"\x90")
mu.reg_write(UC_X86_REG_RIP, 0x1000001)
return True
def runTest(self):
global mu
ips = list(xrange(0x1000000, 0x1001000, 0x1))
sps = list(xrange(0x8000000, 0x8001000, 0x1))
j = 0
for i in ips:
j += 1
index = ips.index(i)
self.init_unicorn(i, sps[index], j)
ips = range(0x1000000, 0x1001000)
sps = range(0x8000000, 0x8001000)
for i, (ip, sp) in enumerate(zip(ips, sps)):
self.init_unicorn(ip, sp, i)
mu.emu_start(0x1000000, 0x1000000 + 0x1)
if __name__ == '__main__':
regress.main()

View File

@ -2,38 +2,42 @@
"""See https://github.com/unicorn-engine/unicorn/issues/82"""
import unicorn
from unicorn import *
import regress
from unicorn import *
from unicorn.x86_const import *
CODE_ADDR = 0x10101000
CODE = b'\xff\xe3' # jmp ebx
class JumEbxHang(regress.RegressTest):
def runTest(self):
mu = unicorn.Uc(UC_ARCH_X86, UC_MODE_32)
mu = Uc(UC_ARCH_X86, UC_MODE_32)
mu.mem_map(CODE_ADDR, 1024 * 4)
mu.mem_write(CODE_ADDR, CODE)
# If EBX is zero then an exception is raised, as expected
mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0x0)
mu.reg_write(UC_X86_REG_EBX, 0x0)
print(">>> jmp ebx (ebx = 0)")
regress.logger.debug(">>> jmp ebx (ebx = 0)")
with self.assertRaises(UcError) as m:
mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1)
self.assertEqual(m.exception.errno, UC_ERR_FETCH_UNMAPPED)
print(">>> jmp ebx (ebx = 0xaa96a47f)")
mu = unicorn.Uc(UC_ARCH_X86, UC_MODE_32)
regress.logger.debug(">>> jmp ebx (ebx = 0xaa96a47f)")
mu = Uc(UC_ARCH_X86, UC_MODE_32)
mu.mem_map(CODE_ADDR, 1024 * 4)
# If we write this address to EBX then the emulator hangs on emu_start
mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0xaa96a47f)
mu.reg_write(UC_X86_REG_EBX, 0xaa96a47f)
mu.mem_write(CODE_ADDR, CODE)
with self.assertRaises(UcError) as m:
mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1)
self.assertEqual(m.exception.errno, UC_ERR_FETCH_UNMAPPED)
if __name__ == '__main__':
regress.main()

View File

@ -1,166 +1,126 @@
#!/usr/bin/env python
# Mariano Graziano
import binascii
import regress
from unicorn import *
from unicorn.x86_const import *
import regress
#echo -ne "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90" | ndisasm - -b64
#00000000 4831C0 xor rax,rax
#00000003 48B8040000000000 mov rax,0x4
# -0000
#0000000D 483D05000000 cmp rax,0x5
#00000013 7405 jz 0x1a
#00000015 E90F000000 jmp qword 0x29
#0000001A 48BABEBA00000000 mov rdx,0xbabe
# -0000
#00000024 E90F000000 jmp qword 0x38
#00000029 48BACAC000000000 mov rdx,0xc0ca
# -0000
#00000033 E900000000 jmp qword 0x38
#00000038 90 nop
# set rdx to either 0xbabe or 0xc0ca, based on a comparison.
# rdx would never be set to 0xbabe unless we set zf to 1
CODE = (
b"\x48\x31\xc0" # xor rax, rax
b"\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00" # movabs rax, 0x4
b"\x48\x3d\x05\x00\x00\x00" # cmp rax, 0x5 <-- never true, zf is cleared
b"\x74\x05" # je 0x1a
b"\xe9\x0f\x00\x00\x00" # jmp 0x29
b"\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00" # 1a: movabs rdx, 0xbabe <-- never reached unless we set zf
b"\xe9\x0f\x00\x00\x00" # jmp 0x38
b"\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00" # 29: movabs rdx, 0xc0ca
b"\xe9\x00\x00\x00\x00" # jmp 0x38
b"\xf4" # 38: hlt
)
BASE = 0x1000000
mu = 0
zf = 1 # (0:clear, 1:set)
class Init(regress.RegressTest):
class Jumping(regress.RegressTest):
def clear_zf(self):
eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS)
eflags = eflags_cur & ~(1 << 6)
#eflags = 0x0
print "[clear_zf] - eflags from %x to %x" % (eflags_cur, eflags)
if eflags != eflags_cur:
print "[clear_zf] - writing new eflags..."
mu.reg_write(UC_X86_REG_EFLAGS, eflags)
eflags = self.uc.reg_read(UC_X86_REG_EFLAGS)
if (eflags >> 6) & 0b1 == 0b1:
eflags &= ~(0b1 << 6)
regress.logger.debug("[clear_zf] clearing zero flag")
self.uc.reg_write(UC_X86_REG_EFLAGS, eflags)
else:
regress.logger.debug("[clear_zf] no change needed")
def set_zf(self):
eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS)
eflags = eflags_cur | (1 << 6)
#eflags = 0xFFFFFFFF
print "[set_zf] - eflags from %x to %x" % (eflags_cur, eflags)
if eflags != eflags_cur:
print "[set_zf] - writing new eflags..."
mu.reg_write(UC_X86_REG_EFLAGS, eflags)
eflags = self.uc.reg_read(UC_X86_REG_EFLAGS)
def handle_zf(self, zf):
print "[handle_zf] - eflags " , zf
if zf == 0: self.clear_zf()
else: self.set_zf()
if (eflags >> 6) & 0b1 == 0b0:
eflags |= (0b1 << 6)
regress.logger.debug("[set_zf] setting zero flag")
self.uc.reg_write(UC_X86_REG_EFLAGS, eflags)
else:
regress.logger.debug("[set_zf] no change needed")
def multipath(self):
print "[multipath] - handling ZF (%s) - default" % zf
self.handle_zf(zf)
regress.logger.debug("[multipath] - handling ZF (%s) - default", self.fixed_zf)
if self.fixed_zf:
self.set_zf()
else:
self.clear_zf()
# BUG: eflags changes do not get reflected unless re-writing eip
# callback for tracing basic blocks
def hook_block(self, uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
def hook_block(self, uc, address, size, _):
regress.logger.debug("Reached a new basic block at %#x (%d bytes in size)", address, size)
# callback for tracing instructions
def hook_code(self, uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size))
rax = mu.reg_read(UC_X86_REG_RAX)
rbx = mu.reg_read(UC_X86_REG_RBX)
rcx = mu.reg_read(UC_X86_REG_RCX)
rdx = mu.reg_read(UC_X86_REG_RDX)
rsi = mu.reg_read(UC_X86_REG_RSI)
rdi = mu.reg_read(UC_X86_REG_RDI)
r8 = mu.reg_read(UC_X86_REG_R8)
r9 = mu.reg_read(UC_X86_REG_R9)
r10 = mu.reg_read(UC_X86_REG_R10)
r11 = mu.reg_read(UC_X86_REG_R11)
r12 = mu.reg_read(UC_X86_REG_R12)
r13 = mu.reg_read(UC_X86_REG_R13)
r14 = mu.reg_read(UC_X86_REG_R14)
r15 = mu.reg_read(UC_X86_REG_R15)
eflags = mu.reg_read(UC_X86_REG_EFLAGS)
print(">>> RAX = %x" %rax)
print(">>> RBX = %x" %rbx)
print(">>> RCX = %x" %rcx)
print(">>> RDX = %x" %rdx)
print(">>> RSI = %x" %rsi)
print(">>> RDI = %x" %rdi)
print(">>> R8 = %x" %r8)
print(">>> R9 = %x" %r9)
print(">>> R10 = %x" %r10)
print(">>> R11 = %x" %r11)
print(">>> R12 = %x" %r12)
print(">>> R13 = %x" %r13)
print(">>> R14 = %x" %r14)
print(">>> R15 = %x" %r15)
print(">>> ELAGS = %x" %eflags)
print "-"*11
def hook_code(self, uc, address, size, _):
insn = uc.mem_read(address, size)
regress.logger.debug(">>> Tracing instruction at %#x : %s", address, binascii.hexlify(insn))
regs = uc.reg_read_batch((
UC_X86_REG_RAX, UC_X86_REG_RBX, UC_X86_REG_RCX, UC_X86_REG_RDX,
UC_X86_REG_RSI, UC_X86_REG_RDI, UC_X86_REG_RBP, UC_X86_REG_RSP,
UC_X86_REG_R8, UC_X86_REG_R9, UC_X86_REG_R10, UC_X86_REG_R11,
UC_X86_REG_R12, UC_X86_REG_R13, UC_X86_REG_R14, UC_X86_REG_R15,
UC_X86_REG_EFLAGS
))
zf = (regs[16] >> 6) & 0b1
regress.logger.debug(" RAX = %08x, R8 = %08x", regs[0], regs[ 8])
regress.logger.debug(" RBX = %08x, R9 = %08x", regs[1], regs[ 9])
regress.logger.debug(" RCX = %08x, R10 = %08x", regs[2], regs[10])
regress.logger.debug(" RDX = %08x, R11 = %08x", regs[3], regs[11])
regress.logger.debug(" RSI = %08x, R12 = %08x", regs[4], regs[12])
regress.logger.debug(" RDI = %08x, R13 = %08x", regs[5], regs[13])
regress.logger.debug(" RBP = %08x, R14 = %08x", regs[6], regs[14])
regress.logger.debug(" RSP = %08x, R15 = %08x", regs[7], regs[15])
regress.logger.debug(" EFLAGS = %08x (ZF = %d)", regs[16], zf)
regress.logger.debug("-" * 32)
self.multipath()
print "-"*11
# callback for tracing memory access (READ or WRITE)
def hook_mem_access(self, uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE:
print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
%(address, size, value))
else: # READ
print(">>> Memory is being READ at 0x%x, data size = %u" \
%(address, size))
# callback for tracing invalid memory access (READ or WRITE)
def hook_mem_invalid(self, uc, access, address, size, value, user_data):
print("[ HOOK_MEM_INVALID - Address: %s ]" % hex(address))
if access == UC_MEM_WRITE_UNMAPPED:
print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" %(address, size, value))
return True
else:
print(">>> Missing memory is being READ at 0x%x, data size = %u, data value = 0x%x" %(address, size, value))
return True
regress.logger.debug("-" * 32)
def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data):
print("[ HOOK_MEM_FETCH - Address: %s ]" % hex(address))
print("[ mem_fetch_unmapped: faulting address at %s ]" % hex(address).strip("L"))
return True
def setUp(self):
# decide how to fixate zf value: 0 to clear, 1 to set
self.fixed_zf = 1
def runTest(self):
global mu
JUMP = "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90"
ADDRESS = 0x1000000
print("Emulate x86_64 code")
# Initialize emulator in X86-64bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_64)
uc = Uc(UC_ARCH_X86, UC_MODE_64)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# map one page for this emulation
uc.mem_map(BASE, 0x1000)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, JUMP)
uc.mem_write(BASE, CODE)
# setup stack
mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000)
self.uc = uc
def runTest(self):
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, self.hook_block)
self.uc.hook_add(UC_HOOK_BLOCK, self.hook_block)
# tracing all instructions in range [ADDRESS, ADDRESS+0x60]
mu.hook_add(UC_HOOK_CODE, self.hook_code, None, ADDRESS, ADDRESS+0x60)
self.uc.hook_add(UC_HOOK_CODE, self.hook_code, begin=BASE, end=BASE + 0x60)
# tracing all memory READ & WRITE access
mu.hook_add(UC_HOOK_MEM_WRITE, self.hook_mem_access)
mu.hook_add(UC_HOOK_MEM_READ, self.hook_mem_access)
mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, self.hook_mem_fetch_unmapped)
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid)
# emulate machine code in infinite time
self.uc.emu_start(BASE, BASE + len(CODE))
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(JUMP))
except UcError as e:
print("ERROR: %s" % e)
rdx = mu.reg_read(UC_X86_REG_RDX)
self.assertEqual(rdx, 0xbabe, "RDX contains the wrong value. Eflags modification failed.")
self.assertEqual(self.uc.reg_read(UC_X86_REG_RDX), 0xbabe, "rdx contains the wrong value. eflags modification failed")
if __name__ == '__main__':

View File

@ -1,63 +1,74 @@
#!/usr/bin/python
from __future__ import print_function
import time
import gc
import regress
import weakref
from unicorn import *
from unicorn.x86_const import *
import objgraph
import regress
ADDRESS = 0x8048000
STACK_ADDRESS = 0xffff000
STACK_SIZE = 4096
'''
31 DB xor ebx, ebx
53 push ebx
43 inc ebx
53 push ebx
6A 02 push 2
6A 66 push 66h
58 pop eax
89 E1 mov ecx, esp
CD 80 int 80h
'''
CODE = "\x31\xDB\x53\x43\x53\x6A\x02\x6A\x66\x58\x89\xE1\xCD\x80"
STACK_SIZE = 0x1000
CODE = (
b"\x31\xDB" # xor ebx, ebx
b"\x53" # push ebx
b"\x43" # inc ebx
b"\x53" # push ebx
b"\x6A\x02" # push 2
b"\x6A\x66" # push 66h
b"\x58" # pop eax
b"\x89\xE1" # mov ecx, esp
b"\xCD\x80" # int 80h
)
EP = ADDRESS + 0x54
# Dictionary to keep weak references to instances
instances = weakref.WeakValueDictionary()
def create_instance(key, *args, **kwargs):
obj = Uc(*args, **kwargs)
instances[key] = obj
return obj
def hook_code(mu, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size))
def emu_loop():
emu = Uc(UC_ARCH_X86, UC_MODE_32)
regress.logger.debug(">>> Tracing instruction at %#x, instruction size = %u", address, size)
def emu_loop(key):
emu = create_instance(key, UC_ARCH_X86, UC_MODE_32)
emu.mem_map(ADDRESS, 0x1000)
emu.mem_write(EP, CODE)
emu.mem_map(STACK_ADDRESS, STACK_SIZE)
emu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + STACK_SIZE)
i = emu.hook_add(UC_HOOK_CODE, hook_code, None)
emu.hook_del(i)
emu.emu_start(EP, EP + len(CODE), count = 3)
print("EIP: 0x%x" % emu.reg_read(UC_X86_REG_EIP))
regress.logger.debug("EIP: %#x", emu.reg_read(UC_X86_REG_EIP))
def debugMem():
import gc
gc.collect() # don't care about stuff that would be garbage collected properly
#print("Orphaned objects in gc.garbage:", gc.garbage)
assert(len(objgraph.by_type("Uc")) == 0)
#assert(len(objgraph.get_leaking_objects()) == 0)
assert(len(instances) == 0)
class EmuLoopReferenceTest(regress.RegressTest):
def runTest(self):
for i in range(5):
emu_loop()
emu_loop('obj%d' % i)
debugMem()
if __name__ == '__main__':
regress.main()
regress.main()

View File

@ -3,38 +3,45 @@
# this prints out 2 lines and the contents must be the same
from unicorn import *
import regress
from unicorn import *
from unicorn.x86_const import *
class MemMap(regress.RegressTest):
def test_mmap_write(self):
uc = Uc(UC_ARCH_X86, UC_MODE_64)
uc.mem_map(0x8048000, 0x2000)
uc.mem_write(0x8048000, 'test')
s1 = str(uc.mem_read(0x8048000, 4)).encode('hex')
uc.mem_write(0x8048000, b'test')
s1 = uc.mem_read(0x8048000, 4)
self.assertEqual('test'.encode('hex'), s1)
self.assertEqual(b'test', s1)
uc.mem_map(0x804a000, 0x8000)
s2 = str(uc.mem_read(0x8048000, 4)).encode('hex')
s2 = uc.mem_read(0x8048000, 4)
self.assertEqual(s1, s2)
def test_mmap_invalid(self):
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
u = Uc(UC_ARCH_X86, UC_MODE_32)
with self.assertRaises(UcError):
u.mem_map(0x2000, 0)
with self.assertRaises(UcError):
u.mem_map(0x4000, 1)
def test_mmap_weird(self):
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
u = Uc(UC_ARCH_X86, UC_MODE_32)
for i in xrange(20):
for i in range(20):
with self.assertRaises(UcError):
u.mem_map(i*0x1000, 5)
u.mem_read(i*0x1000+6, 1)
u.mem_map(i * 0x1000, 5)
u.mem_read(i * 0x1000+6, 1)
if __name__ == '__main__':
regress.main()

View File

@ -1,35 +1,38 @@
#!/usr/bin/env python
import unicorn
from unicorn import *
import regress
class MmapSeg(regress.RegressTest):
from unicorn import *
from unicorn.x86_const import *
def test_seg1(self):
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
class MmapSeg1(regress.RegressTest):
def runTest(self):
u = Uc(UC_ARCH_X86, UC_MODE_32)
u.mem_map(0x2000, 0x1000)
u.mem_read(0x2000, 1)
for i in range(50):
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
u.mem_map(i*0x1000, 0x1000)
u.mem_read(i*0x1000, 1)
u = Uc(UC_ARCH_X86, UC_MODE_32)
u.mem_map(i * 0x1000, 0x1000)
u.mem_read(i * 0x1000, 1)
for i in range(20):
with self.assertRaises(UcError):
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
u.mem_map(i*0x1000, 5)
u.mem_read(i*0x1000, 1)
u = Uc(UC_ARCH_X86, UC_MODE_32)
u.mem_map(i * 0x1000, 5)
u.mem_read(i * 0x1000, 1)
def test_seg2(self):
class MmapSeg2(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_X86, UC_MODE_32)
uc.mem_map(0x0000, 0x2000)
uc.mem_map(0x2000, 0x4000)
uc.mem_write(0x1000, 0x1004 * ' ')
self.assertTrue(1,
'If not reached, then we have BUG (crash on x86_64 Linux).')
uc.mem_write(0x1000, b' ' * 0x1004)
self.assertTrue(True, 'If not reached, then we have BUG (crash on x86_64 Linux).')
if __name__ == '__main__':
regress.main()

View File

@ -1,8 +1,19 @@
#!/usr/bin/python
import regress
from capstone import *
from unicorn import *
import regress
CODE = (
b'\x00\x00\xa4\x12' # beq $a0, $s5, 0x4008a0
b'\x6a\x00\x82\x28' # slti $v0, $a0, 0x6a
b'\x00\x00\x00\x00' # nop
)
BASE = 0x400000
class MipsBranchDelay(regress.RegressTest):
@ -10,27 +21,24 @@ class MipsBranchDelay(regress.RegressTest):
md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN)
def disas(code, addr):
for i in md.disasm(code, addr):
print '0x%x: %s %-6s %s' % (i.address, str(i.bytes).encode('hex'), i.mnemonic, i.op_str)
for insn in md.disasm(code, addr):
regress.logger.debug('%#x: %-8s %s', insn.address, insn.mnemonic, insn.op_str)
def hook_code(uc, addr, size, _):
mem = str(uc.mem_read(addr, size))
disas(mem, addr)
disas(uc.mem_read(addr, size), addr)
CODE = 0x400000
asm = '0000a4126a00822800000000'.decode('hex') # beq $a0, $s5, 0x4008a0; slti $v0, $a0, 0x6a; nop
regress.logger.debug('Input instructions:')
disas(CODE, BASE)
print 'Input instructions:'
disas(asm, CODE)
print
print 'Hooked instructions:'
regress.logger.debug('Hooked instructions:')
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
uc.hook_add(UC_HOOK_CODE, hook_code)
uc.mem_map(CODE, 0x1000)
uc.mem_write(CODE, asm)
self.assertEqual(None, uc.emu_start(CODE, CODE + len(asm)))
uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)
self.assertEqual(None, uc.emu_start(BASE, BASE + len(CODE)))
if __name__ == '__main__':
regress.main()

View File

@ -1,13 +1,29 @@
import regress
from unicorn import *
from unicorn.mips_const import *
CODE = b'\x44\x43\xF8\x00' # cfc1 $v1, FCSR
BASE = 0x416CB0
# .text:00416CB0 cfc1 $v1, FCSR
shellcode = [0x44, 0x43, 0xF8, 0x00]
base = 0x416CB0
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
uc.mem_map(0x416000, 0x1000)
uc.mem_write(base, bytes(shellcode))
uc.emu_start(base, base + len(shellcode))
class TestMipsCp1(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
uc.mem_map(BASE & ~(0x1000 - 1), 0x1000)
uc.mem_write(BASE, CODE)
# set a wrong value in v1
uc.reg_write(UC_MIPS_REG_V1, 0x0badc0de)
uc.emu_start(BASE, BASE + len(CODE))
# default FCSR value should be 0
self.assertEqual(0x0000, uc.reg_read(UC_MIPS_REG_V1))
if __name__ == '__main__':
regress.main()

View File

@ -1,41 +1,59 @@
#!/usr/bin/python
from unicorn import *
from unicorn.mips_const import *
import regress
def hook_intr(uc, intno, _):
print 'interrupt', intno
from unicorn import *
from unicorn.mips_const import *
CODE = (
b'\x00\x00\x00\x00' # nop
b'\x00\x00\xa4\x8f' # lw $a0, 0($sp)
)
BASE = 0x20000000
CODE = 0x400000
asm = '0000a48f'.decode('hex') # lw $a0, ($sp)
class MipsExcept(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
uc.hook_add(UC_HOOK_INTR, hook_intr)
uc.mem_map(CODE, 0x1000)
uc.mem_write(CODE, asm)
uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)
# execute nop. we should be ok
uc.emu_start(BASE, BASE + len(CODE), count=1)
# ----------------------------------------
# set sp to a mapped but unaligned address to read from
uc.reg_write(UC_MIPS_REG_SP, BASE + 0x801)
with self.assertRaises(UcError) as m:
uc.reg_write(UC_MIPS_REG_SP, 0x400001)
uc.emu_start(CODE, CODE + len(asm), 300)
uc.emu_start(BASE + 4, BASE + len(CODE), count=1)
self.assertEqual(UC_ERR_READ_UNALIGNED, m.exception.errno)
# ----------------------------------------
# set sp to an umapped address to read from
uc.reg_write(UC_MIPS_REG_SP, 0xfffffff0)
with self.assertRaises(UcError) as m:
uc.reg_write(UC_MIPS_REG_SP, 0xFFFFFFF0)
uc.emu_start(CODE, CODE + len(asm), 200)
uc.emu_start(BASE + 4, BASE + len(CODE), count=1)
self.assertEqual(UC_ERR_READ_UNMAPPED, m.exception.errno)
# ----------------------------------------
uc.reg_write(UC_MIPS_REG_SP, 0x40000000)
with self.assertRaises(UcError) as m:
uc.reg_write(UC_MIPS_REG_SP, 0x80000000)
uc.emu_start(CODE, CODE + len(asm), 100)
uc.emu_start(BASE + 4, BASE + len(CODE), count=1)
self.assertEqual(UC_ERR_READ_UNMAPPED, m.exception.errno)
if __name__ == '__main__':
regress.main()

View File

@ -1,23 +1,26 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.mips_const import *
import regress
CODE = b'\x34\x21\x34\x56' # ori $at, $at, 0x3456
BASE = 0x10000000
class MipsSyscall(regress.RegressTest):
def test(self):
addr = 0x80000000
code = '34213456'.decode('hex') # ori $at, $at, 0x3456
def test_syscall(self):
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
uc.mem_map(addr, 0x1000)
uc.mem_write(addr, code)
uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)
uc.reg_write(UC_MIPS_REG_AT, 0)
uc.emu_start(addr, addr + len(code))
uc.emu_start(BASE, BASE + len(CODE))
self.assertEqual(uc.reg_read(UC_MIPS_REG_AT), 0x3456)
self.assertEqual(0x3456, uc.reg_read(UC_MIPS_REG_AT))
if __name__ == '__main__':

View File

@ -1,51 +1,56 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.mips_const import *
import regress
CODE = (
b'\xf8\xff\x01\x24' # addiu $at, $zero, -8
b'\x24\xe8\xa1\x03' # and $sp, $sp, $at
b'\x09\xf8\x20\x03' # jalr $t9
b'\xe8\xff\xbd\x23' # addi $sp, $sp, -0x18
b'\xb8\xff\xbd\x27' # addiu $sp, $sp, -0x48
b'\x00\x00\x00\x00' # nop
)
BASE = 0x4010dc
def code_hook(uc, addr, size, user_data):
print 'code hook: pc=%08x sp=%08x' % (addr, uc.reg_read(UC_MIPS_REG_SP))
regress.logger.debug('code hook: pc=%08x sp=%08x', addr, uc.reg_read(UC_MIPS_REG_SP))
def run(step=False):
addr = 0x4010dc
code = (
'f8ff0124' # addiu $at, $zero, -8
'24e8a103' # and $sp, $sp, $at
'09f82003' # jalr $t9
'e8ffbd23' # addi $sp, $sp, -0x18
'b8ffbd27' # addiu $sp, $sp, -0x48
'00000000' # nop
).decode('hex')
def run(step) -> int:
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
if step:
uc.hook_add(UC_HOOK_CODE, code_hook)
uc.reg_write(UC_MIPS_REG_SP, 0x60800000)
uc.reg_write(UC_MIPS_REG_T9, addr + len(code) - 8)
uc.reg_write(UC_MIPS_REG_T9, BASE + len(CODE) - 8)
print 'sp =', hex(uc.reg_read(UC_MIPS_REG_SP))
print 'at =', hex(uc.reg_read(UC_MIPS_REG_AT))
print '<run> (single step: %s)' % (str(step))
regress.logger.debug('sp = %08x', uc.reg_read(UC_MIPS_REG_SP))
regress.logger.debug('at = %08x', uc.reg_read(UC_MIPS_REG_AT))
regress.logger.debug('<run> (single step: %s)', str(step))
uc.mem_map(addr & ~(0x1000 - 1), 0x2000)
uc.mem_write(addr, code)
uc.emu_start(addr, addr + len(code))
uc.mem_map(BASE & ~(0x1000 - 1), 0x2000)
uc.mem_write(BASE, CODE)
uc.emu_start(BASE, BASE + len(CODE))
regress.logger.debug('sp = %08x', uc.reg_read(UC_MIPS_REG_SP))
regress.logger.debug('at = %08x', uc.reg_read(UC_MIPS_REG_AT))
print 'sp =', hex(uc.reg_read(UC_MIPS_REG_SP))
print 'at =', hex(uc.reg_read(UC_MIPS_REG_AT))
print
return uc.reg_read(UC_MIPS_REG_SP)
class MipsSingleStep(regress.RegressTest):
def test(self):
def runTest(self):
sp1 = run(step=False)
sp2 = run(step=True)
self.assertEqual(sp1, sp2)
if __name__ == '__main__':
regress.main()

View File

@ -1,25 +1,29 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.mips_const import *
import regress
CODE = b'\x0c\x00\x00\x00' # syscall
BASE = 0x40000
def intr_hook(uc, intno, data):
print 'interrupt=%d, v0=%d, pc=0x%08x' % (intno, uc.reg_read(UC_MIPS_REG_V0), uc.reg_read(UC_MIPS_REG_PC))
regress.logger.debug('interrupt=%d, v0=%d, pc=%#010x', intno, uc.reg_read(UC_MIPS_REG_V0), uc.reg_read(UC_MIPS_REG_PC))
class MipsSyscall(regress.RegressTest):
def test(self):
addr = 0x40000
code = '0c000000'.decode('hex') # syscall
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
uc.mem_map(addr, 0x1000)
uc.mem_write(addr, code)
uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)
uc.reg_write(UC_MIPS_REG_V0, 100)
uc.hook_add(UC_HOOK_INTR, intr_hook)
uc.emu_start(addr, addr+len(code))
uc.emu_start(BASE, BASE+len(CODE))
self.assertEqual(0x40004, uc.reg_read(UC_MIPS_REG_PC))

View File

@ -1,24 +1,34 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.x86_const import *
import regress
CODE = (
b'\x8e\xe8' # mov gs, eax
b'\xb8\x01\x00\x00\x00' # mov eax, 1
)
BASE = 0x1000
class VldrPcInsn(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_X86, UC_MODE_32)
uc.mem_map(0x1000, 0x1000)
# mov gs, eax; mov eax, 1
code = '8ee8b801000000'.decode('hex')
uc.mem_write(0x1000, code)
uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)
uc.reg_write(UC_X86_REG_EAX, 0xFFFFFFFF)
with self.assertRaises(UcError) as ex_ctx:
uc.emu_start(0x1000, 0x1000 + len(code))
uc.emu_start(BASE, BASE + len(CODE))
self.assertEqual(UC_ERR_EXCEPTION, ex_ctx.exception.errno)
self.assertEquals(ex_ctx.exception.errno, UC_ERR_EXCEPTION)
if __name__ == '__main__':
regress.main()

View File

@ -1,35 +1,39 @@
#!/usr/bin/python
# By Ryan Hileman, issue #3
from capstone import *
import regress
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
from unicorn import *
from unicorn.x86_const import *
import regress
code = 'f20f1005aa120000'.decode('hex')
def dis(mem, addr):
md = Cs(CS_ARCH_X86, CS_MODE_64)
return '\n'.join([
'%s %s' % (i.mnemonic, i.op_str)
for i in md.disasm(str(mem), addr)
])
CODE = b'\xf2\x0f\x10\x05\xaa\x12\x00\x00'
def hook_code(uc, addr, size, user_data):
def dis(md, mem, addr):
return '\n'.join(('%-16s %s' % (insn.mnemonic, insn.op_str) for insn in md.disasm(mem, addr)))
def hook_code(uc, addr, size, md):
mem = uc.mem_read(addr, size)
print 'instruction size:', size
print 'instruction:', str(mem).encode('hex'), dis(mem, addr)
print 'reference: ', code.encode('hex'), dis(code, addr)
regress.logger.debug('instruction size: %d', size)
regress.logger.debug('instruction: %s %s', mem, dis(md, mem, addr))
regress.logger.debug('reference: %s %s', CODE, dis(md, CODE, addr))
class Movsd(regress.RegressTest):
def runTest(self):
addr = 0x400000
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.hook_add(UC_HOOK_CODE, hook_code)
md = Cs(CS_ARCH_X86, CS_MODE_64)
mu.hook_add(UC_HOOK_CODE, hook_code, md)
mu.mem_map(addr, 8 * 1024 * 1024)
mu.mem_write(addr, code)
mu.emu_start(addr, addr + len(code))
mu.mem_write(addr, CODE)
mu.emu_start(addr, addr + len(CODE))
if __name__ == '__main__':
regress.main()

View File

@ -1,20 +1,18 @@
#!/usr/bin/env python
import platform
import resource
import regress
from unicorn import *
import regress
# OS X: OK with 2047 iterations.
# OS X: Crashes at 2048:th iteration ("qemu: qemu_thread_create: Resource temporarily unavailable").
# Linux: No crashes observed.
class ThreadCreateCrash(regress.RegressTest):
def test(self):
for i in xrange(2048):
for _ in range(2048):
Uc(UC_ARCH_X86, UC_MODE_64)
self.assertTrue(True, "If not reached, then we have a crashing bug.")
if __name__ == '__main__':
regress.main()

View File

@ -2,10 +2,12 @@
import platform
import resource
import regress
from unicorn import *
import regress
ITERATIONS = 10000
class MemoryLeak(regress.RegressTest):
def test(self):
@ -15,15 +17,20 @@ class MemoryLeak(regress.RegressTest):
rusage_multiplier = 1024
else:
# resource.getrusage(...) is platform dependent. Only tested under OS X and Linux.
return
self.skipTest('not OSx neither Linux')
max_rss_before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * rusage_multiplier
for i in xrange(10000):
for _ in range(ITERATIONS):
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(0, 4096)
mu.mem_map(0, 0x1000)
mu.emu_start(0, 0)
max_rss_after = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * rusage_multiplier
rss_increase_per_iteration = (max_rss_after - max_rss_before) / i
self.assertLess(rss_increase_per_iteration, 8000)
rss_increase_per_iteration = (max_rss_after - max_rss_before) / ITERATIONS
self.assertLess(rss_increase_per_iteration, 8000.0)
if __name__ == '__main__':
regress.main()

View File

@ -1,21 +1,25 @@
#!/usr/bin/python
# By Ryan Hileman, issue #91
# Invalid instruction = test failed
import regress
from unicorn import *
from unicorn.x86_const import *
import regress
class Pshufb(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_X86, UC_MODE_64)
uc.ctl_set_cpu_model(UC_CPU_X86_HASWELL)
uc.mem_map(0x2000, 0x1000)
# pshufb xmm0, xmm1
uc.mem_write(0x2000, '660f3800c1'.decode('hex'))
uc.mem_write(0x2000, b'\x66\x0f\x38\x00\xc1') # pshufb xmm0, xmm1
# Invalid instruction -> test failed
uc.emu_start(0x2000, 0x2005)
if __name__ == '__main__':
regress.main()

View File

@ -2,30 +2,37 @@
"""See https://github.com/unicorn-engine/unicorn/issues/98"""
import unicorn
import regress
from unicorn import *
ADDR = 0xffaabbcc
def hook_mem_invalid(mu, access, address, size, value, user_data):
print ">>> Access type: %u, expected value: 0x%x, actual value: 0x%x" % (access, ADDR, address)
regress.logger.debug(">>> Access type: %u, expected value: 0x%x, actual value: 0x%x", access, ADDR, address)
assert(address == ADDR)
mu.mem_map(address & 0xfffff000, 4 * 1024)
mu.mem_write(address, b'\xcc')
return True
class RegWriteSignExt(regress.RegressTest):
def runTest(self):
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, ADDR)
mu = Uc(UC_ARCH_X86, UC_MODE_32)
mu.reg_write(x86_const.UC_X86_REG_EBX, ADDR)
mu.mem_map(0x10000000, 1024 * 4)
# jmp ebx
mu.mem_write(0x10000000, b'\xff\xe3')
mu.hook_add(unicorn.UC_HOOK_MEM_FETCH_UNMAPPED | unicorn.UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid)
mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid)
mu.emu_start(0x10000000, 0x10000000 + 2, count=1)
if __name__ == '__main__':
regress.main()

View File

@ -1,34 +1,64 @@
#!/usr/bin/python
#!/usr/bin/env python
import glob
import logging
import os
import unittest
from os.path import dirname, basename, isfile
import glob
# Find all unittest type in this directory and run it.
class RegressTest(unittest.TestCase):
pass
"""Regress test case dummy class.
"""
def __setup_logger(name):
"""Set up a unified logger for all tests.
"""
instance = logging.getLogger(name)
instance.propagate = False
handler = logging.StreamHandler()
formatter = logging.Formatter('[%(levelname)s] %(message)s')
if not instance.hasHandlers():
handler.setFormatter(formatter)
instance.addHandler(handler)
return instance
logger = __setup_logger('UnicornRegress')
logger.setLevel(logging.INFO)
def main():
unittest.main()
if __name__ == '__main__':
directory = dirname(__file__)
if directory == '':
directory = '.'
modules = glob.glob(directory+"/*.py")
__all__ = [ basename(f)[:-3] for f in modules if isfile(f)]
suite = unittest.TestSuite()
for module in __all__:
m = __import__(module)
for cl in dir(m):
try:
realcl = getattr(m,cl)
if issubclass(realcl, unittest.TestCase):
suite.addTest(realcl())
except Exception as e:
pass
logger.info('starting discovery')
# Find all unittest type in this directory and run it.
directory = os.path.dirname(__file__) or '.'
pyfiles = glob.glob(directory + '/*.py')
modules = [os.path.splitext(os.path.basename(f))[0] for f in pyfiles if os.path.isfile(f) and f != __file__]
logger.info('%d test modules found', len(modules))
for mname in modules:
try:
module = __import__(mname)
except ImportError as ex:
logger.error('could not load %s: %s is missing', mname, ex.name)
else:
tests = unittest.defaultTestLoader.loadTestsFromModule(module)
suite.addTests(tests)
logger.debug('found %d test cases in %s', tests.countTestCases(), mname)
logger.info('%d test cases were added', suite.countTestCases())
unittest.TextTestRunner().run(suite)

View File

@ -1,28 +1,36 @@
#!/usr/bin/python
from unicorn import *
from unicorn.x86_const import *
import regress
PAGE_SIZE = 4 * 1024
from unicorn import *
from unicorn.x86_const import *
PAGE_SIZE = 0x1000
CODE = b'\xf3\xaa' # rep stosb
BASE = 0x00000000
def hook_code(uc, addr, size, user_data):
print("hook called at %x" %addr)
class TestRep(regress.RegressTest):
class REP(regress.RegressTest):
def test_rep(self):
def runTest(self):
mu = Uc(UC_ARCH_X86, UC_MODE_32)
mu.mem_map(0, PAGE_SIZE)
mu.mem_write(0, CODE)
mu.reg_write(UC_X86_REG_ECX, 3)
mu.reg_write(UC_X86_REG_EDI, 0x100)
mu.hook_add(UC_HOOK_CODE, hook_code)
mu.mem_map(BASE, PAGE_SIZE)
mu.mem_write(BASE, CODE)
mu.reg_write(UC_X86_REG_ECX, 8)
mu.reg_write(UC_X86_REG_ESI, 0x10)
mu.reg_write(UC_X86_REG_EDI, 0x20)
def __hook_code(uc, addr, size, ud):
regress.logger.debug('iterations remaining: %d', uc.reg_read(UC_X86_REG_ECX))
mu.hook_add(UC_HOOK_CODE, __hook_code)
mu.emu_start(BASE, len(CODE))
mu.emu_start(0, len(CODE))
self.assertEqual(0, mu.reg_read(UC_X86_REG_ECX))

View File

@ -3,33 +3,31 @@
# This test demonstrates emulation behavior within and across
# basic blocks.
from __future__ import print_function
import binascii
import regress
import struct
import regress
from unicorn import *
from unicorn.x86_const import *
CODE = binascii.unhexlify(b"".join([
b"b800000000", # 1000: b8 00 00 00 00 mov eax,0x0
b"40", # 1005: 40 inc eax
b"40", # 1006: 40 inc eax
b"6810100000", # 1007: 68 10 10 00 00 push 0x1010
b"c3", # 100c: c3 ret
b"cc", # 100d: cc int3
b"cc", # 100e: cc int3
b"cc", # 100f: cc int3
b"b800000000", # 1010: b8 00 00 00 00 mov eax,0x0
b"40", # 1015: 40 inc eax
b"40", # 1016: 40 inc eax
]))
CODE = (
b"\xb8\x00\x00\x00\x00" # 1000: mov eax,0x0
b"\x40" # 1005: inc eax
b"\x40" # 1006: inc eax
b"\x68\x10\x10\x00\x00" # 1007: push 0x1010
b"\xc3" # 100c: ret
b"\xcc" # 100d: int3
b"\xcc" # 100e: int3
b"\xcc" # 100f: int3
b"\xb8\x00\x00\x00\x00" # 1010: mov eax,0x0
b"\x40" # 1015: inc eax
b"\x40" # 1016: inc eax
)
def showpc(mu):
pc = mu.reg_read(UC_X86_REG_EIP)
print("pc: 0x%x" % (pc))
regress.logger.debug("pc: 0x%x", mu.reg_read(UC_X86_REG_EIP))
class RunAcrossBBTest(regress.RegressTest):
@ -38,13 +36,13 @@ class RunAcrossBBTest(regress.RegressTest):
#######################################################################
# emu SETUP
#######################################################################
print("\n---- test: run_all ----")
regress.logger.debug("\n---- test: run_all ----")
mu = Uc(UC_ARCH_X86, UC_MODE_32)
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size))
regress.logger.debug(">>> Tracing instruction at 0x%x, instruction size = %u", address, size)
mu.hook_add(UC_HOOK_CODE, hook_code)
# base of CODE
@ -73,33 +71,34 @@ class RunAcrossBBTest(regress.RegressTest):
# 1010: b8 00 00 00 00 mov eax,0x0 <-+
# 1015: 40 inc eax <
# 1016: 40 inc eax <
self.assertEqual(0x1016, mu.reg_read(UC_X86_REG_EIP), "unexpected PC (2)")
self.assertEqual(0x2800, mu.reg_read(UC_X86_REG_ESP), "unexpected SP (2)")
showpc(mu)
except UcError as e:
eip = mu.reg_read(UC_X86_REG_EIP)
if e.errno == UC_ERR_FETCH_UNMAPPED:
# during initial test dev, bad fetch at 0x1010, but the data is there,
# and this proves it
print("!!! about to bail due to bad fetch... here's the data at PC:")
print(binascii.hexlify(mu.mem_read(mu.reg_read(UC_X86_REG_EIP), 0x8)))
self.assertFalse(True, "ERROR: %s @ 0x%x" % (e, mu.reg_read(UC_X86_REG_EIP)))
# during initial test dev, bad fetch at 0x1010, but the data is there, and this proves it
regress.logger.error("!!! about to bail due to bad fetch... here's the data at PC:")
regress.logger.error(binascii.hexlify(mu.mem_read(eip, 8)))
self.fail("ERROR: %s @ 0x%x" % (e, eip))
def test_run_across_bb(self):
try:
#######################################################################
# emu SETUP
#######################################################################
print("\n---- test: run_across_bb ----")
regress.logger.debug("\n---- test: run_across_bb ----")
mu = Uc(UC_ARCH_X86, UC_MODE_32)
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size))
regress.logger.debug(">>> Tracing instruction at 0x%x, instruction size = %u", address, size)
mu.hook_add(UC_HOOK_CODE, hook_code)
# base of CODE
@ -111,8 +110,10 @@ class RunAcrossBBTest(regress.RegressTest):
mu.reg_write(UC_X86_REG_EIP, 0x1000)
mu.reg_write(UC_X86_REG_ESP, 0x2800)
self.assertEqual(0x1000, mu.reg_read(UC_X86_REG_EIP), "unexpected PC")
self.assertEqual(0x2800, mu.reg_read(UC_X86_REG_ESP), "unexpected SP")
showpc(mu)
@ -182,13 +183,14 @@ class RunAcrossBBTest(regress.RegressTest):
showpc(mu)
except UcError as e:
if e.errno == UC_ERR_FETCH_UNMAPPED:
# during initial test dev, bad fetch at 0x1010, but the data is there,
# and this proves it
print("!!! about to bail due to bad fetch... here's the data at PC:")
print(binascii.hexlify(mu.mem_read(mu.reg_read(UC_X86_REG_EIP), 0x8)))
eip = mu.reg_read(UC_X86_REG_EIP)
self.assertFalse(True, "ERROR: %s @ 0x%x" % (e, mu.reg_read(UC_X86_REG_EIP)))
if e.errno == UC_ERR_FETCH_UNMAPPED:
# during initial test dev, bad fetch at 0x1010, but the data is there, and this proves it
regress.logger.error("!!! about to bail due to bad fetch... here's the data at PC:")
regress.logger.error(binascii.hexlify(mu.mem_read(eip, 8)))
self.fail("ERROR: %s @ 0x%x" % (e, eip))
if __name__ == '__main__':

View File

@ -1,24 +1,33 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.sparc_const import *
PAGE_SIZE = 1 * 1024 * 1024
uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN)
uc.reg_write(UC_SPARC_REG_SP, 100)
print 'writing sp = 100'
CODE = (
b"\xb0\x06\x20\x01" # 0: b0 06 20 01 inc %i0
b"\xb2\x06\x60\x01" # 4: b2 06 60 01 inc %i1
)
# 0: b0 06 20 01 inc %i0
# 4: b2 06 60 01 inc %i1
BASE = 0x00000000
CODE = "\xb0\x06\x20\x01" \
"\xb2\x06\x60\x01"
class TestSparcRegRead(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN)
uc.mem_map(0, PAGE_SIZE)
uc.mem_write(0, CODE)
uc.emu_start(0, len(CODE), 0, 2)
uc.mem_map(BASE, 0x1000 ** 2)
uc.mem_write(BASE, CODE)
print 'sp =', uc.reg_read(UC_SPARC_REG_SP)
print 'i0 =', uc.reg_read(UC_SPARC_REG_I0)
print 'i1 =', uc.reg_read(UC_SPARC_REG_I1)
uc.reg_write(UC_SPARC_REG_SP, 100)
uc.emu_start(BASE, BASE + len(CODE), count=2)
regress.logger.debug('sp = %#x', uc.reg_read(UC_SPARC_REG_SP))
regress.logger.debug('i0 = %#x', uc.reg_read(UC_SPARC_REG_I0))
regress.logger.debug('i1 = %#x', uc.reg_read(UC_SPARC_REG_I1))
if __name__ == '__main__':
regress.main()

View File

@ -1,205 +1,109 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.sparc_const import *
PAGE_SIZE = 1 * 1024 * 1024
uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN)
uc.reg_write(UC_SPARC_REG_SP, 100)
uc.reg_write(UC_SPARC_REG_FP, 200)
CODE =(
b"\x80\x00\x20\x01" # add %g0, 1, %g0
b"\x82\x00\x60\x01" # add %g1, 1, %g1
b"\x84\x00\xA0\x01" # add %g2, 1, %g2
b"\x86\x00\xE0\x01" # add %g3, 1, %g3
b"\x88\x01\x20\x01" # add %g4, 1, %g4
b"\x8A\x01\x60\x01" # add %g5, 1, %g5
b"\x8C\x01\xA0\x01" # add %g6, 1, %g6
b"\x8E\x01\xE0\x01" # add %g7, 1, %g7
b"\x90\x02\x20\x01" # add %o0, 1, %o0
b"\x92\x02\x60\x01" # add %o1, 1, %o1
b"\x94\x02\xA0\x01" # add %o2, 1, %o2
b"\x96\x02\xE0\x01" # add %o3, 1, %o3
b"\x98\x03\x20\x01" # add %o4, 1, %o4
b"\x9A\x03\x60\x01" # add %o5, 1, %o5
b"\x9C\x03\xA0\x01" # add %sp, 1, %sp
b"\x9E\x03\xE0\x01" # add %o7, 1, %o7
b"\xA0\x04\x20\x01" # add %l0, 1, %l0
b"\xA2\x04\x60\x01" # add %l1, 1, %l1
b"\xA4\x04\xA0\x01" # add %l2, 1, %l2
b"\xA6\x04\xE0\x01" # add %l3, 1, %l3
b"\xA8\x05\x20\x01" # add %l4, 1, %l4
b"\xAA\x05\x60\x01" # add %l5, 1, %l5
b"\xAC\x05\xA0\x01" # add %l6, 1, %l6
b"\xAE\x05\xE0\x01" # add %l7, 1, %l7
b"\xB0\x06\x20\x01" # add %i0, 1, %i0
b"\xB2\x06\x60\x01" # add %i1, 1, %i1
b"\xB4\x06\xA0\x01" # add %i2, 1, %i2
b"\xB6\x06\xE0\x01" # add %i3, 1, %i3
b"\xB8\x07\x20\x01" # add %i4, 1, %i4
b"\xBA\x07\x60\x01" # add %i5, 1, %i5
b"\xBC\x07\xA0\x01" # add %fp, 1, %fp
b"\xBE\x07\xE0\x01" # add %i7, 1, %i7
)
# 0x0: \x80\x00\x20\x01 add %g0, 1, %g0
# 0x4: \x82\x00\x60\x01 add %g1, 1, %g1
# 0x8: \x84\x00\xA0\x01 add %g2, 1, %g2
# 0xc: \x86\x00\xE0\x01 add %g3, 1, %g3
# 0x10: \x88\x01\x20\x01 add %g4, 1, %g4
# 0x14: \x8A\x01\x60\x01 add %g5, 1, %g5
# 0x18: \x8C\x01\xA0\x01 add %g6, 1, %g6
# 0x1c: \x8E\x01\xE0\x01 add %g7, 1, %g7
# 0x20: \x90\x02\x20\x01 add %o0, 1, %o0
# 0x24: \x92\x02\x60\x01 add %o1, 1, %o1
# 0x28: \x94\x02\xA0\x01 add %o2, 1, %o2
# 0x2c: \x96\x02\xE0\x01 add %o3, 1, %o3
# 0x30: \x98\x03\x20\x01 add %o4, 1, %o4
# 0x34: \x9A\x03\x60\x01 add %o5, 1, %o5
# 0x38: \x9C\x03\xA0\x01 add %sp, 1, %sp
# 0x3c: \x9E\x03\xE0\x01 add %o7, 1, %o7
# 0x40: \xA0\x04\x20\x01 add %l0, 1, %l0
# 0x44: \xA2\x04\x60\x01 add %l1, 1, %l1
# 0x48: \xA4\x04\xA0\x01 add %l2, 1, %l2
# 0x4c: \xA6\x04\xE0\x01 add %l3, 1, %l3
# 0x50: \xA8\x05\x20\x01 add %l4, 1, %l4
# 0x54: \xAA\x05\x60\x01 add %l5, 1, %l5
# 0x58: \xAC\x05\xA0\x01 add %l6, 1, %l6
# 0x5c: \xAE\x05\xE0\x01 add %l7, 1, %l7
# 0x0: \xB0\x06\x20\x01 add %i0, 1, %i0
# 0x4: \xB2\x06\x60\x01 add %i1, 1, %i1
# 0x8: \xB4\x06\xA0\x01 add %i2, 1, %i2
# 0xc: \xB6\x06\xE0\x01 add %i3, 1, %i3
# 0x10: \xB8\x07\x20\x01 add %i4, 1, %i4
# 0x14: \xBA\x07\x60\x01 add %i5, 1, %i5
# 0x18: \xBC\x07\xA0\x01 add %fp, 1, %fp
# 0x1c: \xBE\x07\xE0\x01 add %i7, 1, %i7
BASE = 0x00000000
CODE = "\x80\x00\x20\x01" \
"\x82\x00\x60\x01" \
"\x84\x00\xA0\x01" \
"\x86\x00\xE0\x01" \
"\x88\x01\x20\x01" \
"\x8A\x01\x60\x01" \
"\x8C\x01\xA0\x01" \
"\x8E\x01\xE0\x01" \
"\x90\x02\x20\x01" \
"\x92\x02\x60\x01" \
"\x94\x02\xA0\x01" \
"\x96\x02\xE0\x01" \
"\x98\x03\x20\x01" \
"\x9A\x03\x60\x01" \
"\x9C\x03\xA0\x01" \
"\x9E\x03\xE0\x01" \
"\xA0\x04\x20\x01" \
"\xA2\x04\x60\x01" \
"\xA4\x04\xA0\x01" \
"\xA6\x04\xE0\x01" \
"\xA8\x05\x20\x01" \
"\xAA\x05\x60\x01" \
"\xAC\x05\xA0\x01" \
"\xAE\x05\xE0\x01" \
"\xB0\x06\x20\x01" \
"\xB2\x06\x60\x01" \
"\xB4\x06\xA0\x01" \
"\xB6\x06\xE0\x01" \
"\xB8\x07\x20\x01" \
"\xBA\x07\x60\x01" \
"\xBC\x07\xA0\x01" \
"\xBE\x07\xE0\x01"
def hook_code(uc, addr, size, ud):
regress.logger.debug("executing at 0x%04x", uc.reg_read(UC_SPARC_REG_PC))
uc.mem_map(0, PAGE_SIZE)
uc.mem_write(0, CODE)
uc.emu_start(0, len(CODE), 0, 32)
class TestSparcRegRead(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32 | UC_MODE_BIG_ENDIAN)
def print_registers(mu):
g0 = mu.reg_read(UC_SPARC_REG_G0)
g1 = mu.reg_read(UC_SPARC_REG_G1)
g2 = mu.reg_read(UC_SPARC_REG_G2)
g3 = mu.reg_read(UC_SPARC_REG_G3)
g4 = mu.reg_read(UC_SPARC_REG_G4)
g5 = mu.reg_read(UC_SPARC_REG_G5)
g6 = mu.reg_read(UC_SPARC_REG_G6)
g7 = mu.reg_read(UC_SPARC_REG_G7)
uc.reg_write(UC_SPARC_REG_SP, 100)
uc.reg_write(UC_SPARC_REG_FP, 200)
o0 = mu.reg_read(UC_SPARC_REG_O0)
o1 = mu.reg_read(UC_SPARC_REG_O1)
o2 = mu.reg_read(UC_SPARC_REG_O2)
o3 = mu.reg_read(UC_SPARC_REG_O3)
o4 = mu.reg_read(UC_SPARC_REG_O4)
o5 = mu.reg_read(UC_SPARC_REG_O5)
o6 = mu.reg_read(UC_SPARC_REG_O6)
o7 = mu.reg_read(UC_SPARC_REG_O7)
uc.mem_map(BASE, PAGE_SIZE)
uc.mem_write(BASE, CODE)
l0 = mu.reg_read(UC_SPARC_REG_L0)
l1 = mu.reg_read(UC_SPARC_REG_L1)
l2 = mu.reg_read(UC_SPARC_REG_L2)
l3 = mu.reg_read(UC_SPARC_REG_L3)
l4 = mu.reg_read(UC_SPARC_REG_L4)
l5 = mu.reg_read(UC_SPARC_REG_L5)
l6 = mu.reg_read(UC_SPARC_REG_L6)
l7 = mu.reg_read(UC_SPARC_REG_L7)
uc.hook_add(UC_HOOK_CODE, hook_code)
uc.emu_start(BASE, len(CODE))
i0 = mu.reg_read(UC_SPARC_REG_I0)
i1 = mu.reg_read(UC_SPARC_REG_I1)
i2 = mu.reg_read(UC_SPARC_REG_I2)
i3 = mu.reg_read(UC_SPARC_REG_I3)
i4 = mu.reg_read(UC_SPARC_REG_I4)
i5 = mu.reg_read(UC_SPARC_REG_I5)
i6 = mu.reg_read(UC_SPARC_REG_I6)
i7 = mu.reg_read(UC_SPARC_REG_I7)
self.assertEqual(0, uc.reg_read(UC_SPARC_REG_G0)) # G0 is always zero
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G1))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G2))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G3))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G4))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G5))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G6))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_G7))
pc = mu.reg_read(UC_SPARC_REG_PC)
sp = mu.reg_read(UC_SPARC_REG_SP)
fp = mu.reg_read(UC_SPARC_REG_FP)
print(" G0 = %d" % g0)
print(" G1 = %d" % g1)
print(" G2 = %d" % g2)
print(" G3 = %d" % g3)
print(" G4 = %d" % g4)
print(" G5 = %d" % g5)
print(" G6 = %d" % g6)
print(" G7 = %d" % g7)
print("")
print(" O0 = %d" % o0)
print(" O1 = %d" % o1)
print(" O2 = %d" % o2)
print(" O3 = %d" % o3)
print(" O4 = %d" % o4)
print(" O5 = %d" % o5)
print(" O6 = %d" % o6)
print(" O7 = %d" % o7)
print("")
print(" L0 = %d" % l0)
print(" L1 = %d" % l1)
print(" L2 = %d" % l2)
print(" L3 = %d" % l3)
print(" L4 = %d" % l4)
print(" L5 = %d" % l5)
print(" L6 = %d" % l6)
print(" L7 = %d" % l7)
print("")
print(" I0 = %d" % i0)
print(" I1 = %d" % i1)
print(" I2 = %d" % i2)
print(" I3 = %d" % i3)
print(" I4 = %d" % i4)
print(" I5 = %d" % i5)
print(" I6 = %d" % i6)
print(" I7 = %d" % i7)
print("")
print(" PC = %d" % pc)
print(" SP = %d" % sp)
print(" FP = %d" % fp)
print("")
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O0))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O1))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O2))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O3))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O4))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O5))
self.assertEqual(101, uc.reg_read(UC_SPARC_REG_O6))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_O7))
print_registers(uc)
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L0))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L1))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L2))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L3))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L4))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L5))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L6))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_L7))
assert uc.reg_read(UC_SPARC_REG_PC) == 132 # make sure we executed all instructions
assert uc.reg_read(UC_SPARC_REG_SP) == 101
assert uc.reg_read(UC_SPARC_REG_FP) == 201
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I0))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I1))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I2))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I3))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I4))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I5))
self.assertEqual(201, uc.reg_read(UC_SPARC_REG_I6))
self.assertEqual(1, uc.reg_read(UC_SPARC_REG_I7))
assert uc.reg_read(UC_SPARC_REG_G0) == 0 # G0 is always zero
assert uc.reg_read(UC_SPARC_REG_G1) == 1
assert uc.reg_read(UC_SPARC_REG_G2) == 1
assert uc.reg_read(UC_SPARC_REG_G3) == 1
assert uc.reg_read(UC_SPARC_REG_G4) == 1
assert uc.reg_read(UC_SPARC_REG_G5) == 1
assert uc.reg_read(UC_SPARC_REG_G6) == 1
assert uc.reg_read(UC_SPARC_REG_G7) == 1
# BUG: PC seems to get reset to 4 when done executing
# self.assertEqual(4 * 32, uc.reg_read(UC_SPARC_REG_PC)) # make sure we executed all instructions
self.assertEqual(101, uc.reg_read(UC_SPARC_REG_SP))
self.assertEqual(201, uc.reg_read(UC_SPARC_REG_FP))
assert uc.reg_read(UC_SPARC_REG_O0) == 1
assert uc.reg_read(UC_SPARC_REG_O1) == 1
assert uc.reg_read(UC_SPARC_REG_O2) == 1
assert uc.reg_read(UC_SPARC_REG_O3) == 1
assert uc.reg_read(UC_SPARC_REG_O4) == 1
assert uc.reg_read(UC_SPARC_REG_O5) == 1
assert uc.reg_read(UC_SPARC_REG_O6) == 101
assert uc.reg_read(UC_SPARC_REG_O7) == 1
assert uc.reg_read(UC_SPARC_REG_L0) == 1
assert uc.reg_read(UC_SPARC_REG_L1) == 1
assert uc.reg_read(UC_SPARC_REG_L2) == 1
assert uc.reg_read(UC_SPARC_REG_L3) == 1
assert uc.reg_read(UC_SPARC_REG_L4) == 1
assert uc.reg_read(UC_SPARC_REG_L5) == 1
assert uc.reg_read(UC_SPARC_REG_L6) == 1
assert uc.reg_read(UC_SPARC_REG_L7) == 1
assert uc.reg_read(UC_SPARC_REG_I0) == 1
assert uc.reg_read(UC_SPARC_REG_I1) == 1
assert uc.reg_read(UC_SPARC_REG_I2) == 1
assert uc.reg_read(UC_SPARC_REG_I3) == 1
assert uc.reg_read(UC_SPARC_REG_I4) == 1
assert uc.reg_read(UC_SPARC_REG_I5) == 1
assert uc.reg_read(UC_SPARC_REG_I6) == 201
assert uc.reg_read(UC_SPARC_REG_I7) == 1
if __name__ == '__main__':
regress.main()

View File

@ -1,134 +1,150 @@
from unicorn import *

import regress
from unicorn import *
from unicorn.arm_const import *
import binascii
MB = 1024 * 1024
PAGE = 4 * 1024
def PrintArmRegisters(uc_emu):
print 'R0 : '+hex(uc_emu.reg_read(UC_ARM_REG_R0))
print 'R1 : '+hex(uc_emu.reg_read(UC_ARM_REG_R1))
print 'R2 : '+hex(uc_emu.reg_read(UC_ARM_REG_R2))
print 'R3 : '+hex(uc_emu.reg_read(UC_ARM_REG_R3))
print 'R4 : '+hex(uc_emu.reg_read(UC_ARM_REG_R4))
print 'R5 : '+hex(uc_emu.reg_read(UC_ARM_REG_R5))
print 'R6 : '+hex(uc_emu.reg_read(UC_ARM_REG_R6))
print 'R7 : '+hex(uc_emu.reg_read(UC_ARM_REG_R7))
print 'R8 : '+hex(uc_emu.reg_read(UC_ARM_REG_R8))
print 'R9 : '+hex(uc_emu.reg_read(UC_ARM_REG_R9))
print 'R10 : '+hex(uc_emu.reg_read(UC_ARM_REG_R10))
print 'R11 : '+hex(uc_emu.reg_read(UC_ARM_REG_R11))
print 'R12 : '+hex(uc_emu.reg_read(UC_ARM_REG_R12))
print 'SP : '+hex(uc_emu.reg_read(UC_ARM_REG_SP))
print 'LR : '+hex(uc_emu.reg_read(UC_ARM_REG_LR))
print 'PC : '+hex(uc_emu.reg_read(UC_ARM_REG_PC))
flags = uc_emu.reg_read(UC_ARM_REG_CPSR)
print 'carry : '+str(flags >> 29 & 0x1)
print 'overflow : '+str(flags >> 28 & 0x1)
print 'negative : '+str(flags >> 31 & 0x1)
print 'zero : '+str(flags >> 30 & 0x1)
'''
issue #287
Initial Register States: R0=3, R1=24, R2=16, R3=0
----- code start -----
CMP R0,R1,LSR#3
SUBCS R0,R0,R1,LSR#3 # CPU flags got changed in these two instructions, and *REMEMBERED*, now NF == VF == 0
CMP R0,#1 # CPU flags changed again, now NF == 1, VF == 0, but they are not properly *REMEMBERED*
MOV R1,R1,LSR#4
SUBGES R2,R2,#4 # according to the result of CMP, we should skip this op
MOVGE R3,#100 # since changed flags are not *REMEMBERED* in CMP, now NF == VF == 0, which result in wrong branch
# at the end of this code block, should R3 == 0
----- code end ------
# issue #287
# Initial Register States: R0=3, R1=24, R2=16, R3=0
#
# ----- code start -----
# CMP R0,R1,LSR#3
# SUBCS R0,R0,R1,LSR#3 # CPU flags got changed in these two instructions, and *REMEMBERED*, now NF == VF == 0
# CMP R0,#1 # CPU flags changed again, now NF == 1, VF == 0, but they are not properly *REMEMBERED*
# MOV R1,R1,LSR#4
# SUBGES R2,R2,#4 # according to the result of CMP, we should skip this op
#
# MOVGE R3,#100 # since changed flags are not *REMEMBERED* in CMP, now NF == VF == 0, which result in wrong branch
# # at the end of this code block, should R3 == 0
# ----- code end ------
#
# TCG ops are correct, plain op translation is done correctly,
# but there're In-Memory bits invisible from ops that control the host code generation.
# all these codes are in one TCG translation-block, so wrong things could happen.
# detail explanation is given on the right side.
# remember, both set_label and brcond are point to refresh the dead_temps and mem_temps states in TCG
#
# ----- TCG ops ------
# ld_i32 tmp5,env,$0xfffffffffffffff4
# movi_i32 tmp6,$0x0
# brcond_i32 tmp5,tmp6,ne,$0x0
# mov_i32 tmp5,r1 -------------------------
# movi_i32 tmp6,$0x3 |
# shr_i32 tmp5,r1,tmp6 |
# mov_i32 tmp6,r0 |
# sub_i32 NF,r0,tmp5 |
# mov_i32 ZF,NF |
# setcond_i32 CF,r0,tmp5,geu | # This part is "CMP R0,R1,LSR#3"
# xor_i32 VF,NF,r0 |-----> # and "SUBCS R0,R0,R1,LSR#3"
# xor_i32 tmp7,r0,tmp5 | # the last op in this block, set_label get a chance to refresh the TCG globals memory states,
# and_i32 VF,VF,tmp7 | # so things get back to normal states
# mov_i32 tmp6,NF | # these codes are not affected by the bug. Let's called this Part-D
# movi_i32 tmp5,$0x0 |
# brcond_i32 CF,tmp5,eq,$0x1 |
# mov_i32 tmp5,r1 |
# movi_i32 tmp6,$0x3 |
# shr_i32 tmp5,r1,tmp6 |
# mov_i32 tmp6,r0 |
# sub_i32 tmp6,r0,tmp5 |
# mov_i32 r0,tmp6 |
# set_label $0x1 -------------------------
# movi_i32 tmp5,$0x1 ----------------- # Let's called this Part-C
# mov_i32 tmp6,r0 | # NF is used as output operand again!
# sub_i32 NF,r0,tmp5 ----------------|-----> # but it is stated as Not-In-Memory,
# mov_i32 ZF,NF | # no need to sync it after calculation.
# setcond_i32 CF,r0,tmp5,geu | # the generated host code does not write NF
# xor_i32 VF,NF,r0 | # back to its memory location, hence forgot. And the CPU flags after this calculation is not changed.
# xor_i32 tmp7,r0,tmp5 | # Caution: the following SUBGES's condition check is right, even though the generated host code does not *REMEMBER* NF, it will cache the calculated result and serve SUBGES correctly
# and_i32 VF,VF,tmp7 |
# mov_i32 tmp6,NF |
# mov_i32 tmp5,r1 | # this part is "CMP R0,#1"
# movi_i32 tmp6,$0x4 | # and "MOV R1,R1,LSR#4"
# shr_i32 tmp5,r1,tmp6 | # and "SUBGES R2,R2,#4"
# mov_i32 r1,tmp5 |-----> # This is the part where problem start to arise
# xor_i32 tmp5,VF,NF |
# movi_i32 tmp6,$0x0 |
# brcond_i32 tmp5,tmp6,lt,$0x2 --------|-----> # QEMU will refresh the InMemory bit for TCG globals here, but Unicorn won't
# movi_i32 tmp5,$0x4 |
# mov_i32 tmp6,r2 | # this is the 1st bug-related op get analyzed.
# sub_i32 NF,r2,tmp5 ----------------|-----> # here, NF is an output operand, it's flagged dead
# mov_i32 ZF,NF | # and the InMemory bit is clear, tell the previous(above) ops
# setcond_i32 CF,r2,tmp5,geu | # if it is used as output operand again, do not sync it
# xor_i32 VF,NF,r2 | # so the generated host-code for previous ops will not write it back to Memory
# xor_i32 tmp7,r2,tmp5 | # Caution: the CPU flags after this calculation is also right, because the set_label is a point of refresh, make them *REMEMBERED*
# and_i32 VF,VF,tmp7 | # Let's call this Part-B
# mov_i32 tmp6,NF |
# mov_i32 r2,ZF |
# set_label $0x2 -----------------
# xor_i32 tmp5,VF,NF -----------------
# movi_i32 tmp6,$0x0 |
# brcond_i32 tmp5,tmp6,lt,$0x3 | # Let's call this Part-A
# movi_i32 tmp5,$0x64 | # if Part-B is not skipped, this part won't go wrong, because we'll check the CPU flags as the result of Part-B, it's *REMEMBERED*
# movi_i32 r3,$0x64 |-----> # but if Part-B is skipped,
# set_label $0x3 | # what should we expected? we will check the condition based on the result of Part-D!!!
# call wfi,$0x0,$0,env | # because result of Part-C is lost. this is why things go wrong.
# set_label $0x0 |
# exit_tb $0x7f6401714013 -----------------
# ###########
# ----- TCG ends ------
# TCG ops are correct, plain op translation is done correctly,
# but there're In-Memory bits invisible from ops that control the host code generation.
# all these codes are in one TCG translation-block, so wrong things could happen.
# detail explanation is given on the right side.
# remember, both set_label and brcond are point to refresh the dead_temps and mem_temps states in TCG
----- TCG ops ------
ld_i32 tmp5,env,$0xfffffffffffffff4
movi_i32 tmp6,$0x0
brcond_i32 tmp5,tmp6,ne,$0x0
mov_i32 tmp5,r1 -------------------------
movi_i32 tmp6,$0x3 |
shr_i32 tmp5,r1,tmp6 |
mov_i32 tmp6,r0 |
sub_i32 NF,r0,tmp5 |
mov_i32 ZF,NF |
setcond_i32 CF,r0,tmp5,geu | # This part is "CMP R0,R1,LSR#3"
xor_i32 VF,NF,r0 |-----> # and "SUBCS R0,R0,R1,LSR#3"
xor_i32 tmp7,r0,tmp5 | # the last op in this block, set_label get a chance to refresh the TCG globals memory states,
and_i32 VF,VF,tmp7 | # so things get back to normal states
mov_i32 tmp6,NF | # these codes are not affected by the bug. Let's called this Part-D
movi_i32 tmp5,$0x0 |
brcond_i32 CF,tmp5,eq,$0x1 |
mov_i32 tmp5,r1 |
movi_i32 tmp6,$0x3 |
shr_i32 tmp5,r1,tmp6 |
mov_i32 tmp6,r0 |
sub_i32 tmp6,r0,tmp5 |
mov_i32 r0,tmp6 |
set_label $0x1 -------------------------
movi_i32 tmp5,$0x1 ----------------- # Let's called this Part-C
mov_i32 tmp6,r0 | # NF is used as output operand again!
sub_i32 NF,r0,tmp5 ----------------|-----> # but it is stated as Not-In-Memory,
mov_i32 ZF,NF | # no need to sync it after calculation.
setcond_i32 CF,r0,tmp5,geu | # the generated host code does not write NF
xor_i32 VF,NF,r0 | # back to its memory location, hence forgot. And the CPU flags after this calculation is not changed.
xor_i32 tmp7,r0,tmp5 | # Caution: the following SUBGES's condition check is right, even though the generated host code does not *REMEMBER* NF, it will cache the calculated result and serve SUBGES correctly
and_i32 VF,VF,tmp7 |
mov_i32 tmp6,NF |
mov_i32 tmp5,r1 | # this part is "CMP R0,#1"
movi_i32 tmp6,$0x4 | # and "MOV R1,R1,LSR#4"
shr_i32 tmp5,r1,tmp6 | # and "SUBGES R2,R2,#4"
mov_i32 r1,tmp5 |-----> # This is the part where problem start to arise
xor_i32 tmp5,VF,NF |
movi_i32 tmp6,$0x0 |
brcond_i32 tmp5,tmp6,lt,$0x2 --------|-----> # QEMU will refresh the InMemory bit for TCG globals here, but Unicorn won't
movi_i32 tmp5,$0x4 |
mov_i32 tmp6,r2 | # this is the 1st bug-related op get analyzed.
sub_i32 NF,r2,tmp5 ----------------|-----> # here, NF is an output operand, it's flagged dead
mov_i32 ZF,NF | # and the InMemory bit is clear, tell the previous(above) ops
setcond_i32 CF,r2,tmp5,geu | # if it is used as output operand again, do not sync it
xor_i32 VF,NF,r2 | # so the generated host-code for previous ops will not write it back to Memory
xor_i32 tmp7,r2,tmp5 | # Caution: the CPU flags after this calculation is also right, because the set_label is a point of refresh, make them *REMEMBERED*
and_i32 VF,VF,tmp7 | # Let's call this Part-B
mov_i32 tmp6,NF |
mov_i32 r2,ZF |
set_label $0x2 -----------------
xor_i32 tmp5,VF,NF -----------------
movi_i32 tmp6,$0x0 |
brcond_i32 tmp5,tmp6,lt,$0x3 | # Let's call this Part-A
movi_i32 tmp5,$0x64 | # if Part-B is not skipped, this part won't go wrong, because we'll check the CPU flags as the result of Part-B, it's *REMEMBERED*
movi_i32 r3,$0x64 |-----> # but if Part-B is skipped,
set_label $0x3 | # what should we expected? we will check the condition based on the result of Part-D!!!
call wfi,$0x0,$0,env | # because result of Part-C is lost. this is why things go wrong.
set_label $0x0 |
exit_tb $0x7f6401714013 -----------------
###########
----- TCG ends ------
'''
TestCode = b'\xa1\x01\x50\xe1\xa1\x01\x40\x20\x01\x00\x50\xe3\x21\x12\xa0\xe1\x04\x20\x52\xa2\x64\x30\xa0\xa3'
CODE = (
b'\xa1\x01\x50\xe1' # cmp r0, r1, lsr #3
b'\xa1\x01\x40\x20' # subhs r0, r0, r1, lsr #3
b'\x01\x00\x50\xe3' # cmp r0, #1
b'\x21\x12\xa0\xe1' # lsr r1, r1, #4
b'\x04\x20\x52\xa2' # subsge r2, r2, #4
b'\x64\x30\xa0\xa3' # movge r3, #0x64
)
def UseUcToEmulate():
try:
uc_emu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
#if LoadCode(uc_emu, 2*MB, 0x9004):
uc_emu.mem_map(0, 2*MB)
uc_emu.reg_write(UC_ARM_REG_SP, 0x40000)
uc_emu.reg_write(UC_ARM_REG_R0, 3)
uc_emu.reg_write(UC_ARM_REG_R1, 24)
uc_emu.reg_write(UC_ARM_REG_R2, 16)
uc_emu.mem_write(0, TestCode)
uc_emu.emu_start(0, 24)
PrintArmRegisters(uc_emu)
except UcError as e:
print("ERROR: %s" % e)
PrintArmRegisters(uc_emu)
BASE = 0x00000000
UseUcToEmulate()
def show_regs(uc):
regress.logger.debug('R0 : %08x', uc.reg_read(UC_ARM_REG_R0))
regress.logger.debug('R1 : %08x', uc.reg_read(UC_ARM_REG_R1))
regress.logger.debug('R2 : %08x', uc.reg_read(UC_ARM_REG_R2))
regress.logger.debug('R3 : %08x', uc.reg_read(UC_ARM_REG_R3))
regress.logger.debug('R4 : %08x', uc.reg_read(UC_ARM_REG_R4))
regress.logger.debug('R5 : %08x', uc.reg_read(UC_ARM_REG_R5))
regress.logger.debug('R6 : %08x', uc.reg_read(UC_ARM_REG_R6))
regress.logger.debug('R7 : %08x', uc.reg_read(UC_ARM_REG_R7))
regress.logger.debug('R8 : %08x', uc.reg_read(UC_ARM_REG_R8))
regress.logger.debug('R9 : %08x', uc.reg_read(UC_ARM_REG_R9))
regress.logger.debug('R10 : %08x', uc.reg_read(UC_ARM_REG_R10))
regress.logger.debug('R11 : %08x', uc.reg_read(UC_ARM_REG_R11))
regress.logger.debug('R12 : %08x', uc.reg_read(UC_ARM_REG_R12))
regress.logger.debug('SP : %08x', uc.reg_read(UC_ARM_REG_SP))
regress.logger.debug('LR : %08x', uc.reg_read(UC_ARM_REG_LR))
regress.logger.debug('PC : %08x', uc.reg_read(UC_ARM_REG_PC))
flags = uc.reg_read(UC_ARM_REG_CPSR)
regress.logger.debug('carry : %d', (flags >> 29) & 0b1)
regress.logger.debug('overflow : %d', (flags >> 28) & 0b1)
regress.logger.debug('negative : %d', (flags >> 31) & 0b1)
regress.logger.debug('zero : %d', (flags >> 30) & 0b1)
class TestReadMem(regress.RegressTest):
def runTest(self):
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
uc.mem_map(BASE, 0x1000)
uc.reg_write(UC_ARM_REG_SP, 0x40000)
uc.reg_write(UC_ARM_REG_R0, 3)
uc.reg_write(UC_ARM_REG_R1, 24)
uc.reg_write(UC_ARM_REG_R2, 16)
uc.reg_write(UC_ARM_REG_R3, 0)
uc.mem_write(BASE, CODE)
uc.emu_start(BASE, len(CODE))
show_regs(uc)
self.assertEqual(0, uc.reg_read(UC_ARM_REG_R3))
if __name__ == '__main__':
regress.main()

View File

@ -1,22 +1,32 @@
#!/usr/bin/python
# By Mariano Graziano
import struct
import regress
from unicorn import *
from unicorn.x86_const import *
import regress, struct
class Emulator:
def __init__(self, code, stack):
self.mask = 0xFFFFFFFFFFFFF000
def __page_aligned(address):
return address & ~(0x1000 - 1)
self.unicorn_code = code
self.unicorn_stack = stack
self.mu = Uc(UC_ARCH_X86, UC_MODE_64)
size = 1 * 4096
self.mu.mem_map(code & self.mask, size)
size = 1 * 4096
self.mu.mem_map(stack & self.mask, size)
regress.logger.debug("mapping code : %#x", __page_aligned(code))
regress.logger.debug("mapping stack : %#x", __page_aligned(stack))
self.mu.mem_map(__page_aligned(code), 0x1000)
self.mu.mem_map(__page_aligned(stack), 0x1000)
self.mu.reg_write(UC_X86_REG_RSP, stack)
self.mu.reg_write(UC_X86_REG_RIP, code)
self.set_hooks()
def set_hooks(self):
@ -26,53 +36,55 @@ class Emulator:
def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data):
next_ip = self.unicorn_code + size
self.mu.reg_write(UC_X86_REG_RIP, next_ip)
self.mu.mem_write(next_ip, "\x90")
self.mu.reg_write(UC_X86_REG_RIP, address)
self.write_reg(UC_X86_REG_RIP, next_ip)
self.write_data(next_ip, b"\x90")
# self.write_reg(UC_X86_REG_RIP, address) # ???
return True
def hook_mem_invalid(self, uc, access, address, size, value, user_data):
regress.logger.debug("invalid mem access: access type = %d, to = %#x, size = %u, value = %#x", access, address, size, value)
return True
def hook_mem_access(self, uc, access, address, size, value, user_data):
return True
def emu(self, size):
def emu(self, steps):
ip = self.mu.reg_read(UC_X86_REG_RIP)
try:
self.mu.emu_start(ip, ip + size, timeout=10000, count=1)
except UcError as e:
print("Error %s" % e)
max_intel_insn_size = 15
regress.logger.debug("starting at : %#x", ip)
self.mu.emu_start(ip, ip + max_intel_insn_size, count=steps)
def write_data(self, address, content):
self.mu.mem_write(address, content)
def write_reg(self, reg, value):
self.mu.reg_write(reg, value)
class Init(regress.RegressTest):
def init_unicorn(self, ip, sp, counter):
#print "[+] Emulating IP: %x SP: %x - Counter: %x" % (ip, sp, counter)
E = Emulator(ip, sp)
E.write_data(ip, "\x90")
E.write_data(sp, self.generate_value(counter))
E.mu.reg_write(UC_X86_REG_RSP, sp)
E.mu.reg_write(UC_X86_REG_RIP, ip)
E.emu(1)
def generate_value(self, counter):
start = 0xffff880026f02000
offset = counter * 8
address = start + offset
return struct.pack("<Q", address)
def init_unicorn(self, ip, sp, magic):
emu = Emulator(ip, sp)
emu.write_data(ip, b"\xf4" * 8)
emu.write_data(sp, struct.pack("<Q", magic))
emu.emu(1)
def runTest(self):
global mu
ips = list(range(0xffffffff816a9000, 0xffffffff816af000, 0x1))
sps = list(range(0xffff88001b800000, 0xffff88001b801000, 0x1))
j = 0
for i in ips:
j += 1
index = ips.index(i)
self.init_unicorn(i, sps[index], j)
ip_base = 0x000fffff816a0000 # was: 0xffffffff816a0000
sp_base = 0x000f88001b800000 # was: 0xffff88001b800000
mg_base = 0x000f880026f02000 # was: 0xffff880026f02000
ips = range(0x9000, 0xf000, 8)
sps = range(0x0000, 0x6000, 8)
for i, (ip, sp) in enumerate(zip(ips, sps)):
self.init_unicorn(ip_base + ip, sp_base + sp, mg_base + i * 8)
if __name__ == '__main__':
regress.main()

View File

@ -1,113 +1,105 @@
#!/usr/bin/env python
# Moshe Kravchik
from __future__ import print_function
from unicorn import *
from unicorn.arm_const import *
import binascii
import regress
from unicorn import *
from unicorn.arm_const import *
# code to be emulated
#enable VFP
'''
00000016 f44f0370 mov.w r3, #0xf00000
0000001a ee013f50 mcr p15, #0x0, r3, c1, c0, #0x2
0000bfb6 f3bf8f6f isb sy
0000bfba f04f4380 mov.w r3, #0x40000000
0000bfbe eee83a10 vmsr fpexc, r3
'''
ENABLE_VFP_CODE = "\x4f\xf4\x70\x03\x01\xee\x50\x3f\xbf\xf3\x6f\x8f\x4f\xf0\x80\x43\xe8\xee\x10\x3a"
VLD_CODE = "\x21\xf9\x0f\x6a"
#0000002a f9216a0f vld1.8 {d6, d7}, [r1]
VST_CODE = "\x00\xf9\x0f\x6a"
#0000002e f9006a0f vst1.8 {d6, d7}, [r0]
ENABLE_VFP_CODE = (
b"\x4f\xf4\x70\x03" # 00000016 mov.w r3, #0xf00000
b"\x01\xee\x50\x3f" # 0000001a mcr p15, #0x0, r3, c1, c0, #0x2
b"\xbf\xf3\x6f\x8f" # 0000bfb6 isb sy
b"\x4f\xf0\x80\x43" # 0000bfba mov.w r3, #0x40000000
b"\xe8\xee\x10\x3a" # 0000bfbe vmsr fpexc, r3
)
VLD_CODE = b"\x21\xf9\x0f\x6a" # 0000002a vld1.8 {d6, d7}, [r1]
VST_CODE = b"\x00\xf9\x0f\x6a" # 0000002e vst1.8 {d6, d7}, [r0]
# memory address where emulation starts
ADDRESS = 0x10000
SCRATCH_ADDRESS = 0x1000
ADDRESS = 0x10000
SCRATCH_ADDRESS = 0x1000
class SIMDNotReadArm(regress.RegressTest):
def runTest(self):
code = ENABLE_VFP_CODE+VLD_CODE+VST_CODE
print("Emulate THUMB code")
try:
# Initialize emulator in thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
regress.logger.debug("Emulate THUMB code")
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# Initialize emulator in thumb mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, code)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# map 10K scratch memory for this emulation
mu.mem_map(SCRATCH_ADDRESS, 10 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, code)
# write dummy data to be emulated to memory
mu.mem_write(SCRATCH_ADDRESS, "\x01"*64)
# map 10K scratch memory for this emulation
mu.mem_map(SCRATCH_ADDRESS, 10 * 1024)
# initialize machine registers
for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
val = mu.reg_write(i, i - UC_ARM_REG_R0)
# write dummy data to be emulated to memory
mu.mem_write(SCRATCH_ADDRESS, b"\x01" * 64)
mu.reg_write(UC_ARM_REG_R1, SCRATCH_ADDRESS)
mu.reg_write(UC_ARM_REG_R0, SCRATCH_ADDRESS + 0x100)
# initialize machine registers
for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
mu.reg_write(i, i - UC_ARM_REG_R0)
mu.reg_write(UC_ARM_REG_SP, 0x1234)
mu.reg_write(UC_ARM_REG_D6, UC_ARM_REG_D6)
mu.reg_write(UC_ARM_REG_D7, UC_ARM_REG_D7)
mu.reg_write(UC_ARM_REG_R1, SCRATCH_ADDRESS)
mu.reg_write(UC_ARM_REG_R0, SCRATCH_ADDRESS + 0x100)
print(">>> Before emulation ")
print("\tD6 = 0x%x" % mu.reg_read(UC_ARM_REG_D6))
print("\tD7 = 0x%x" % mu.reg_read(UC_ARM_REG_D7))
for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
val = mu.reg_read(i)
print("\t %s = 0x%x" % ("R" + str(i-UC_ARM_REG_R0),val))
mu.reg_write(UC_ARM_REG_SP, 0x1234)
mu.reg_write(UC_ARM_REG_D6, UC_ARM_REG_D6)
mu.reg_write(UC_ARM_REG_D7, UC_ARM_REG_D7)
self.assertEqual(UC_ARM_REG_D6, mu.reg_read(UC_ARM_REG_D6))
self.assertEqual(UC_ARM_REG_D7, mu.reg_read(UC_ARM_REG_D7))
regress.logger.debug(">>> Before emulation")
regress.logger.debug("\tD6 = %#x", mu.reg_read(UC_ARM_REG_D6))
regress.logger.debug("\tD7 = %#x", mu.reg_read(UC_ARM_REG_D7))
try:
content = mu.mem_read(SCRATCH_ADDRESS, 100)
print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS, binascii.hexlify(content)))
content = mu.mem_read(SCRATCH_ADDRESS+0x100, 100)
print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS+0x100, binascii.hexlify(content)))
except Exception, errtxt:
print (errtxt)
for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
regress.logger.debug("\tR%d = %#x", (i - UC_ARM_REG_R0), mu.reg_read(i))
addr = SCRATCH_ADDRESS
data = mu.mem_read(addr, 100)
regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data))
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(code))
addr = SCRATCH_ADDRESS + 0x100
data = mu.mem_read(addr, 100)
regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data))
# now print out some registers
print(">>> Emulation done. Below is the CPU context")
self.assertEqual(UC_ARM_REG_D6, mu.reg_read(UC_ARM_REG_D6))
self.assertEqual(UC_ARM_REG_D7, mu.reg_read(UC_ARM_REG_D7))
sp = mu.reg_read(UC_ARM_REG_SP)
print(">>> SP = 0x%x" %sp)
val = mu.reg_read(UC_ARM_REG_PC)
print(">>> PC = 0x%x" %val)
for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
val = mu.reg_read(i)
print(">>> %s = 0x%x" % ("R" + str(i-UC_ARM_REG_R0),val))
# emulate machine code in infinite time
mu.emu_start(ADDRESS | 0b1, ADDRESS + len(code))
print("\tD6 = 0x%x" % mu.reg_read(UC_ARM_REG_D6))
print("\tD7 = 0x%x" % mu.reg_read(UC_ARM_REG_D7))
# now print out some registers
regress.logger.debug(">>> After emulation")
regress.logger.debug(">>> SP = %#x", mu.reg_read(UC_ARM_REG_SP))
regress.logger.debug(">>> PC = %#x", mu.reg_read(UC_ARM_REG_PC))
try:
content = mu.mem_read(SCRATCH_ADDRESS, 100)
print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS, binascii.hexlify(content)))
content = mu.mem_read(SCRATCH_ADDRESS+0x100, 100)
print("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS+0x100, binascii.hexlify(content)))
except Exception, errtxt:
print (errtxt)
for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
regress.logger.debug("\tR%d = %#x", (i-UC_ARM_REG_R0), mu.reg_read(i))
self.assertEqual(mu.reg_read(UC_ARM_REG_D6), 0x0101010101010101)
self.assertEqual(mu.reg_read(UC_ARM_REG_D7), 0x0101010101010101)
regress.logger.debug("\tD6 = %#x", mu.reg_read(UC_ARM_REG_D6))
regress.logger.debug("\tD7 = %#x", mu.reg_read(UC_ARM_REG_D7))
addr = SCRATCH_ADDRESS
data = mu.mem_read(addr, 100)
regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data))
addr = SCRATCH_ADDRESS + 0x100
data = mu.mem_read(addr, 100)
regress.logger.debug("Memory at addr %#x: %s", addr, binascii.hexlify(data))
self.assertEqual(mu.reg_read(UC_ARM_REG_D6), 0x0101010101010101)
self.assertEqual(mu.reg_read(UC_ARM_REG_D7), 0x0101010101010101)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
regress.main()

View File

@ -1,12 +1,12 @@
#!/usr/bin/env python
from __future__ import print_function
from unicorn import *
from unicorn.x86_const import *
import regress
X86_CODE64 = "\x90" # NOP
from unicorn import *
from unicorn.x86_const import *
X86_CODE64 = b"\x90" # NOP
class WriteBeforeMap(regress.RegressTest):
@ -18,7 +18,10 @@ class WriteBeforeMap(regress.RegressTest):
ADDRESS = 0x1000000
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE64)
with self.assertRaises(UcError) as raisedEx:
mu.mem_write(ADDRESS, X86_CODE64)
self.assertEqual(UC_ERR_WRITE_UNMAPPED, raisedEx.exception.errno)
if __name__ == '__main__':

View File

@ -1,38 +1,45 @@
#!/usr/bin/python
import regress
from unicorn import *
from unicorn.x86_const import *
from unicorn.arm_const import *
import regress
# adds r1, #0x48
# ldrsb r7, [r7, r7]
# ldrsh r7, [r2, r1]
# ldr r0, [pc, #0x168]
# cmp r7, #0xbf
# str r7, [r5, #0x20]
# ldr r1, [r5, #0x64]
# strb r7, [r5, #0xc]
# ldr r0, [pc, #0x1a0]
binary1 = b'\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'
# binary1 = b'\x48\x31\xff\x57'
#adds r1, #0x48
#ldrsb r7, [r7, r7]
CODE = (
b'\x48\x31' # adds r1, #0x48
b'\xff\x57' # ldrsb r7, [r7, r7]
b'\x57\x5e' # ldrsh r7, [r2, r1]
b'\x5a\x48' # ldr r0, [pc, #0x168]
b'\xbf\x2f' # cmp r7, #0xbf
b'\x2f\x62' # str r7, [r5, #0x20]
b'\x69\x6e' # ldr r1, [r5, #0x64]
b'\x2f\x73' # strb r7, [r5, #0xc]
b'\x68\x48' # ldr r0, [pc, #0x1a0]
b'\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05' # data?
)
BASE = 0x00000000
class WrongRIPArm(regress.RegressTest):
def runTest(self):
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
mu.mem_map(0, 2 * 1024 * 1024)
mu.mem_map(BASE, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(0, binary1)
mu.mem_write(BASE, CODE)
mu.reg_write(UC_ARM_REG_R13, 1 * 1024 * 1024)
# emu for maximum 1 instruction.
mu.emu_start(0, len(binary1), 0, 1)
mu.emu_start(BASE | 0b1, BASE + len(CODE), count=1)
self.assertEqual(0x48, mu.reg_read(UC_ARM_REG_R1))
pos = mu.reg_read(UC_ARM_REG_R15)
self.assertEqual(0x2, pos)
self.assertEqual(0x2, mu.reg_read(UC_ARM_REG_R15))
if __name__ == '__main__':
regress.main()

View File

@ -1,21 +1,26 @@
#!/usr/bin/python
import regress
import unicorn as U
from unicorn import *
from unicorn.x86_const import *
class WrongConditionalPath(regress.RegressTest):
def test_eflags(self):
# 0: 4d 31 f6 xor r14, r14
# 3: 45 85 f6 test r14d, r14d
# 6: 75 fe jne 0x6
# 8: f4 hlt
CODE = 'M1\xf6E\x85\xf6u\xfe\xf4'
code = (
b'\x4d\x31\xf6' # xor r14, r14
b'\x45\x85\xf6' # test r14d, r14d
b'\x75\xfe' # jne 0x6
b'\xf4' # hlt
)
uc = U.Uc(U.UC_ARCH_X86, U.UC_MODE_64)
uc.reg_write(U.x86_const.UC_X86_REG_RIP, 0x6000b0)
uc.reg_write(U.x86_const.UC_X86_REG_EFLAGS, 0x246)
uc = Uc(UC_ARCH_X86, UC_MODE_64)
uc.reg_write(UC_X86_REG_RIP, 0x6000b0)
uc.reg_write(UC_X86_REG_EFLAGS, 0x246)
uc.mem_map(0x600000, 0x1000)
uc.mem_write(0x6000b0, CODE)
uc.mem_write(0x6000b0, code)
uc.emu_start(0x6000b0 + 6, 0, count=1)
@ -33,7 +38,8 @@ class WrongConditionalPath(regress.RegressTest):
# RIP=00000000006000b6 RFL=00000246 [---Z-P-] CPL=3 II=0 A20=1 SMM=0 HLT=0
# 0x00000000006000b8: hlt
# RIP=00000000006000b8 RFL=00000246 [---Z-P-] CPL=3 II=0 A20=1 SMM=0 HLT=0
self.assertEqual(0x6000b0 + 8, uc.reg_read(U.x86_const.UC_X86_REG_RIP))
self.assertEqual(0x6000b0 + 8, uc.reg_read(UC_X86_REG_RIP))
if __name__ == '__main__':
regress.main()

View File

@ -1,15 +1,19 @@
#!/usr/bin/python
import regress
import unicorn as U
from unicorn import *
from unicorn.x86_const import *
class WrongEFLAGS(regress.RegressTest):
def test_eflags(self):
# xor r14,r14
CODE = 'M1\xf6'
CODE = b'M1\xf6'
uc = U.Uc(U.UC_ARCH_X86, U.UC_MODE_64)
uc.reg_write(U.x86_const.UC_X86_REG_RIP, 0x6000b0)
uc.reg_write(U.x86_const.UC_X86_REG_EFLAGS, 0x200)
uc = Uc(UC_ARCH_X86, UC_MODE_64)
uc.reg_write(UC_X86_REG_RIP, 0x6000b0)
uc.reg_write(UC_X86_REG_EFLAGS, 0x200)
uc.mem_map(0x600000, 0x1000)
uc.mem_write(0x6000b0, CODE)
@ -31,8 +35,9 @@ class WrongEFLAGS(regress.RegressTest):
# (gdb) p $eflags
# $4 = [ PF ZF IF ]
self.assertEqual(0x6000b3, uc.reg_read(U.x86_const.UC_X86_REG_RIP))
self.assertEqual(0x246, uc.reg_read(U.x86_const.UC_X86_REG_EFLAGS))
self.assertEqual(0x6000b3, uc.reg_read(UC_X86_REG_RIP))
self.assertEqual(0x246, uc.reg_read(UC_X86_REG_EFLAGS))
if __name__ == '__main__':
regress.main()

Some files were not shown because too many files have changed in this diff Show More