Over the past few months, I’ve found that it’s really quite easy to use IDA as a library within Python scripts via idalib. With HCLI, you can get it going in about two commands. Therefore, I’ve been adding IDA/idalib support to some binary analysis tools.
To be sure my changes work well, I’ve also been adding Continuous Integration (CI) tests for these tools. For example, using GitHub Actions to ensure all tests pass upon every commit to a GitHub repository. With HCLI, it’s feasible to install IDA/idalib across a wide matrix of operating systems (Windows, macOS, Linux), architectures (x86-64 and aarch64), and IDA versions (9.0-9.2+), and run tests against every combination.
Here’s an example of a recent PR that does this for capa. By adding the tests and running them in CI, we found a bunch of latent bugs in the IDA integration and fixed them - nice!
Here’s what it takes to install, configure, and license IDA and idalib using HCLI (just two commands!):
export HCLI_API_KEY="hrp-1-..." # see [0]
export IDA_LICENSE_ID="00-0000-..." # see [1]
uv run --with ida-hcli \
hcli ida install \
--download-id ida-pro:latest \
--license-id "${IDA_LICENSE_ID}" \
--set-default \
--accept-eula \
--yes
uv pip install idapro ida-domain
and then you can do:
import sys
from ida_domain import Database
with Database.open(sys.argv[1]) as db:
for f in db.functions.get_all():
print(db.functions.get_name(f))
Within the context of GitHub Actions, you can use a workflow fragment like the following to install, configure, and license IDA/idalib in the container and ensure tests pass:
idalib-tests:
name: IDA ${{ matrix.ida.version }} tests for ${{ matrix.python-version }}
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.13"]
ida:
- version: 9.0
slug: "release/9.0/ida-essential/ida-essential_90_x64linux.run"
- version: 9.1
slug: "release/9.1/ida-essential/ida-essential_91_x64linux.run"
- version: 9.2
slug: "release/9.2/ida-essential/ida-essential_92_x64linux.run"
steps:
- name: Checkout capa with submodules
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Setup uv
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0
- name: Install tool
run: |
pip install -r requirements.txt
pip install -e .[dev,scripts]
pip install idapro
- name: Install IDA ${{ matrix.ida.version }}
run: |
uv run hcli --disable-updates ida install --download-id ${{ matrix.ida.slug }} --license-id ${{ secrets.IDA_LICENSE_ID }} --set-default --yes
env:
HCLI_API_KEY: ${{ secrets.HCLI_API_KEY }}
IDA_LICENSE_ID: ${{ secrets.IDA_LICENSE_ID }}
- name: Run tests
run: pytest -v tests/
(source)
Add HCLI_API_KEY and IDA_LICENSE_ID to the GitHub Actions environment secrets (and protect these during PR review) for this to work.
And, if you want to test against various OS/arch/IDA versions, you could use the matrix like we use in HCLI itself:
If you have any questions, ideas, or need help setting up something similar in your environment - don’t hesitate to reach out! We’re happy to share our tips, tricks, and experiences.
[0] In order to create an HCLI API key, you can do:
uv run --with ida-hcli hcli auth key create --name unpacker-gh-actions
The key will be displayed only once, so make sure to save it in a secure place.
? Do you want to create a new API key unpacker-gh-actions? Yes
Creating API key 'my-tool-gh-actions'...
API key created: hrp-1-...HERE_IT_IS...
? Do you want to use this key for hcli? No
Just make sure you protect this token, as it has equal privilege as your my.hex-rays.com credentials.
[1] In order to list your license IDs, you can do:
uv run --with ida-hcli hcli license list
Licenses for customer [-1]:
Subscription Licenses (3):
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ID ┃ Edition ┃ Type ┃ Status ┃ Expiration ┃ Addons ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 96-xxxxxxxxxxxx │ IDA Expert-4 │ named │ Active │ 2027-01-20 │ 4 decompiler(s) │
│ 96-xxxxxxxxxxxx │ IDA Essential PC │ named │ Active │ 2026-08-25 │ 2 decompiler(s) │
│ 96-xxxxxxxxxxxx │ IDA Ultimate │ named │ Active │ 2026-07-02 │ 11 decompiler(s) + TEAMS, LUMINA │
└─────────────────┴──────────────────┴───────┴────────┴────────────┴──────────────────────────────────┘
Total: 3 license(s)
The license ID isn’t particularly sensitive, but I typically avoid making this public to avoid embarrassment if people see function names in one of my IDBs (“maybe_maybe_encryption???”
).