Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a1cbd67ae8 | ||
|
ff88348a51 | ||
|
429dbf3012 | ||
|
1742486319 | ||
|
d0d0b697e6 | ||
|
dccba3f445 | ||
|
b860629595 | ||
|
22591634ec | ||
|
28dcf7c29f | ||
|
c20d196316 | ||
|
92bd175b4c | ||
|
be2b45bd49 | ||
|
5b8c5885c8 | ||
|
3c84dd399a | ||
|
f645fd087b | ||
|
69c5a707c8 | ||
|
0626d4799a | ||
|
2181f187ba | ||
|
b978b09d66 | ||
|
bbb238ec43 | ||
|
5a1c8ea8b5 | ||
|
f90429db72 | ||
|
683b97497b | ||
|
16fa9ed521 | ||
|
4e18047c8a | ||
|
c9a51ccf43 | ||
|
8d21f262e0 | ||
|
8b8126cb75 |
167
.github/workflows/PyPI-publishing.yml
vendored
167
.github/workflows/PyPI-publishing.yml
vendored
@ -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 }}
|
6
.github/workflows/build-uc2.yml
vendored
6
.github/workflows/build-uc2.yml
vendored
@ -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',
|
||||
|
333
.github/workflows/build-wheels-publish.yml
vendored
Normal file
333
.github/workflows/build-wheels-publish.yml
vendored
Normal 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
10
.gitignore
vendored
@ -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/
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
@ -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/
|
40
bindings/python/pyproject.toml
Normal file
40
bindings/python/pyproject.toml
Normal 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*"]
|
@ -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
|
@ -1,2 +0,0 @@
|
||||
[bdist_wheel]
|
||||
universal=1
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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()
|
@ -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
|
@ -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)
|
@ -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
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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)
|
@ -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)
|
@ -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()
|
@ -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()
|
@ -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']
|
||||
|
@ -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']
|
||||
|
@ -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']
|
||||
|
@ -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_
|
||||
|
@ -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']
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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__':
|
||||
|
@ -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
177
tests/regress/core_ctl.py
Normal 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()
|
@ -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__':
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
66
tests/regress/high_mem.py
Normal 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()
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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__':
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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__':
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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__':
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
@ -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__':
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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__':
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user