Preface

Qiling is an advanced binary emulation framework that works on almost every platform. Currently, due to some issues related to Unicorn and Keystone, it is quite difficult to install on macOS with ARM CPUs. In this post, I will share my journey toward a successful installation.

Installation

I don’t want to install Python packages globally, so I will use virtualenv to create a virtual environment.

virtualenv env
source env/bin/activate

We are now ready to install Qiling:

git clone -b dev https://github.com/qilingframework/qiling.git
cd qiling && git submodule update --init --recursive
brew install pkg-config cmake ninja

We need to install Unicorn and Keystone separately to resolve compatibility issues.

  • Install a compatible Unicorn version:
pip install --force-reinstall -v "unicorn==2.0.1.post1"
  • Install Keystone from source:

Keystone requires an older version of CMake. Download CMake version 3.15.0 from CMake’s official website 1

cd .. # go to main folder
git clone https://github.com/keystone-engine/keystone.git
cp keystone/make-share.sh keystone/make-share-arm64.sh
chmod +x keystone/make-share-arm64.sh

Modify the content of make-share-arm64.sh to use the path of the downloaded CMake. Example:

/PATH_OF_CMAKE/CMake.app/Contents/bin/cmake -DBUILD_LIBS_ONLY=$BUILD_LIBS_ONLY -DLLVM_BUILD_32_BITS="$LLVM_BUILD_32_BITS" -DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_BUILD_TYPE=$BUILDTYPE -DBUILD_SHARED_LIBS=ON -DLLVM_TARGETS_TO_BUILD="all" -DPYTHON_EXECUTABLE=/PATH_OF_PARENT_FOLDER/env/bin/python3 -G "Unix Makefiles" ..

After this modification, build Keystone and copy the .dylib files to your environment’s lib folder. Adjust the paths according to your Python version:

mkdir -p keystone/build
cd keystone/build
../make-share-arm64.sh
cd ..
cp keystone/build/llvm/lib/libkeystone.0.dylib env/lib/python3.13/site-packages/keystone/
cp keystone/build/llvm/lib/libkeystone.dylib env/lib/python3.13/site-packages/keystone/
  • We also need to patch Qiling source code so that it won’t crash during startup. Please apply the below patches to your qiling source code 2
diff --git a/qiling/os/posix/const.py b/qiling/os/posix/const.py
index 6fcea41b..c03c2343 100644
--- a/qiling/os/posix/const.py
+++ b/qiling/os/posix/const.py
@@ -481,6 +481,22 @@ class macos_x86_open_flags(QlPrettyFlag):
     O_BINARY    = FLAG_UNSUPPORTED
     O_LARGEFILE = FLAG_UNSUPPORTED
 
+class macos_arm_open_flags(QlPrettyFlag):
+    O_RDONLY    = 0x000000
+    O_WRONLY    = 0x000001
+    O_RDWR      = 0x000002
+    O_NONBLOCK  = 0x000004
+    O_APPEND    = 0x000008
+    O_ASYNC     = 0x000040
+    O_SYNC      = 0x000080
+    O_NOFOLLOW  = 0x000100
+    O_CREAT     = 0x000200
+    O_TRUNC     = 0x000400
+    O_EXCL      = 0x000800
+    O_NOCTTY    = 0x020000
+    O_DIRECTORY = 0x100000
+    O_BINARY    = FLAG_UNSUPPORTED
+    O_LARGEFILE = FLAG_UNSUPPORTED
 
 class linux_x86_open_flags(QlPrettyFlag):
     O_RDONLY    = 0x000000
diff --git a/qiling/os/posix/const_mapping.py b/qiling/os/posix/const_mapping.py
index dd95f717..7e36a253 100644
--- a/qiling/os/posix/const_mapping.py
+++ b/qiling/os/posix/const_mapping.py
@@ -57,7 +57,9 @@ def get_open_flags_class(archtype: QL_ARCH, ostype: QL_OS) -> Union[Type[Flag],
 
         QL_OS.MACOS: {
             QL_ARCH.X86:   macos_x86_open_flags,
-            QL_ARCH.X8664: macos_x86_open_flags
+            QL_ARCH.X8664: macos_x86_open_flags,
+            QL_ARCH.ARM: macos_arm_open_flags,
+            QL_ARCH.ARM64: macos_arm_open_flags
         },
 
         QL_OS.WINDOWS: {
  • Finally, go to Qiling’s folder and install it:
pip3 install .

References: