pax_global_header00006660000000000000000000000064150140232650014510gustar00rootroot0000000000000052 comment=5caac1ea81f7e700dc7969abd9706dd0cd1580ec yara-python-4.5.3/000077500000000000000000000000001501402326500137745ustar00rootroot00000000000000yara-python-4.5.3/.github/000077500000000000000000000000001501402326500153345ustar00rootroot00000000000000yara-python-4.5.3/.github/workflows/000077500000000000000000000000001501402326500173715ustar00rootroot00000000000000yara-python-4.5.3/.github/workflows/publish-to-pypi.yml000066400000000000000000000073161501402326500231700ustar00rootroot00000000000000name: Publish Python distribution to PyPI on: push: branches: - master pull_request: workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: build: name: Build wheels on ${{ matrix.os }} (${{ matrix.arch }}) runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-24.04 arch: x86_64 build-sdist: true - os: ubuntu-24.04 arch: i686 - os: ubuntu-24.04 arch: aarch64 - os: macos-15 arch: x86_64 arm64 env: MACOSX_DEPLOYMENT_TARGET=15.0 - os: macos-14 arch: x86_64 arm64 env: MACOSX_DEPLOYMENT_TARGET=14.0 - os: windows-2022 arch: x86 before: vcpkg install openssl:x86-windows-static env: LIB="C:\\vcpkg\\packages\\openssl_x86-windows-static\\lib" INCLUDE="C:\\vcpkg\\packages\\openssl_x86-windows-static\\include" - os: windows-2022 arch: AMD64 before: vcpkg install openssl:x64-windows-static env: LIB="C:\\vcpkg\\packages\\openssl_x64-windows-static\\lib" INCLUDE="C:\\vcpkg\\packages\\openssl_x64-windows-static\\include" steps: - uses: actions/checkout@v4 with: submodules: true - name: Set up QEMU if: runner.os == 'Linux' uses: docker/setup-qemu-action@v3 with: platforms: all - name: Build wheels uses: pypa/cibuildwheel@v2.23.3 with: output-dir: dist env: CIBW_ARCHS: ${{ matrix.arch }} CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*" CIBW_BEFORE_ALL_LINUX: | if [[ ! -z "$(which yum)" ]]; then yum install -y make gcc perl-core pcre-devel wget zlib-devel git automake wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz tar xf openssl*.gz cd openssl* ./config --prefix=/usr --openssldir=/etc/ssl zlib-dynamic make -j$(nproc) make install elif [[ ! -z "$(which apk)" ]]; then apk add openssl-dev fi CIBW_BEFORE_ALL_WINDOWS: ${{ matrix.before }} CIBW_BUILD_FRONTEND: "build; args: --config-setting=--enable-openssl" CIBW_ENVIRONMENT: ${{ matrix.env }} CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_x86_64" CIBW_TEST_COMMAND: python {package}/tests.py - name: Store the distribution packages uses: actions/upload-artifact@v4 with: name: python-package-distributions-${{ matrix.os }}-${{ matrix.arch }} path: dist/*.whl - name: Build Sdist if: ${{ matrix.build-sdist }} run: pipx run build --sdist - name: Store the source distribution package if: ${{ matrix.build-sdist }} uses: actions/upload-artifact@v4 with: name: python-package-distributions-source path: dist/*.tar.gz publish-to-pypi: needs: [build] runs-on: ubuntu-latest # Only publish to PyPI on tag pushes if: startsWith(github.ref, 'refs/tags/') environment: name: pypi url: https://pypi.org/p/yara-python permissions: id-token: write # IMPORTANT: mandatory for trusted publishing steps: - name: Download all the dists uses: actions/download-artifact@v4 with: pattern: python-package-distributions-* merge-multiple: true path: dist/ - name: Publish distribution to PyPI uses: pypa/gh-action-pypi-publish@release/v1 yara-python-4.5.3/.gitmodules000066400000000000000000000001131501402326500161440ustar00rootroot00000000000000[submodule "yara"] path = yara url=https://github.com/VirusTotal/yara.git yara-python-4.5.3/LICENSE000066400000000000000000000261361501402326500150110ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. yara-python-4.5.3/MANIFEST.in000066400000000000000000000001441501402326500155310ustar00rootroot00000000000000include *.c include yara/libyara/modules/module_list recursive-include yara *.c *.h include LICENSE yara-python-4.5.3/README.rst000066400000000000000000000040771501402326500154730ustar00rootroot00000000000000.. image:: https://ci.appveyor.com/api/projects/status/gidnb9ulj3rje5s2?svg=true :target: https://ci.appveyor.com/project/plusvic/yara-python yara-python =========== With this library you can use `YARA `_ from your Python programs. It covers all YARA's features, from compiling, saving and loading rules to scanning files, strings and processes. Here it goes a little example: .. code-block:: python >>> import yara >>> rule = yara.compile(source='rule foo: bar {strings: $a = "lmn" condition: $a}') >>> matches = rule.match(data='abcdefgjiklmnoprstuvwxyz') >>> print(matches) [foo] >>> print(matches[0].rule) foo >>> print(matches[0].tags) ['bar'] >>> print(matches[0].strings) [$a] >>> print(matches[0].strings[0].identifier) $a >>> print(matches[0].strings[0].instances) [lmn] >>> print(matches[0].strings[0].instances[0].offset) 10 >>> print(matches[0].strings[0].instances[0].matched_length) 3 Installation ------------ The easiest way to install YARA is by using ``pip``: .. code-block:: bash $ pip install yara-python But you can also get the source from GitHub and compile it yourself: .. code-block:: bash $ git clone --recursive https://github.com/VirusTotal/yara-python $ cd yara-python $ python setup.py build $ sudo python setup.py install Notice the ``--recursive`` option used with ``git``. This is important because we need to download the ``yara`` subproject containing the source code for ``libyara`` (the core YARA library). It's also important to note that the two methods above link ``libyara`` statically into yara-python. If you want to link dynamically against a shared ``libyara`` library, use: .. code-block:: bash $ python setup.py build --dynamic-linking For this option to work you must build and install `YARA `_ separately before installing ``yara-python``. Documentation ------------- Find more information about how to use yara-python at https://yara.readthedocs.org/en/latest/yarapython.html. yara-python-4.5.3/appveyor.yml000077500000000000000000000154751501402326500164030ustar00rootroot00000000000000environment: global: # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the # /E:ON and /V:ON options are not enabled in the batch script intepreter # See: http://stackoverflow.com/a/13751649/163740 CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" JANSSON_VERSION: "2.13" matrix: - PYTHON: "C:\\Python38" PYTHON_VERSION: "3.8.x" PYTHON_ARCH: "32" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/fakubeldw67e9pmg/artifacts/YARA.OpenSSL.x86.1.1.1.nupkg" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python38-x64" PYTHON_VERSION: "3.8.x" PYTHON_ARCH: "64" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/q63539qt9yqaqspo/artifacts/YARA.OpenSSL.x64.1.1.1.nupkg" VS: "Visual Studio 14 2015 Win64" - PYTHON: "C:\\Python39" PYTHON_VERSION: "3.9.0" PYTHON_ARCH: "32" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/fakubeldw67e9pmg/artifacts/YARA.OpenSSL.x86.1.1.1.nupkg" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python39-x64" PYTHON_VERSION: "3.9.0" PYTHON_ARCH: "64" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/q63539qt9yqaqspo/artifacts/YARA.OpenSSL.x64.1.1.1.nupkg" VS: "Visual Studio 14 2015 Win64" - PYTHON: "C:\\Python310" PYTHON_VERSION: "3.10.0" PYTHON_ARCH: "32" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/fakubeldw67e9pmg/artifacts/YARA.OpenSSL.x86.1.1.1.nupkg" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python310-x64" PYTHON_VERSION: "3.10.0" PYTHON_ARCH: "64" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/q63539qt9yqaqspo/artifacts/YARA.OpenSSL.x64.1.1.1.nupkg" VS: "Visual Studio 14 2015 Win64" - PYTHON: "C:\\Python311" PYTHON_VERSION: "3.11.2" PYTHON_ARCH: "32" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/fakubeldw67e9pmg/artifacts/YARA.OpenSSL.x86.1.1.1.nupkg" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python311-x64" PYTHON_VERSION: "3.11.2" PYTHON_ARCH: "64" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/q63539qt9yqaqspo/artifacts/YARA.OpenSSL.x64.1.1.1.nupkg" VS: "Visual Studio 14 2015 Win64" - PYTHON: "C:\\Python312" PYTHON_VERSION: "3.12.0" PYTHON_ARCH: "32" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/fakubeldw67e9pmg/artifacts/YARA.OpenSSL.x86.1.1.1.nupkg" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python312-x64" PYTHON_VERSION: "3.12.0" PYTHON_ARCH: "64" OPENSSL_LIB: "https://ci.appveyor.com/api/buildjobs/q63539qt9yqaqspo/artifacts/YARA.OpenSSL.x64.1.1.1.nupkg" VS: "Visual Studio 14 2015 Win64" install: # If there is a newer build queued for the same PR, cancel this one. # The AppVeyor 'rollout builds' option is supposed to serve the same # purpose but it is problematic because it tends to cancel builds pushed # directly to master instead of just PR builds (or the converse). # credits: JuliaLang developers. - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` throw "There are newer queued builds for this pull request, failing early." } - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" # Install Python (from the official .msi of http://python.org) and pip when # not already installed. - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 } - "echo %APPVEYOR_BUILD_WORKER_IMAGE%" # Prepend newly installed Python to the PATH of this build (this cannot be # done from inside the powershell script as it would require to restart # the parent CMD process). - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" # Check that we have the expected version and architecture for Python - "python --version" - 'python -c "import struct; print(struct.calcsize(''P'') * 8)"' # Upgrade to the latest version of pip to avoid it displaying warnings # about it being out of date. - "python -m pip install --disable-pip-version-check --user --upgrade pip" # Install the build dependencies of the project. If some dependencies contain # compiled extensions and are not provided as pre-built wheel packages, # pip will build them from source using the MSVC compiler matching the # target Python version and architecture. # Install wheel. - "%CMD_IN_ENV% pip install wheel" # Install setuptools. Setup tools doesn't comes by default in Python 3.12. - "%CMD_IN_ENV% pip install setuptools" # We are in projects/yara-python, lets go out to projects. - cd .. # Download precompiled OpenSSL library. - ps: Invoke-WebRequest "$env:OPENSSL_LIB" -OutFile "openssl.zip" - ps: Expand-Archive openssl.zip -DestinationPath openssl # Download and build jansson library. - ps: Invoke-WebRequest "https://github.com/akheron/jansson/archive/v$env:JANSSON_VERSION.zip" -OutFile "jansson.zip" - ps: Expand-Archive jansson.zip -DestinationPath . - cd jansson-%JANSSON_VERSION% - md build - cd build - cmake -DJANSSON_BUILD_DOCS=OFF -DJANSSON_WITHOUT_TESTS=ON -G "%VS%" .. - cmake --build . --config Release # We are in projects/jansson-%JANSSON_VERSION%/build, lets move to # projects/yara-python - cd ../../yara-python clone_script: - cmd: git clone -q --recursive --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER% - cmd: git fetch - cmd: git checkout -qf %APPVEYOR_REPO_BRANCH% build_script: # Build the compiled extension - "%CMD_IN_ENV% python setup.py build_ext --enable-cuckoo --enable-openssl -L../jansson-%JANSSON_VERSION%/build/lib/Release;../openssl/lib -I../jansson-%JANSSON_VERSION%/build/include;../openssl/include -llibcrypto" after_build: - "%CMD_IN_ENV% python setup.py install" test_script: - "%CMD_IN_ENV% python tests.py" after_test: # If tests are successful, create binary packages for the project. - "%CMD_IN_ENV% python setup.py bdist_wheel" artifacts: # Archive the generated packages in the ci.appveyor.com build report. - path: dist\* deploy: tag: $(APPVEYOR_REPO_TAG_NAME) provider: GitHub auth_token: secure: d3qqX7bmrBiKJI38yFPc5vHrGGfS3LxLC7FaG6ewI2ghPPE22Pk6QtyrEFFb73PL artifact: /.*\.exe/ draft: true on: APPVEYOR_REPO_TAG: true # deploy on tag push only #on_success: # - TODO: upload the content of dist/*.whl to a public wheelhouse # # Uncomment these lines for enabling Remote Desktop for debugging purposes. #on_finish: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) yara-python-4.5.3/appveyor/000077500000000000000000000000001501402326500156415ustar00rootroot00000000000000yara-python-4.5.3/appveyor/install.ps1000077500000000000000000000123301501402326500177360ustar00rootroot00000000000000# Sample script to install Python and pip under Windows # Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer # License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ $BASE_URL = "https://www.python.org/ftp/python/" $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" $GET_PIP_PATH = "C:\get-pip.py" $PYTHON_PRERELEASE_REGEX = @" (?x) (?\d+) \. (?\d+) \. (?\d+) (?[a-z]{1,2}\d+) "@ function Download ($filename, $url) { $webclient = New-Object System.Net.WebClient $basedir = $pwd.Path + "\" $filepath = $basedir + $filename if (Test-Path $filename) { Write-Host "Reusing" $filepath return $filepath } # Download and retry up to 3 times in case of network transient errors. Write-Host "Downloading" $filename "from" $url $retry_attempts = 2 for ($i = 0; $i -lt $retry_attempts; $i++) { try { $webclient.DownloadFile($url, $filepath) break } Catch [Exception]{ Start-Sleep 1 } } if (Test-Path $filepath) { Write-Host "File saved at" $filepath } else { # Retry once to get the error message if any at the last try $webclient.DownloadFile($url, $filepath) } return $filepath } function ParsePythonVersion ($python_version) { if ($python_version -match $PYTHON_PRERELEASE_REGEX) { return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro, $matches.prerelease) } $version_obj = [version]$python_version return ($version_obj.major, $version_obj.minor, $version_obj.build, "") } function DownloadPython ($python_version, $platform_suffix) { $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version if (($major -le 2 -and $micro -eq 0) ` -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) ` ) { $dir = "$major.$minor" $python_version = "$major.$minor$prerelease" } else { $dir = "$major.$minor.$micro" } if ($prerelease) { if (($major -le 2) ` -or ($major -eq 3 -and $minor -eq 1) ` -or ($major -eq 3 -and $minor -eq 2) ` -or ($major -eq 3 -and $minor -eq 3) ` ) { $dir = "$dir/prev" } } if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) { $ext = "msi" if ($platform_suffix) { $platform_suffix = ".$platform_suffix" } } else { $ext = "exe" if ($platform_suffix) { $platform_suffix = "-$platform_suffix" } } $filename = "python-$python_version$platform_suffix.$ext" $url = "$BASE_URL$dir/$filename" $filepath = Download $filename $url return $filepath } function InstallPython ($python_version, $architecture, $python_home) { Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home if (Test-Path $python_home) { Write-Host $python_home "already exists, skipping." return $false } if ($architecture -eq "32") { $platform_suffix = "" } else { $platform_suffix = "amd64" } $installer_path = DownloadPython $python_version $platform_suffix $installer_ext = [System.IO.Path]::GetExtension($installer_path) Write-Host "Installing $installer_path to $python_home" $install_log = $python_home + ".log" if ($installer_ext -eq '.msi') { InstallPythonMSI $installer_path $python_home $install_log } else { InstallPythonEXE $installer_path $python_home $install_log } if (Test-Path $python_home) { Write-Host "Python $python_version ($architecture) installation complete" } else { Write-Host "Failed to install Python in $python_home" Get-Content -Path $install_log Exit 1 } } function InstallPythonEXE ($exepath, $python_home, $install_log) { $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home" RunCommand $exepath $install_args } function InstallPythonMSI ($msipath, $python_home, $install_log) { $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home" $uninstall_args = "/qn /x $msipath" RunCommand "msiexec.exe" $install_args if (-not(Test-Path $python_home)) { Write-Host "Python seems to be installed else-where, reinstalling." RunCommand "msiexec.exe" $uninstall_args RunCommand "msiexec.exe" $install_args } } function RunCommand ($command, $command_args) { Write-Host $command $command_args Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru } function InstallPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $python_path = $python_home + "\python.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $webclient = New-Object System.Net.WebClient $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) Write-Host "Executing:" $python_path $GET_PIP_PATH & $python_path $GET_PIP_PATH } else { Write-Host "pip already installed." } } function main () { InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON InstallPip $env:PYTHON } main yara-python-4.5.3/appveyor/run_with_env.cmd000077500000000000000000000064461501402326500210520ustar00rootroot00000000000000:: To build extensions for 64 bit Python 3, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) :: :: To build extensions for 64 bit Python 2, we need to configure environment :: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) :: :: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific :: environment configurations. :: :: Note: this script needs to be run with the /E:ON and /V:ON flags for the :: cmd interpreter, at least for (SDK v7.0) :: :: More details at: :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows :: http://stackoverflow.com/a/13751649/163740 :: :: Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ :: :: Notes about batch files for Python people: :: :: Quotes in values are literally part of the values: :: SET FOO="bar" :: FOO is now five characters long: " b a r " :: If you don't want quotes, don't include them on the right-hand side. :: :: The CALL lines at the end of this file look redundant, but if you move them :: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y :: case, I don't know why. @ECHO OFF SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf :: Extract the major and minor versions, and allow for the minor version to be :: more than 9. This requires the version number to have two dots in it. SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% IF "%PYTHON_VERSION:~3,1%" == "." ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% ) ELSE ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% ) :: Based on the Python version, determine what SDK version to use, and whether :: to set the SDK for 64-bit. IF %MAJOR_PYTHON_VERSION% == 2 ( SET WINDOWS_SDK_VERSION="v7.0" SET SET_SDK_64=Y ) ELSE ( IF %MAJOR_PYTHON_VERSION% == 3 ( SET WINDOWS_SDK_VERSION="v7.1" IF %MINOR_PYTHON_VERSION% LEQ 4 ( SET SET_SDK_64=Y ) ELSE ( SET SET_SDK_64=N IF EXIST "%WIN_WDK%" ( :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ REN "%WIN_WDK%" 0wdf ) ) ) ELSE ( ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" EXIT 1 ) ) IF %PYTHON_ARCH% == 64 ( IF %SET_SDK_64% == Y ( ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture SET DISTUTILS_USE_SDK=1 SET MSSdk=1 "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ELSE ( ECHO Using default MSVC build environment for 64 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ) ELSE ( ECHO Using default MSVC build environment for 32 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) yara-python-4.5.3/appveyor/setup_x64.bat000077500000000000000000000020461501402326500201770ustar00rootroot00000000000000regedit /s x64\VC_OBJECTS_PLATFORM_INFO.reg regedit /s x64\600dd186-2429-11d7-8bf6-00b0d03daa06.reg regedit /s x64\600dd187-2429-11d7-8bf6-00b0d03daa06.reg regedit /s x64\600dd188-2429-11d7-8bf6-00b0d03daa06.reg regedit /s x64\600dd189-2429-11d7-8bf6-00b0d03daa06.reg regedit /s x64\656d875f-2429-11d7-8bf6-00b0d03daa06.reg regedit /s x64\656d8760-2429-11d7-8bf6-00b0d03daa06.reg regedit /s x64\656d8763-2429-11d7-8bf6-00b0d03daa06.reg regedit /s x64\656d8766-2429-11d7-8bf6-00b0d03daa06.reg copy "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\AMD64.VCPlatform.config" "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\AMD64.VCPlatform.Express.config" copy "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\Itanium.VCPlatform.config" "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\Itanium.VCPlatform.Express.config" copy "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64\vcvarsamd64.bat" yara-python-4.5.3/appveyor/x64/000077500000000000000000000000001501402326500162625ustar00rootroot00000000000000yara-python-4.5.3/appveyor/x64/600dd186-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011461501402326500234370ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{600dd186-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectIA64Platform.dll" @="Win64 (Itanium) Platform Class" yara-python-4.5.3/appveyor/x64/600dd187-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011361501402326500234370ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{600dd187-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectIA64Platform.dll" @="ClIA64CodeGeneration Class" yara-python-4.5.3/appveyor/x64/600dd188-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011201501402326500234310ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{600dd188-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectIA64Platform.dll" @="ClIA64General Class" yara-python-4.5.3/appveyor/x64/600dd189-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011441501402326500234400ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{600dd189-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectIA64Platform.dll" @="ClIA64AdditionalOptions Class" yara-python-4.5.3/appveyor/x64/656d875f-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011421501402326500234550ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{656d875f-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectAMD64Platform.dll" @="ClAMD64CodeGeneration Class" yara-python-4.5.3/appveyor/x64/656d8760-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011241501402326500233700ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{656d8760-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectAMD64Platform.dll" @="ClAMD64General Class" yara-python-4.5.3/appveyor/x64/656d8763-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011441501402326500233750ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{656d8763-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectAMD64Platform.dll" @="Win64 (AMD64) Platform Class" yara-python-4.5.3/appveyor/x64/656d8766-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011501501402326500233750ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\CLSID\{656d8766-2429-11d7-8bf6-00b0d03daa06}] "InprocServer32"="C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\VCProjectAMD64Platform.dll" @="ClAMD64AdditionalOptions Class" yara-python-4.5.3/appveyor/x64/VC_OBJECTS_PLATFORM_INFO.reg000077500000000000000000000066041501402326500225720ustar00rootroot00000000000000ÿþWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\VC\VC_OBJECTS_PLATFORM_INFO] [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\VC\VC_OBJECTS_PLATFORM_INFO\Win64 (AMD64)] @="{656d8763-2429-11d7-8bf6-00b0d03daa06}" [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\VC\VC_OBJECTS_PLATFORM_INFO\Win64 (AMD64)\ToolDefaultExtensionLists] "VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c" "VCLinkerTool"="*.obj;*.res;*.lib;*.rsc" "VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc" "VCMIDLTool"="*.idl;*.odl" "VCCustomBuildTool"="*.bat" "VCResourceCompilerTool"="*.rc" "VCPreBuildEventTool"="*.bat" "VCPreLinkEventTool"="*.bat" "VCPostBuildEventTool"="*.bat" "VCBscMakeTool"="*.sbr" "VCNMakeTool"="" "VCWebServiceProxyGeneratorTool"="*.sdl;*.wsdl" "VCWebDeploymentTool"="" "VCALinkTool"="*.resources" "VCManagedResourceCompilerTool"="*.resx" [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\VC\VC_OBJECTS_PLATFORM_INFO\Win64 (Itanium)] @="{600dd186-2429-11d7-8bf6-00b0d03daa06}" [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\VC\VC_OBJECTS_PLATFORM_INFO\Win64 (Itanium)\ToolDefaultExtensionLists] "VCCLCompilerTool"="*.cpp;*.cxx;*.cc;*.c" "VCLinkerTool"="*.obj;*.res;*.lib;*.rsc" "VCLibrarianTool"="*.obj;*.res;*.lib;*.rsc" "VCMIDLTool"="*.idl;*.odl" "VCCustomBuildTool"="*.bat" "VCResourceCompilerTool"="*.rc" "VCPreBuildEventTool"="*.bat" "VCPreLinkEventTool"="*.bat" "VCPostBuildEventTool"="*.bat" "VCBscMakeTool"="*.sbr" "VCNMakeTool"="" "VCWebServiceProxyGeneratorTool"="*.sdl;*.wsdl" "VCWebDeploymentTool"="" "VCALinkTool"="*.resources" "VCManagedResourceCompilerTool"="*.resx" yara-python-4.5.3/setup.cfg000066400000000000000000000004321501402326500156140ustar00rootroot00000000000000[metadata] description_file = README.rst license_file = LICENSE [test] test_suite=tests [build_ext] # These modules are not stable or tested enough # enable_dex = true # enable_macho = true # need libjansson-dev # enable_cuckoo = true # need libmagic-dev # enable_magic = true yara-python-4.5.3/setup.py000066400000000000000000000346271501402326500155220ustar00rootroot00000000000000# # Copyright (c) 2007-2022. The YARA Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # from setuptools import setup, Command, Extension from distutils.command.build import build from distutils.command.build_ext import build_ext from codecs import open import distutils.errors import distutils.ccompiler import distutils.sysconfig import contextlib import os import sys import tempfile import shutil import subprocess OPTIONS = [ ('dynamic-linking', None, 'link dynamically against libyara'), ('enable-cuckoo', None, 'enable "cuckoo" module'), ('enable-magic', None, 'enable "magic" module'), ('enable-dex', None, 'enable "dex" module'), ('enable-macho', None, 'enable "macho" module'), ('enable-profiling', None, 'enable profiling features'), ('enable-openssl', None, 'enable features that depend on OpenSSL'), ] BOOLEAN_OPTIONS = [ 'dynamic-linking', 'enable-cuckoo', 'enable-magic', 'enable-dex', 'enable-macho', 'enable-profiling', 'enable-openssl', ] @contextlib.contextmanager def muted(*streams): """A context manager to redirect stdout and/or stderr to /dev/null. Examples: with muted(sys.stdout): ... with muted(sys.stderr): ... with muted(sys.stdout, sys.stderr): ... """ devnull = open(os.devnull, 'w') try: old_streams = [os.dup(s.fileno()) for s in streams] for s in streams: os.dup2(devnull.fileno(), s.fileno()) yield finally: for o,n in zip(old_streams, streams): os.dup2(o, n.fileno()) devnull.close() def has_function(function_name, includes=None, include_dirs=None, libraries=None, library_dirs=None): """Checks if a given functions exists in the current platform.""" compiler = distutils.ccompiler.new_compiler() with muted(sys.stdout, sys.stderr): result = compiler.has_function( function_name, includes=includes, include_dirs=include_dirs, libraries=libraries, library_dirs=library_dirs) if os.path.exists('a.out'): os.remove('a.out') return result def has_header(header_name): compiler = distutils.ccompiler.new_compiler() with muted(sys.stdout, sys.stderr): with tempfile.NamedTemporaryFile(mode='w', prefix=header_name, delete=False, suffix='.c') as f: f.write(""" #include <{}> int main() {{ return 0; }} """.format(header_name)) f.close() try: compiler.compile([f.name]) except distutils.errors.CompileError: return False return True class BuildCommand(build): user_options = build.user_options + OPTIONS boolean_options = build.boolean_options + BOOLEAN_OPTIONS def initialize_options(self): build.initialize_options(self) self.dynamic_linking = None self.enable_magic = None self.enable_cuckoo = None self.enable_dex = None self.enable_macho = None self.enable_profiling = None self.enable_openssl = None def finalize_options(self): build.finalize_options(self) class BuildExtCommand(build_ext): user_options = build_ext.user_options + OPTIONS boolean_options = build_ext.boolean_options + BOOLEAN_OPTIONS def initialize_options(self): build_ext.initialize_options(self) self.dynamic_linking = None self.enable_magic = None self.enable_cuckoo = None self.enable_dex = None self.enable_macho = None self.enable_profiling = None self.enable_openssl = None def finalize_options(self): build_ext.finalize_options(self) # If the build_ext command was invoked by the build command, take the # values for these options from the build command. self.set_undefined_options('build', ('dynamic_linking', 'dynamic_linking'), ('enable_magic', 'enable_magic'), ('enable_cuckoo', 'enable_cuckoo'), ('enable_dex', 'enable_dex'), ('enable_macho', 'enable_macho'), ('enable_profiling', 'enable_profiling'), ('enable_openssl', 'enable_openssl')) if self.enable_magic and self.dynamic_linking: raise distutils.errors.DistutilsOptionError( '--enable-magic can''t be used with --dynamic-linking') if self.enable_cuckoo and self.dynamic_linking: raise distutils.errors.DistutilsOptionError( '--enable-cuckoo can''t be used with --dynamic-linking') if self.enable_dex and self.dynamic_linking: raise distutils.errors.DistutilsOptionError( '--enable-dex can''t be used with --dynamic-linking') if self.enable_macho and self.dynamic_linking: raise distutils.errors.DistutilsOptionError( '--enable-macho can''t be used with --dynamic-linking') if self.enable_openssl and self.dynamic_linking: raise distutils.errors.DistutilsOptionError( '--enable-openssl can''t be used with --dynamic-linking') def run(self): """Execute the build command.""" module = self.distribution.ext_modules[0] base_dir = os.path.dirname(__file__) if base_dir: os.chdir(base_dir) exclusions = [] for define in self.define or []: module.define_macros.append(define) for library in self.libraries or []: module.libraries.append(library) building_for_windows = self.plat_name in ('win32','win-amd64') building_for_osx = 'macosx' in self.plat_name building_for_linux = 'linux' in self.plat_name building_for_freebsd = 'freebsd' in self.plat_name building_for_openbsd = 'openbsd' in self.plat_name # need testing if building_for_windows: arch = 'x86' if self.plat_name == 'win32' else 'x64' openssl_include_dirs = [ os.path.join(base_dir, 'yara\\windows\\vs2015\\packages\\YARA.OpenSSL.{}.1.1.1\\include'.format(arch)), os.path.join(base_dir, 'yara\\windows\\vs2017\\packages\\YARA.OpenSSL.{}.1.1.1\\include'.format(arch)) ] openssl_library_dirs = [ os.path.join(base_dir, 'yara\\windows\\vs2015\\packages\\YARA.OpenSSL.{}.1.1.1\\lib'.format(arch)), os.path.join(base_dir, 'yara\\windows\\vs2017\\packages\\YARA.OpenSSL.{}.1.1.1\\lib'.format(arch)) ] openssl_libraries = ['libcrypto'] else: openssl_include_dirs = [] openssl_library_dirs = [] openssl_libraries = ['crypto'] if building_for_linux: module.define_macros.append(('_GNU_SOURCE', '1')) module.define_macros.append(('USE_LINUX_PROC', '1')) module.extra_compile_args.append('-std=c99') elif building_for_windows: module.define_macros.append(('USE_WINDOWS_PROC', '1')) module.define_macros.append(('_CRT_SECURE_NO_WARNINGS', '1')) module.libraries.append('kernel32') module.libraries.append('advapi32') module.libraries.append('user32') module.libraries.append('crypt32') module.libraries.append('ws2_32') elif building_for_osx: module.define_macros.append(('_GNU_SOURCE', '1')) module.define_macros.append(('USE_MACH_PROC', '1')) module.extra_compile_args.append('-std=c99') module.include_dirs.append('/usr/local/opt/openssl/include') module.include_dirs.append('/opt/local/include') module.library_dirs.append('/opt/local/lib') module.include_dirs.append('/usr/local/include') module.library_dirs.append('/usr/local/lib') module.library_dirs.append('/usr/local/opt/openssl/lib') module.include_dirs.append('/opt/homebrew/include') module.library_dirs.append('/opt/homebrew/opt/openssl/lib') elif building_for_freebsd: module.define_macros.append(('_GNU_SOURCE', '1')) module.define_macros.append(('USE_FREEBSD_PROC', '1')) module.include_dirs.append('/opt/local/include') module.library_dirs.append('/opt/local/lib') module.include_dirs.append('/usr/local/include') module.library_dirs.append('/usr/local/lib') elif building_for_openbsd: module.define_macros.append(('_GNU_SOURCE', '1')) module.define_macros.append(('USE_OPENBSD_PROC', '1')) module.extra_compile_args.append('-std=c99') module.include_dirs.append('/opt/local/include') module.library_dirs.append('/opt/local/lib') module.include_dirs.append('/usr/local/include') module.library_dirs.append('/usr/local/lib') else: module.define_macros.append(('_GNU_SOURCE', '1')) module.define_macros.append(('USE_NO_PROC', '1')) module.extra_compile_args.append('-std=c99') if has_header('stdbool.h'): module.define_macros.append(('HAVE_STDBOOL_H', '1')) if has_function('memmem'): module.define_macros.append(('HAVE_MEMMEM', '1')) if has_function('strlcpy'): module.define_macros.append(('HAVE_STRLCPY', '1')) if has_function('strlcat'): module.define_macros.append(('HAVE_STRLCAT', '1')) if self.enable_profiling: module.define_macros.append(('YR_PROFILING_ENABLED', '1')) if self.dynamic_linking: module.libraries.append('yara') else: # Is OpenSSL available? if (has_function('OpenSSL_add_all_algorithms', includes=['openssl/evp.h'], include_dirs=module.include_dirs + openssl_include_dirs, libraries=module.libraries + openssl_libraries, library_dirs=module.library_dirs + openssl_library_dirs) # In case OpenSSL is being linked statically or has_function('OpenSSL_add_all_algorithms', includes=['openssl/evp.h'], include_dirs=module.include_dirs + openssl_include_dirs, libraries=module.libraries + openssl_libraries + ['dl', 'pthread', 'z'], library_dirs=module.library_dirs + openssl_library_dirs) or self.enable_openssl): module.define_macros.append(('HASH_MODULE', '1')) module.define_macros.append(('HAVE_LIBCRYPTO', '1')) module.libraries.extend(openssl_libraries) module.include_dirs.extend(openssl_include_dirs) module.library_dirs.extend(openssl_library_dirs) elif building_for_windows: # OpenSSL is not available, but in Windows we can rely on Wincrypt for # hashing functions. module.define_macros.append(('HASH_MODULE', '1')) module.define_macros.append(('HAVE_WINCRYPT_H', '1')) # The authenticode parser depends on OpenSSL and must be excluded. exclusions.append('yara/libyara/modules/pe/authenticode-parser') else: # Without OpenSSL there's no hash module nor authenticode parser. exclusions.append('yara/libyara/modules/hash/hash.c') exclusions.append('yara/libyara/modules/pe/authenticode-parser') if self.enable_magic: module.define_macros.append(('MAGIC_MODULE', '1')) module.libraries.append('magic') else: exclusions.append('yara/libyara/modules/magic/magic.c') if self.enable_cuckoo: module.define_macros.append(('CUCKOO_MODULE', '1')) module.libraries.append('jansson') else: exclusions.append('yara/libyara/modules/cuckoo/cuckoo.c') if self.enable_dex: module.define_macros.append(('DEX_MODULE', '1')) else: exclusions.append('yara/libyara/modules/dex/dex.c') if self.enable_macho: module.define_macros.append(('MACHO_MODULE', '1')) else: exclusions.append('yara/libyara/modules/macho/macho.c') # exclude pb_tests module exclusions.append('yara/libyara/modules/pb_tests/pb_tests.c') exclusions.append('yara/libyara/modules/pb_tests/pb_tests.pb-c.c') # Always turn on the DOTNET module. module.define_macros.append(('DOTNET_MODULE', '1')) exclusions = [os.path.normpath(x) for x in exclusions] for directory, _, files in os.walk('yara/libyara/'): for f in files: f = os.path.normpath(os.path.join(directory, f)) # Ignore any file that is not a .c file if not f.endswith('.c'): continue # Ignore files that are listed in the exclusion list. if any(map(lambda e: f.startswith(e), exclusions)): continue module.sources.append(f) build_ext.run(self) class UpdateCommand(Command): """Update libyara source. This is normally only run by packagers to make a new release. """ user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): subprocess.check_call(['git', 'stash'], cwd='yara') subprocess.check_call(['git', 'submodule', 'init']) subprocess.check_call(['git', 'submodule', 'update']) subprocess.check_call(['git', 'reset', '--hard'], cwd='yara') subprocess.check_call(['git', 'clean', '-x', '-f', '-d'], cwd='yara') subprocess.check_call(['git', 'checkout', 'master'], cwd='yara') subprocess.check_call(['git', 'pull'], cwd='yara') subprocess.check_call(['git', 'fetch', '--tags'], cwd='yara') tag_name = 'tags/v%s' % self.distribution.metadata.version subprocess.check_call(['git', 'checkout', tag_name], cwd='yara') subprocess.check_call(['./bootstrap.sh'], cwd='yara') subprocess.check_call(['./configure'], cwd='yara') with open('README.rst', 'r', 'utf-8') as f: readme = f.read() setup( name='yara-python', version='4.5.2', description='Python interface for YARA', long_description=readme, long_description_content_type='text/markdown', license='Apache 2.0', author='Victor M. Alvarez', author_email='plusvic@gmail.com, vmalvarez@virustotal.com', url='https://github.com/VirusTotal/yara-python', classifiers=[ 'Programming Language :: Python', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Development Status :: 5 - Production/Stable', ], zip_safe=False, cmdclass={ 'build': BuildCommand, 'build_ext': BuildExtCommand, 'update': UpdateCommand}, ext_modules=[Extension( name='yara', include_dirs=['yara/libyara/include', 'yara/libyara/', '.'], define_macros=[('BUCKETS_128', 1), ('CHECKSUM_1B', 1)], sources=['yara-python.c'])]) yara-python-4.5.3/tests.py000066400000000000000000001343651501402326500155240ustar00rootroot00000000000000#!/usr/local/bin/python # -*- coding: utf-8 -*- # # Copyright (c) 2007-2022. The YARA Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import tempfile import binascii import os import sys import unittest import yara # Python 2/3 try: import StringIO except: import io PE32_FILE = binascii.unhexlify('\ 4d5a000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000040000000\ 504500004c0101005dbe45450000000000000000e00003010b01080004000000\ 0000000000000000600100006001000064010000000040000100000001000000\ 0400000000000000040000000000000064010000600100000000000002000004\ 0000100000100000000010000010000000000000100000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000002e74657874000000\ 0400000060010000040000006001000000000000000000000000000020000060\ 6a2a58c3') ELF32_FILE = binascii.unhexlify('\ 7f454c4601010100000000000000000002000300010000006080040834000000\ a800000000000000340020000100280004000300010000000000000000800408\ 008004086c0000006c0000000500000000100000000000000000000000000000\ b801000000bb2a000000cd8000546865204e65747769646520417373656d626c\ 657220322e30352e303100002e7368737472746162002e74657874002e636f6d\ 6d656e7400000000000000000000000000000000000000000000000000000000\ 000000000000000000000000000000000b000000010000000600000060800408\ 600000000c000000000000000000000010000000000000001100000001000000\ 00000000000000006c0000001f00000000000000000000000100000000000000\ 010000000300000000000000000000008b0000001a0000000000000000000000\ 0100000000000000') ELF64_FILE = binascii.unhexlify('\ 7f454c4602010100000000000000000002003e00010000008000400000000000\ 4000000000000000c80000000000000000000000400038000100400004000300\ 0100000005000000000000000000000000004000000000000000400000000000\ 8c000000000000008c0000000000000000002000000000000000000000000000\ b801000000bb2a000000cd8000546865204e65747769646520417373656d626c\ 657220322e30352e303100002e7368737472746162002e74657874002e636f6d\ 6d656e7400000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000\ 00000000000000000b0000000100000006000000000000008000400000000000\ 80000000000000000c0000000000000000000000000000001000000000000000\ 0000000000000000110000000100000000000000000000000000000000000000\ 8c000000000000001f0000000000000000000000000000000100000000000000\ 0000000000000000010000000300000000000000000000000000000000000000\ ab000000000000001a0000000000000000000000000000000100000000000000\ 0000000000000000') # The 3 possible outcomes for each pattern [SUCCEED, FAIL, SYNTAX_ERROR] = range(3) RE_TESTS = [ # RE, string, expected result, expected matching (')', '', SYNTAX_ERROR), ('abc', 'abc', SUCCEED, 'abc'), ('abc', 'xbc', FAIL), ('abc', 'axc', FAIL), ('abc', 'abx', FAIL), ('abc', 'xabcx', SUCCEED, 'abc'), ('abc', 'ababc', SUCCEED, 'abc'), ('a.c', 'abc', SUCCEED, 'abc'), ('a.b', 'a\nb', FAIL), ('a.*b', 'acc\nccb', FAIL), ('a.{4,5}b', 'acc\nccb', FAIL), ('a.b', 'a\rb', SUCCEED, 'a\rb'), ('ab*c', 'abc', SUCCEED, 'abc'), ('ab*c', 'ac', SUCCEED, 'ac'), ('ab*bc', 'abc', SUCCEED, 'abc'), ('ab*bc', 'abbc', SUCCEED, 'abbc'), ('a.*bb', 'abbbb', SUCCEED, 'abbbb'), ('a.*?bbb', 'abbbbbb', SUCCEED, 'abbb'), ('a.*c', 'ac', SUCCEED, 'ac'), ('a.*c', 'axyzc', SUCCEED, 'axyzc'), ('ab+c', 'abbc', SUCCEED, 'abbc'), ('ab+c', 'ac', FAIL), ('ab+', 'abbbb', SUCCEED, 'abbbb'), ('ab+?', 'abbbb', SUCCEED, 'ab'), ('ab+bc', 'abc', FAIL), ('ab+bc', 'abq', FAIL), ('a+b+c', 'aabbabc', SUCCEED, 'abc'), ('ab?bc', 'abbbbc', FAIL), ('ab?c', 'abc', SUCCEED, 'abc'), ('ab*?', 'abbb', SUCCEED, 'a'), ('ab?c', 'abc', SUCCEED, 'abc'), ('ab??', 'ab', SUCCEED, 'a'), ('a(b|x)c', 'abc', SUCCEED, 'abc'), ('a(b|x)c', 'axc', SUCCEED, 'axc'), ('a(b|.)c', 'axc', SUCCEED, 'axc'), ('a(b|x|y)c', 'ayc', SUCCEED, 'ayc'), ('(a+|b)*', 'ab', SUCCEED, 'ab'), ('a|b|c|d|e', 'e', SUCCEED, 'e'), ('(a|b|c|d|e)f', 'ef', SUCCEED, 'ef'), ('.b{2}', 'abb', SUCCEED, 'abb'), ('ab{1}c', 'abc', SUCCEED, 'abc'), ('ab{1,2}c', 'abbc', SUCCEED, 'abbc'), ('ab{1,}c', 'abbbc', SUCCEED, 'abbbc'), ('ab{1,}b', 'ab', FAIL), ('ab{1}c', 'abbc', FAIL), ('ab{0,}c', 'ac', SUCCEED, 'ac'), ('ab{0,}c', 'abbbc', SUCCEED, 'abbbc'), ('ab{,3}c', 'abbbc', SUCCEED, 'abbbc'), ('ab{,2}c', 'abbbc', FAIL), ('ab{4,5}bc', 'abbbbc', FAIL), ('ab{2,3}?', 'abbbbb', SUCCEED, 'abb'), ('ab{.*}', 'ab{c}', SUCCEED, 'ab{c}'), ('.(aa){1,2}', 'aaaaaaaaaa', SUCCEED, 'aaaaa'), ('a.(bc.){2}', 'aabcabca', SUCCEED, 'aabcabca'), ('(ab{1,2}c){1,3}', 'abbcabc', SUCCEED, 'abbcabc'), ('ab(c|cc){1,3}d', 'abccccccd', SUCCEED, 'abccccccd'), ('a[bx]c', 'abc', SUCCEED, 'abc'), ('a[bx]c', 'axc', SUCCEED, 'axc'), ('a[0-9]*b', 'ab', SUCCEED, 'ab'), ('a[0-9]*b', 'a0123456789b', SUCCEED, 'a0123456789b'), ('[0-9a-f]+', '0123456789abcdef', SUCCEED, '0123456789abcdef'), ('[0-9a-f]+', 'xyz0123456789xyz', SUCCEED, '0123456789'), (r'a[\s\S]b', 'a b', SUCCEED, 'a b'), (r'a[\d\D]b', 'a1b', SUCCEED, 'a1b'), ('[x-z]+', 'abc', FAIL), ('a[-]?c', 'ac', SUCCEED, 'ac'), ('a[-b]', 'a-', SUCCEED, 'a-'), ('a[-b]', 'ab', SUCCEED, 'ab'), ('a[b-]', 'a-', SUCCEED, 'a-'), ('a[b-]', 'ab', SUCCEED, 'ab'), ('[a-c-e]', 'b', SUCCEED, 'b'), ('[a-c-e]', '-', SUCCEED, '-'), ('[a-c-e]', 'd', FAIL), ('[b-a]', '', SYNTAX_ERROR), ('(abc', '', SYNTAX_ERROR), ('abc)', '', SYNTAX_ERROR), ('a[]b', '', SYNTAX_ERROR), ('a\\', '', SYNTAX_ERROR), ('a[\\-b]', 'a-', SUCCEED, 'a-'), ('a[\\-b]', 'ab', SUCCEED, 'ab'), ('a[\\', '', SYNTAX_ERROR), ('a]', 'a]', SUCCEED, 'a]'), ('a[]]b', 'a]b', SUCCEED, 'a]b'), (r'a[\]]b', 'a]b', SUCCEED, 'a]b'), ('a[^bc]d', 'aed', SUCCEED, 'aed'), ('a[^bc]d', 'abd', FAIL), ('a[^-b]c', 'adc', SUCCEED, 'adc'), ('a[^-b]c', 'a-c', FAIL), ('a[^]b]c', 'a]c', FAIL), ('a[^]b]c', 'adc', SUCCEED, 'adc'), ('[^ab]*', 'cde', SUCCEED, 'cde'), (')(', '', SYNTAX_ERROR), (r'a\sb', 'a b', SUCCEED, 'a b'), (r'a\sb', 'a\tb', SUCCEED, 'a\tb'), (r'a\sb', 'a\rb', SUCCEED, 'a\rb'), (r'a\sb', 'a\nb', SUCCEED, 'a\nb'), (r'a\sb', 'a\vb', SUCCEED, 'a\vb'), (r'a\sb', 'a\fb', SUCCEED, 'a\fb'), (r'a\Sb', 'a b', FAIL), (r'a\Sb', 'a\tb', FAIL), (r'a\Sb', 'a\rb', FAIL), (r'a\Sb', 'a\nb', FAIL), (r'a\Sb', 'a\vb', FAIL), (r'a\Sb', 'a\fb', FAIL), (r'\n\r\t\f\a', '\n\r\t\f\a', SUCCEED, '\n\r\t\f\a'), (r'[\n][\r][\t][\f][\a]', '\n\r\t\f\a', SUCCEED, '\n\r\t\f\a'), (r'\x00\x01\x02', '\x00\x01\x02', SUCCEED, '\x00\x01\x02'), (r'[\x00-\x02]+', '\x00\x01\x02', SUCCEED, '\x00\x01\x02'), (r'[\x00-\x02]+', '\x03\x04\x05', FAIL), (r'[\x5D]', ']', SUCCEED, ']'), (r'[\0x5A-\x5D]', '\x5B', SUCCEED, '\x5B'), (r'[\x5D-\x5F]', '\x5E', SUCCEED, '\x5E'), (r'[\x5C-\x5F]', '\x5E', SUCCEED, '\x5E'), (r'[\x5D-\x5F]', '\x5E', SUCCEED, '\x5E'), (r'a\wc', 'abc', SUCCEED, 'abc'), (r'a\wc', 'a_c', SUCCEED, 'a_c'), (r'a\wc', 'a0c', SUCCEED, 'a0c'), (r'a\wc', 'a*c', FAIL), (r'\w+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'), (r'[\w]+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'), (r'\D+', '1234abc5678', SUCCEED, 'abc'), (r'[\d]+', '0123456789', SUCCEED, '0123456789'), (r'[\D]+', '1234abc5678', SUCCEED, 'abc'), (r'[\da-fA-F]+', '123abc', SUCCEED, '123abc'), ('^(ab|cd)e', 'abcde', FAIL), ('(abc|)ef', 'abcdef', SUCCEED, 'ef'), ('(abc|)ef', 'abcef', SUCCEED, 'abcef'), (r'\babc', 'abc', SUCCEED, 'abc'), (r'abc\b', 'abc', SUCCEED, 'abc'), (r'\babc', '1abc', FAIL), (r'abc\b', 'abc1', FAIL), (r'abc\s\b', 'abc x', SUCCEED, 'abc '), (r'abc\s\b', 'abc ', FAIL), (r'\babc\b', ' abc ', SUCCEED, 'abc'), (r'\b\w\w\w\b', ' abc ', SUCCEED, 'abc'), (r'\w\w\w\b', 'abcd', SUCCEED, 'bcd'), (r'\b\w\w\w', 'abcd', SUCCEED, 'abc'), (r'\b\w\w\w\b', 'abcd', FAIL), (r'\Babc', 'abc', FAIL), (r'abc\B', 'abc', FAIL), (r'\Babc', '1abc', SUCCEED, 'abc'), (r'abc\B', 'abc1', SUCCEED, 'abc'), (r'abc\s\B', 'abc x', FAIL), (r'abc\s\B', 'abc ', SUCCEED, 'abc '), (r'\w\w\w\B', 'abcd', SUCCEED, 'abc'), (r'\B\w\w\w', 'abcd', SUCCEED, 'bcd'), (r'\B\w\w\w\B', 'abcd', FAIL), # This is allowed in most regexp engines but in order to keep the # grammar free of shift/reduce conflicts I've decided not supporting # it. Users can use the (abc|) form instead. ('(|abc)ef', '', SYNTAX_ERROR), ('((a)(b)c)(d)', 'abcd', SUCCEED, 'abcd'), ('(a|b)c*d', 'abcd', SUCCEED, 'bcd'), ('(ab|ab*)bc', 'abc', SUCCEED, 'abc'), ('a([bc]*)c*', 'abc', SUCCEED, 'abc'), ('a([bc]*)c*', 'ac', SUCCEED, 'ac'), ('a([bc]*)c*', 'a', SUCCEED, 'a'), ('a([bc]*)(c*d)', 'abcd', SUCCEED, 'abcd'), ('a([bc]+)(c*d)', 'abcd', SUCCEED, 'abcd'), ('a([bc]*)(c+d)', 'abcd', SUCCEED, 'abcd'), ('a[bcd]*dcdcde', 'adcdcde', SUCCEED, 'adcdcde'), ('a[bcd]+dcdcde', 'adcdcde', FAIL), (r'\((.*), (.*)\)', '(a, b)', SUCCEED, '(a, b)'), ('abc|123$', 'abcx', SUCCEED, 'abc'), ('abc|123$', '123x', FAIL), ('abc|^123', '123', SUCCEED, '123'), ('abc|^123', 'x123', FAIL), ('^abc$', 'abc', SUCCEED, 'abc'), ('^abc$', 'abcc', FAIL), ('^abc', 'abcc', SUCCEED, 'abc'), ('^abc$', 'aabc', FAIL), ('abc$', 'aabc', SUCCEED, 'abc'), ('^a(bc+|b[eh])g|.h$', 'abhg', SUCCEED, 'abhg'), ('(bc+d$|ef*g.|h?i(j|k))', 'effgz', SUCCEED, 'effgz'), ('(bc+d$|ef*g.|h?i(j|k))', 'ij', SUCCEED, 'ij'), ('(bc+d$|ef*g.|h?i(j|k))', 'effg', FAIL), ('(bc+d$|ef*g.|h?i(j|k))', 'bcdd', FAIL), ('(bc+d$|ef*g.|h?i(j|k))', 'reffgz', SUCCEED, 'effgz'), # Test case for issue #324 ('whatever| x. x', ' xy x', SUCCEED, ' xy x'), ] def warnings_callback(warning_type, message): global warnings_callback_called, warnings_callback_message warnings_callback_called = warning_type warnings_callback_message = message class TestYara(unittest.TestCase): def assertTrueRules(self, rules, data='dummy'): for r in rules: r = yara.compile(source=r) self.assertTrue(r.match(data=data)) def assertFalseRules(self, rules, data='dummy'): for r in rules: r = yara.compile(source=r) self.assertFalse(r.match(data=data)) def assertSyntaxError(self, rules): for r in rules: self.assertRaises(yara.SyntaxError, yara.compile, source=r) def runReTest(self, test): regexp = test[0] string = test[1] expected_result = test[2] source = 'rule test { strings: $a = /%s/ condition: $a }' % regexp if expected_result == SYNTAX_ERROR: self.assertRaises(yara.SyntaxError, yara.compile, source=source) else: rule = yara.compile(source=source) matches = rule.match(data=string) if expected_result == SUCCEED: self.assertTrue(matches) matching_string = matches[0].strings[0] instance = matching_string.instances[0] if sys.version_info[0] >= 3: self.assertTrue(instance.matched_data == bytes(test[3], 'utf-8')) else: self.assertTrue(instance.matched_data == test[3]) else: self.assertFalse(matches) def testBooleanOperators(self): self.assertTrueRules([ 'rule test { condition: true }', 'rule test { condition: true or false }', 'rule test { condition: true and true }', 'rule test { condition: 0x1 and 0x2}', ]) self.assertFalseRules([ 'rule test { condition: false }', 'rule test { condition: true and false }', 'rule test { condition: false or false }' ]) def testComparisonOperators(self): self.assertTrueRules([ 'rule test { condition: 2 > 1 }', 'rule test { condition: 1 < 2 }', 'rule test { condition: 2 >= 1 }', 'rule test { condition: 1 <= 1 }', 'rule test { condition: 1 == 1 }', 'rule test { condition: 1.5 == 1.5}', 'rule test { condition: 1.0 == 1}', 'rule test { condition: 1.5 >= 1.0}', 'rule test { condition: 1.5 >= 1}', 'rule test { condition: 1.0 >= 1}', 'rule test { condition: 0.5 < 1}', 'rule test { condition: 0.5 <= 1}', 'rule rest { condition: 1.0 <= 1}', 'rule rest { condition: "abc" == "abc"}', 'rule rest { condition: "abc" <= "abc"}', 'rule rest { condition: "abc" >= "abc"}', 'rule rest { condition: "ab" < "abc"}', 'rule rest { condition: "abc" > "ab"}', 'rule rest { condition: "abc" < "abd"}', 'rule rest { condition: "abd" > "abc"}', ]) self.assertFalseRules([ 'rule test { condition: 1 != 1}', 'rule test { condition: 1 != 1.0}', 'rule test { condition: 2 > 3}', 'rule test { condition: 2.1 < 2}', 'rule test { condition: "abc" != "abc"}', 'rule test { condition: "abc" > "abc"}', 'rule test { condition: "abc" < "abc"}', ]) def testArithmeticOperators(self): self.assertTrueRules([ r'rule test { condition: (1 + 1) * 2 == (9 - 1) \ 2 }', 'rule test { condition: 5 % 2 == 1 }', 'rule test { condition: 1.5 + 1.5 == 3}', r'rule test { condition: 3 \ 2 == 1}', r'rule test { condition: 3.0 \ 2 == 1.5}', 'rule test { condition: 1 + -1 == 0}', 'rule test { condition: -1 + -1 == -2}', 'rule test { condition: 4 --2 * 2 == 8}', 'rule test { condition: -1.0 * 1 == -1.0}', 'rule test { condition: 1-1 == 0}', 'rule test { condition: -2.0-3.0 == -5}', 'rule test { condition: --1 == 1}', 'rule test { condition: 1--1 == 2}', 'rule test { condition: -0x01 == -1}', ]) def testBitwiseOperators(self): self.assertTrueRules([ 'rule test { condition: 0x55 | 0xAA == 0xFF }', 'rule test { condition: ~0xAA ^ 0x5A & 0xFF == (~0xAA) ^ (0x5A & 0xFF) }', 'rule test { condition: ~0x55 & 0xFF == 0xAA }', 'rule test { condition: 8 >> 2 == 2 }', 'rule test { condition: 1 << 3 == 8 }', 'rule test { condition: 1 | 3 ^ 3 == 1 | (3 ^ 3) }' ]) self.assertFalseRules([ 'rule test { condition: ~0xAA ^ 0x5A & 0xFF == 0x0F }', 'rule test { condition: 1 | 3 ^ 3 == (1 | 3) ^ 3}' ]) def testSyntax(self): self.assertSyntaxError([ 'rule test { strings: $a = "a" $a = "a" condition: all of them }' ]) def testAnonymousStrings(self): self.assertTrueRules([ 'rule test { strings: $ = "a" $ = "b" condition: all of them }', ], "ab") def testStrings(self): self.assertTrueRules([ 'rule test { strings: $a = "a" condition: $a }', 'rule test { strings: $a = "ab" condition: $a }', 'rule test { strings: $a = "abc" condition: $a }', 'rule test { strings: $a = "xyz" condition: $a }', 'rule test { strings: $a = "abc" nocase fullword condition: $a }', 'rule test { strings: $a = "aBc" nocase condition: $a }', 'rule test { strings: $a = "abc" fullword condition: $a }', ], "---- abc ---- xyz") self.assertFalseRules([ 'rule test { strings: $a = "a" fullword condition: $a }', 'rule test { strings: $a = "ab" fullword condition: $a }', 'rule test { strings: $a = "abc" wide fullword condition: $a }', ], "---- abc ---- xyz") self.assertTrueRules([ 'rule test { strings: $a = "a" wide condition: $a }', 'rule test { strings: $a = "a" wide ascii condition: $a }', 'rule test { strings: $a = "ab" wide condition: $a }', 'rule test { strings: $a = "ab" wide ascii condition: $a }', 'rule test { strings: $a = "abc" wide condition: $a }', 'rule test { strings: $a = "abc" wide nocase fullword condition: $a }', 'rule test { strings: $a = "aBc" wide nocase condition: $a }', 'rule test { strings: $a = "aBc" wide ascii nocase condition: $a }', 'rule test { strings: $a = "---xyz" wide nocase condition: $a }' ], "---- a\x00b\x00c\x00 -\x00-\x00-\x00-\x00x\x00y\x00z\x00") self.assertTrueRules([ 'rule test { strings: $a = "abc" fullword condition: $a }', ], "abc") self.assertFalseRules([ 'rule test { strings: $a = "abc" fullword condition: $a }', ], "xabcx") self.assertFalseRules([ 'rule test { strings: $a = "abc" fullword condition: $a }', ], "xabc") self.assertFalseRules([ 'rule test { strings: $a = "abc" fullword condition: $a }', ], "abcx") self.assertFalseRules([ 'rule test { strings: $a = "abc" ascii wide fullword condition: $a }', ], "abcx") self.assertTrueRules([ 'rule test { strings: $a = "abc" ascii wide fullword condition: $a }', ], "a\x00abc") self.assertTrueRules([ 'rule test { strings: $a = "abc" wide fullword condition: $a }', ], "a\x00b\x00c\x00") self.assertFalseRules([ 'rule test { strings: $a = "abc" wide fullword condition: $a }', ], "x\x00a\x00b\x00c\x00x\x00") self.assertFalseRules([ 'rule test { strings: $a = "ab" wide fullword condition: $a }', ], "x\x00a\x00b\x00") self.assertFalseRules([ 'rule test { strings: $a = "abc" wide fullword condition: $a }', ], "x\x00a\x00b\x00c\x00") self.assertTrueRules([ 'rule test { strings: $a = "abc" wide fullword condition: $a }', ], "x\x01a\x00b\x00c\x00") self.assertTrueRules([ 'rule test {\ strings:\ $a = "abcdef"\ $b = "cdef"\ $c = "ef"\ condition:\ all of them\ }' ], 'abcdef') def testWildcardStrings(self): self.assertTrueRules([ 'rule test {\ strings:\ $s1 = "abc"\ $s2 = "xyz"\ condition:\ for all of ($*) : ($)\ }' ], "---- abc ---- A\x00B\x00C\x00 ---- xyz") def testHexStrings(self): self.assertTrueRules([ 'rule test { strings: $a = { 64 01 00 00 60 01 } condition: $a }', 'rule test { strings: $a = { 64 0? 00 00 ?0 01 } condition: $a }', 'rule test { strings: $a = { 6? 01 00 00 60 0? } condition: $a }', 'rule test { strings: $a = { 64 01 [1-3] 60 01 } condition: $a }', 'rule test { strings: $a = { 64 01 [1-3] (60|61) 01 } condition: $a }', 'rule test { strings: $a = { 4D 5A [-] 6A 2A [-] 58 C3} condition: $a }', 'rule test { strings: $a = { 4D 5A [300-] 6A 2A [-] 58 C3} condition: $a }', 'rule test { strings: $a = { 2e 7? (65 | ??) 78 } condition: $a }' ], PE32_FILE) self.assertFalseRules([ 'rule test { strings: $a = { 4D 5A [0-300] 6A 2A } condition: $a }', 'rule test { strings: $a = { 4D 5A [0-128] 45 [0-128] 01 [0-128] C3 } condition: $a }', ], PE32_FILE) self.assertTrueRules([ 'rule test { strings: $a = { 31 32 [-] 38 39 } condition: $a }', 'rule test { strings: $a = { 31 32 [-] 33 34 [-] 38 39 } condition: $a }', 'rule test { strings: $a = { 31 32 [1] 34 35 [2] 38 39 } condition: $a }', 'rule test { strings: $a = { 31 32 [1-] 34 35 [1-] 38 39 } condition: $a }', 'rule test { strings: $a = { 31 32 [0-3] 34 35 [1-] 38 39 } condition: $a }', 'rule test { strings: $a = { 31 32 [0-2] 35 [1-] 37 38 39 } condition: $a }', ], '123456789') self.assertTrueRules([ 'rule test { strings: $a = { 31 32 [-] 38 39 } condition: all of them }', ], '123456789') self.assertFalseRules([ 'rule test { strings: $a = { 31 32 [-] 32 33 } condition: $a }', 'rule test { strings: $a = { 35 36 [-] 31 32 } condition: $a }', 'rule test { strings: $a = { 31 32 [2-] 34 35 } condition: $a }', 'rule test { strings: $a = { 31 32 [0-3] 37 38 } condition: $a }', ], '123456789') self.assertSyntaxError([ 'rule test { strings: $a = { 01 [0] 02 } condition: $a }', 'rule test { strings: $a = { [-] 01 02 } condition: $a }', 'rule test { strings: $a = { 01 02 [-] } condition: $a }', 'rule test { strings: $a = { 01 02 ([-] 03 | 04) } condition: $a }', 'rule test { strings: $a = { 01 02 (03 [-] | 04) } condition: $a }', 'rule test { strings: $a = { 01 02 (03 | 04 [-]) } condition: $a }' ]) rules = yara.compile(source='rule test { strings: $a = { 61 [0-3] (62|63) } condition: $a }') matches = rules.match(data='abbb') if sys.version_info[0] >= 3: self.assertTrue(matches[0].strings[0].identifier == '$a') self.assertTrue(matches[0].strings[0].instances[0].offset == 0) self.assertTrue(matches[0].strings[0].instances[0].matched_data == bytes('ab', 'utf-8')) else: self.assertTrue(matches[0].strings[0].identifier == '$a') self.assertTrue(matches[0].strings[0].instances[0].offset == 0) self.assertTrue(matches[0].strings[0].instances[0].matched_data == 'ab') def testCount(self): self.assertTrueRules([ 'rule test { strings: $a = "ssi" condition: #a == 2 }', ], 'mississippi') def testAt(self): self.assertTrueRules([ 'rule test { strings: $a = "ssi" condition: $a at 2 and $a at 5 }', 'rule test { strings: $a = "mis" condition: $a at ~0xFF & 0xFF }' ], 'mississippi') self.assertTrueRules([ 'rule test { strings: $a = { 00 00 00 00 ?? 74 65 78 74 } condition: $a at 308}', ], PE32_FILE) def testIn(self): self.assertTrueRules([ 'import "pe" rule test { strings: $a = { 6a 2a 58 c3 } condition: $a in (pe.entry_point .. pe.entry_point + 1) }', ], PE32_FILE) def testOffset(self): self.assertTrueRules([ 'rule test { strings: $a = "ssi" condition: @a == 2 }', 'rule test { strings: $a = "ssi" condition: @a == @a[1] }', 'rule test { strings: $a = "ssi" condition: @a[2] == 5 }' ], 'mississippi') def testLength(self): self.assertTrueRules([ 'rule test { strings: $a = /m.*?ssi/ condition: !a == 5 }', 'rule test { strings: $a = /m.*?ssi/ condition: !a[1] == 5 }', 'rule test { strings: $a = /m.*ssi/ condition: !a == 8 }', 'rule test { strings: $a = /m.*ssi/ condition: !a[1] == 8 }', 'rule test { strings: $a = /ssi.*ppi/ condition: !a[1] == 9 }', 'rule test { strings: $a = /ssi.*ppi/ condition: !a[2] == 6 }', 'rule test { strings: $a = { 6D [1-3] 73 73 69 } condition: !a == 5}', 'rule test { strings: $a = { 6D [-] 73 73 69 } condition: !a == 5}', 'rule test { strings: $a = { 6D [-] 70 70 69 } condition: !a == 11}', 'rule test { strings: $a = { 6D 69 73 73 [-] 70 69 } condition: !a == 11}', ], 'mississippi') def testOf(self): self.assertTrueRules([ 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: any of them }', 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: 1 of them }', 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: 2 of them }', 'rule test { strings: $a1 = "dummy1" $b1 = "dummy1" $b2 = "ssi" condition: any of ($a*, $b*) }', ], 'mississipi') self.assertTrueRules([""" rule test { strings: $ = /abc/ $ = /def/ $ = /ghi/ condition: for any of ($*) : ( for any i in (1..#): (uint8(@[i] - 1) == 0x00) ) }""" ], 'abc\x00def\x00ghi') self.assertFalseRules([ 'rule test { strings: $a = "ssi" $b = "mis" $c = "oops" condition: all of them }' ], 'mississipi') self.assertSyntaxError([ 'rule test { condition: all of ($a*) }', 'rule test { condition: all of them }' ]) def testFor(self): self.assertTrueRules([ 'rule test { strings: $a = "ssi" condition: for all i in (1..#a) : (@a[i] >= 2 and @a[i] <= 5) }', 'rule test { strings: $a = "ssi" $b = "mi" condition: for all i in (1..#a) : ( for all j in (1..#b) : (@a[i] >= @b[j])) }' ], 'mississipi') self.assertFalseRules([ 'rule test { strings: $a = "ssi" condition: for all i in (1..#a) : (@a[i] == 5) }', ], 'mississipi') def testXorKey(self): global rule_data rule_data = None def callback(data): global rule_data rule_data = data return yara.CALLBACK_CONTINUE r = yara.compile(source='rule test { strings: $a = "dummy" xor(1-2) condition: $a }') r.match(data='etllxfwoo{', callback=callback) self.assertTrue(rule_data['matches']) self.assertEqual(rule_data['rule'], 'test') self.assertEqual(len(rule_data['strings']), 1) string = rule_data['strings'][0] self.assertEqual(len(string.instances), 2) self.assertEqual(string.instances[0].xor_key, 1) self.assertEqual(string.instances[1].xor_key, 2) # Make sure plaintext() works. self.assertTrue(string.instances[0].plaintext() == b'dummy') # Test that the xor_key for matched strings is 0 if the string is not an xor # string. We always want to make sure this is set! def testXorKeyNoXorString(self): global rule_data rule_data = None def callback(data): global rule_data rule_data = data return yara.CALLBACK_CONTINUE r = yara.compile(source='rule test { strings: $a = "dummy" condition: $a }') r.match(data='dummy', callback=callback) self.assertTrue(rule_data['matches']) self.assertEqual(rule_data['rule'],'test') self.assertEqual(len(rule_data['strings']), 1) self.assertEqual(rule_data['strings'][0].instances[0].xor_key, 0) def testMatchedLength(self): yara.set_config(max_match_data=2) r = yara.compile(source='rule test { strings: $a = "dummy" condition: $a }') matches = r.match(data='dummy') self.assertEqual(matches[0].strings[0].instances[0].matched_length, 5) self.assertEqual(matches[0].strings[0].instances[0].matched_data, b'du') yara.set_config(max_match_data=512) def testRE(self): self.assertTrueRules([ 'rule test { strings: $a = /ssi/ condition: $a }', 'rule test { strings: $a = /ssi(s|p)/ condition: $a }', 'rule test { strings: $a = /ssim*/ condition: $a }', 'rule test { strings: $a = /ssa?/ condition: $a }', 'rule test { strings: $a = /Miss/ nocase condition: $a }', 'rule test { strings: $a = /(M|N)iss/ nocase condition: $a }', 'rule test { strings: $a = /[M-N]iss/ nocase condition: $a }', 'rule test { strings: $a = /(Mi|ssi)ssippi/ nocase condition: $a }', r'rule test { strings: $a = /ppi\tmi/ condition: $a }', r'rule test { strings: $a = /ppi\.mi/ condition: $a }', 'rule test { strings: $a = /^mississippi/ fullword condition: $a }', 'rule test { strings: $a = /mississippi.*mississippi$/s condition: $a }', ], 'mississippi\tmississippi.mississippi\nmississippi') self.assertFalseRules([ 'rule test { strings: $a = /^ssi/ condition: $a }', 'rule test { strings: $a = /ssi$/ condition: $a }', 'rule test { strings: $a = /ssissi/ fullword condition: $a }', 'rule test { strings: $a = /^[isp]+/ condition: $a }' ], 'mississippi') for test in RE_TESTS: try: self.runReTest(test) except Exception as e: print('\nFailed test: %s\n' % str(test)) raise e def testEntrypoint(self): self.assertTrueRules([ 'rule test { strings: $a = { 6a 2a 58 c3 } condition: $a at entrypoint }', ], PE32_FILE) self.assertTrueRules([ 'rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }', ], ELF32_FILE) self.assertTrueRules([ 'rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }', ], ELF64_FILE) self.assertFalseRules([ 'rule test { condition: entrypoint >= 0 }', ]) # This test ensures that anything after the NULL character is stripped. def testMetaNull(self): r = yara.compile(source=r'rule test { meta: a = "foo\x00bar\x80" condition: true }') self.assertTrue((list(r)[0].meta['a']) == 'foo') def testMeta(self): r = yara.compile(source=r""" rule test { meta: a = "foo\x80bar" b = "ñ" c = "\xc3\xb1" condition: true } """) meta = list(r)[0].meta if sys.version_info > (3, 0): self.assertTrue(meta['a'] == 'foobar') else: self.assertTrue(meta['a'] == 'foo\x80bar') self.assertTrue(meta['b'] == 'ñ') self.assertTrue(meta['c'] == 'ñ') # This test is similar to testMeta but it tests the meta data generated # when a Match object is created. def testScanMeta(self): r = yara.compile(source=r""" rule test { meta: a = "foo\x80bar" b = "ñ" c = "\xc3\xb1" condition: true } """) m = r.match(data='dummy') meta = list(m)[0].meta if sys.version_info > (3, 0): self.assertTrue(meta['a'] == 'foobar') else: self.assertTrue(meta['a'] == 'foo\x80bar') self.assertTrue(meta['b'] == 'ñ') self.assertTrue(meta['c'] == 'ñ') # This test is similar to testScanMeta but it tests for displaying multiple values in the meta data generated # when a Match object is created (upon request). def testDuplicateMeta(self): r = yara.compile(source=""" rule test { meta: a = 1 a = 2 b = 3 condition: true } """) # Default behaviour should produce a simple KV map and should use the 'latest' metadata value per field meta = r.match(data="dummy")[0].meta self.assertTrue(meta['a'] == 2 and meta['b'] == 3) # `allow_duplicate_metadata` flag should reveal all metadata values per field as a list meta = r.match(data="dummy", allow_duplicate_metadata=True)[0].meta self.assertTrue(meta['a'] == [1, 2] and meta['b'] == [3]) def testFilesize(self): self.assertTrueRules([ 'rule test { condition: filesize == %d }' % len(PE32_FILE), ], PE32_FILE) def testTooManyArguments(self): self.assertRaises(TypeError, yara.compile, 'rules1.yar', 'rules2.yar') def testCompileFile(self): f = tempfile.TemporaryFile('wt') f.write('rule test { condition: true }') f.flush() f.seek(0) r = yara.compile(file=f) f.close() self.assertTrue(r.match(data=PE32_FILE)) def testCompileFiles(self): tmpdir = tempfile.gettempdir() p1 = os.path.join(tmpdir, 'test1') f1 = open(p1, 'wt') f1.write('rule test1 { condition: true }') f1.close() p2 = os.path.join(tmpdir, 'test2') t2 = open(p2, 'wt') t2.write('rule test2 { condition: true }') t2.close() r = yara.compile(filepaths={ 'test1': p1, 'test2': p2 }) self.assertTrue(len(r.match(data='dummy')) == 2) for m in r.match(data='dummy'): self.assertTrue(m.rule in ('test1', 'test2')) self.assertTrue(m.namespace == m.rule) os.remove(p1) os.remove(p2) def testIncludeFiles(self): tmpdir = tempfile.gettempdir() p1 = os.path.join(tmpdir, 'test1') f1 = open(p1, 'wt') f1.write('rule test1 { condition: true }') f1.close() p2 = os.path.join(tmpdir, 'test2') f2 = open(p2, 'wt') f2.write('include "%s" rule test2 { condition: test1 }' % p1) f2.close() r = yara.compile(p2) self.assertTrue(len(r.match(data='dummy')) == 2) self.assertRaises(yara.SyntaxError, yara.compile, source='include "test2"', includes=False) def testExternals(self): r = yara.compile(source='rule test { condition: ext_int == 15 }', externals={'ext_int': 15}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_int == -15}', externals={'ext_int': -15}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_float == 3.14 }', externals={'ext_float': 3.14}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_float == -0.5 }', externals={'ext_float': -0.5}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_bool }', externals={'ext_bool': True}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str }', externals={'ext_str': ''}) self.assertFalse(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str }', externals={'ext_str': 'foo'}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_bool }', externals={'ext_bool': False}) self.assertFalse(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str contains "ssi" }', externals={'ext_str': 'mississippi'}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /foo/ }', externals={'ext_str': ''}) self.assertFalse(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /foo/ }', externals={'ext_str': 'FOO'}) self.assertFalse(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /foo/i }', externals={'ext_str': 'FOO'}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /ssi(s|p)/ }', externals={'ext_str': 'mississippi'}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /ppi$/ }', externals={'ext_str': 'mississippi'}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /ssi$/ }', externals={'ext_str': 'mississippi'}) self.assertFalse(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /^miss/ }', externals={'ext_str': 'mississippi'}) self.assertTrue(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /^iss/ }', externals={'ext_str': 'mississippi'}) self.assertFalse(r.match(data='dummy')) r = yara.compile(source='rule test { condition: ext_str matches /ssi$/ }', externals={'ext_str': 'mississippi'}) self.assertFalse(r.match(data='dummy')) if sys.version_info[0] >= 3: self.assertTrue(yara.compile( source="rule test { condition: true}", externals={'foo': u'\u6765\u6613\u7f51\u7edc\u79d1'})) else: self.assertRaises(UnicodeEncodeError, yara.compile, source="rule test { condition: true}", externals={'foo': u'\u6765\u6613\u7f51\u7edc\u79d1'}) def testCallbackAll(self): global rule_data rule_data = [] def callback(data): global rule_data rule_data.append(data) return yara.CALLBACK_CONTINUE r = yara.compile(source='rule t { condition: true } rule f { condition: false }') r.match(data='dummy', callback=callback, which_callbacks=yara.CALLBACK_ALL) self.assertTrue(len(rule_data) == 2) def testCallback(self): global rule_data rule_data = None def callback(data): global rule_data rule_data = data return yara.CALLBACK_CONTINUE r = yara.compile(source='rule test { strings: $a = { 50 45 00 00 4c 01 } condition: $a }') r.match(data=PE32_FILE, callback=callback) self.assertTrue(rule_data['matches']) self.assertTrue(rule_data['rule'] == 'test') rule_data = None r = yara.compile(source='rule test { condition: false }') r.match(data='dummy', callback=callback, which_callbacks=yara.CALLBACK_NON_MATCHES) self.assertTrue(rule_data['rule'] == 'test') rule_data = None r = yara.compile(source='rule test { condition: true }') r.match(data='dummy', callback=callback, which_callbacks=yara.CALLBACK_MATCHES) self.assertTrue(rule_data['rule'] == 'test') def testIncludeCallback(self): def callback(requested_filename, filename, namespace): if requested_filename == 'foo': return 'rule included {condition: true }' return None r = yara.compile(source='include "foo" rule r { condition: included }', include_callback=callback) self.assertTrue(r.match(data='dummy')) def testConsoleCallback(self): global called called = False def callback(message): global called called = True return yara.CALLBACK_CONTINUE r = yara.compile(source='import "console" rule r { condition: console.log("AXSERS") }') r.match(data='dummy', console_callback=callback) self.assertTrue(called) def testCompare(self): r = yara.compile(sources={ 'test1': 'rule test { condition: true}', 'test2': 'rule test { condition: true}' }) m = r.match(data="dummy") self.assertTrue(len(m) == 2) if sys.version_info[0] < 3: self.assertTrue(m[0] < m[1]) self.assertTrue(m[0] != m[1]) self.assertFalse(m[0] > m[1]) self.assertFalse(m[0] == m[1]) def testComments(self): self.assertTrueRules([ """ rule test { condition: // this is a comment /*** this is a comment ***/ /* /* /* this is a comment */ true } """, ]) def testModules(self): self.assertTrueRules([ 'import "tests" rule test { condition: tests.constants.one + 1 == tests.constants.two }', 'import "tests" rule test { condition: tests.constants.foo == "foo" }', 'import "tests" rule test { condition: tests.constants.empty == "" }', 'import "tests" rule test { condition: tests.empty() == "" }', 'import "tests" rule test { condition: tests.struct_array[1].i == 1 }', 'import "tests" rule test { condition: tests.struct_array[0].i == 1 or true}', 'import "tests" rule test { condition: tests.integer_array[0] == 0}', 'import "tests" rule test { condition: tests.integer_array[1] == 1}', 'import "tests" rule test { condition: tests.string_array[0] == "foo"}', 'import "tests" rule test { condition: tests.string_array[2] == "baz"}', 'import "tests" rule test { condition: tests.string_dict["foo"] == "foo"}', 'import "tests" rule test { condition: tests.string_dict["bar"] == "bar"}', 'import "tests" rule test { condition: tests.isum(1,2) == 3}', 'import "tests" rule test { condition: tests.isum(1,2,3) == 6}', 'import "tests" rule test { condition: tests.fsum(1.0,2.0) == 3.0}', 'import "tests" rule test { condition: tests.fsum(1.0,2.0,3.0) == 6.0}', 'import "tests" rule test { condition: tests.length("dummy") == 5}', ]) self.assertFalseRules([ 'import "tests" rule test { condition: tests.struct_array[0].i == 1 }', 'import "tests" rule test { condition: tests.isum(1,1) == 3}', 'import "tests" rule test { condition: tests.fsum(1.0,1.0) == 3.0}', ]) def testIntegerFunctions(self): self.assertTrueRules([ 'rule test { condition: uint8(0) == 0xAA}', 'rule test { condition: uint16(0) == 0xBBAA}', 'rule test { condition: uint32(0) == 0xDDCCBBAA}', 'rule test { condition: uint8be(0) == 0xAA}', 'rule test { condition: uint16be(0) == 0xAABB}', 'rule test { condition: uint32be(0) == 0xAABBCCDD}', ], b'\xAA\xBB\xCC\xDD') def testStringIO(self): # Python 2/3 try: stream = StringIO.StringIO() except: stream = io.BytesIO() r1 = yara.compile(source='rule test { condition: true }') r1.save(file=stream) stream.seek(0) r2 = yara.load(file=stream) m = r2.match(data="dummy") self.assertTrue(len(m) == 1) def testModuleData(self): data = {} def callback(module_data): data['constants'] = module_data.get('constants') r1 = yara.compile( source='import "tests" rule test { condition: false }') r1.match(data='', modules_callback=callback) if sys.version_info[0] >= 3: self.assertTrue(data['constants']['foo'] == bytes('foo', 'utf-8')) self.assertTrue(data['constants']['empty'] == bytes('', 'utf-8')) else: self.assertTrue(data['constants']['foo'] == 'foo') self.assertTrue(data['constants']['empty'] == '') self.assertTrue(data['constants']['one'] == 1) self.assertTrue(data['constants']['two'] == 2) def testRulesIterator(self): rules = yara.compile( source=''' rule test1 { condition: false } rule test2 { condition: false } rule test3 { condition: false } ''') for i, r in enumerate(rules, start=1): self.assertTrue(r.identifier == 'test%d' % i) it = iter(rules) r = next(it) self.assertTrue(r.identifier == 'test1') r = next(it) self.assertTrue(r.identifier == 'test2') r = next(it) self.assertTrue(r.identifier == 'test3') def testSetConfig(self): yara.set_config(max_strings_per_rule=1) self.assertSyntaxError([''' rule test { strings: $a = "1" $b = "2" condition: all of them } ''']) yara.set_config(max_strings_per_rule=10000) def testGlobalPrivate(self): rules = """ global rule a { condition: true } private rule b { condition: true } """ # Have to convert to a list because Rules are not subscriptable, yet... r = list(yara.compile(source=rules)) self.assertTrue(r[0].is_global == True) self.assertTrue(r[1].is_private == True) def testMatchMemoryview(self): r = yara.compile(source='rule test { strings: $s = "test" condition: $s }') data = memoryview(b"test") self.assertTrue(r.match(data=data)) def testWarningCallback(self): global warnings_callback_called, warnings_callback_message warnings_callback_called = False warnings_callback_message = None r = yara.compile(sources={'ns1': 'rule x { strings: $x = "X" condition: $x }'}) data = memoryview(b"X" * 1000099) r.match(data=data, warnings_callback=warnings_callback) self.assertTrue(warnings_callback_called == yara.CALLBACK_TOO_MANY_MATCHES) self.assertTrue(warnings_callback_message == ("ns1", "x", "$x")) self.assertTrue(warnings_callback_message.namespace == "ns1") self.assertTrue(warnings_callback_message.rule == "x") self.assertTrue(warnings_callback_message.string == "$x") def testCompilerErrorOnWarning(self): # Make sure we always throw on warnings if requested, and that warnings # are accumulated. rules = """ rule a { strings: $a = "A" condition: $a } rule b { strings: $b = "B" condition: $b } """ expected = [ 'line 2: string "$a" may slow down scanning', 'line 3: string "$b" may slow down scanning', ] with self.assertRaises(yara.WarningError) as ctx: yara.compile(source=rules, error_on_warning=True) e = ctx.exception self.assertListEqual(e.warnings, expected) # Now make sure the warnings member is set if error_on_warning is not # set. rules = yara.compile(source=rules) self.assertListEqual(rules.warnings, expected) if __name__ == "__main__": unittest.main() yara-python-4.5.3/yara/000077500000000000000000000000001501402326500147305ustar00rootroot00000000000000yara-python-4.5.3/yara-python.c000066400000000000000000002310361501402326500164200ustar00rootroot00000000000000/* Copyright (c) 2007-2022. The YARA Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* headers */ #define PY_SSIZE_T_CLEAN #include #include "structmember.h" #if PY_VERSION_HEX >= 0x02060000 #include "bytesobject.h" #include "structseq.h" #elif PY_VERSION_HEX < 0x02060000 #define PyBytes_AsString PyString_AsString #define PyBytes_Check PyString_Check #define PyBytes_FromStringAndSize PyString_FromStringAndSize #endif #include #include #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #endif #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, #endif #if PY_VERSION_HEX < 0x03020000 typedef long Py_hash_t; #endif #if PY_MAJOR_VERSION >= 3 #define PY_STRING(x) PyUnicode_DecodeUTF8(x, strlen(x), "ignore" ) #define PY_STRING_FORMAT(...) PyUnicode_FromFormat(__VA_ARGS__) #define PY_STRING_TO_C(x) PyUnicode_AsUTF8(x) #define PY_STRING_CHECK(x) PyUnicode_Check(x) #else #define PY_STRING(x) PyString_FromString(x) #define PY_STRING_FORMAT(...) PyString_FromFormat(__VA_ARGS__) #define PY_STRING_TO_C(x) PyString_AsString(x) #define PY_STRING_CHECK(x) (PyString_Check(x) || PyUnicode_Check(x)) #endif #if PY_VERSION_HEX < 0x03020000 #define PyDescr_NAME(x) (((PyDescrObject*)x)->d_name) #endif /* Module globals */ static PyObject* YaraError = NULL; static PyObject* YaraSyntaxError = NULL; static PyObject* YaraTimeoutError = NULL; static PyObject* YaraWarningError = NULL; #define YARA_DOC "\ This module allows you to apply YARA rules to files or strings.\n\ \n\ For complete documentation please visit:\n\ https://yara.readthedocs.io/en/stable/yarapython.html\n" #if defined(_WIN32) || defined(__CYGWIN__) #include #define strdup _strdup #endif // Match object typedef struct { PyObject_HEAD PyObject* rule; PyObject* ns; PyObject* tags; PyObject* meta; PyObject* strings; } Match; static PyMemberDef Match_members[] = { { "rule", T_OBJECT_EX, offsetof(Match, rule), READONLY, "Name of the matching rule" }, { "namespace", T_OBJECT_EX, offsetof(Match, ns), READONLY, "Namespace of the matching rule" }, { "tags", T_OBJECT_EX, offsetof(Match, tags), READONLY, "List of tags associated to the rule" }, { "meta", T_OBJECT_EX, offsetof(Match, meta), READONLY, "Dictionary with metadata associated to the rule" }, { "strings", T_OBJECT_EX, offsetof(Match, strings), READONLY, "Tuple with offsets and strings that matched the file" }, { NULL } // End marker }; static PyObject* Match_NEW( const char* rule, const char* ns, PyObject* tags, PyObject* meta, PyObject* strings); static void Match_dealloc( PyObject* self); static PyObject* Match_repr( PyObject* self); static PyObject* Match_getattro( PyObject* self, PyObject* name); static PyObject* Match_richcompare( PyObject* self, PyObject* other, int op); static Py_hash_t Match_hash( PyObject* self); static PyMethodDef Match_methods[] = { { NULL }, }; static PyTypeObject Match_Type = { PyVarObject_HEAD_INIT(NULL, 0) "yara.Match", /*tp_name*/ sizeof(Match), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Match_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ Match_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ Match_hash, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ Match_getattro, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "Match class", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ Match_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Match_methods, /* tp_methods */ Match_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; // StringMatch object typedef struct { PyObject_HEAD PyObject* identifier; PyObject* instances; // This is not exposed directly because it contains flags that are internal // to yara (eg: STRING_FLAGS_FITS_IN_ATOM) along with modifiers // (eg: STRING_FLAGS_XOR). uint64_t flags; } StringMatch; static PyMemberDef StringMatch_members[] = { { "identifier", T_OBJECT_EX, offsetof(StringMatch, identifier), READONLY, "Name of the matching string" }, { "instances", T_OBJECT_EX, offsetof(StringMatch, instances), READONLY, "StringMatchInstance objects of the matching string" }, { NULL } // End marker }; static PyObject* StringMatch_NEW( const char* identifier, uint64_t flags, PyObject* instance_list); static void StringMatch_dealloc( PyObject* self); static PyObject* StringMatch_repr( PyObject* self); static PyObject* StringMatch_getattro( PyObject* self, PyObject* name); static Py_hash_t StringMatch_hash( PyObject* self); static PyObject* StringMatch_is_xor( PyObject* self, PyObject* args); static PyMethodDef StringMatch_methods[] = { { "is_xor", (PyCFunction) StringMatch_is_xor, METH_NOARGS, "Return true if a string has the xor modifier" }, { NULL }, }; static PyTypeObject StringMatch_Type = { PyVarObject_HEAD_INIT(NULL, 0) "yara.StringMatch", /*tp_name*/ sizeof(StringMatch), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)StringMatch_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ StringMatch_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ StringMatch_hash, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ StringMatch_getattro, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "StringMatch class", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ // XXX: Implement richcompare? 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ StringMatch_methods, /* tp_methods */ StringMatch_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; // StringMatchInstance object typedef struct { PyObject_HEAD PyObject* offset; PyObject* matched_data; PyObject* matched_length; PyObject* xor_key; } StringMatchInstance; static PyMemberDef StringMatchInstance_members[] = { { "offset", T_OBJECT_EX, offsetof(StringMatchInstance, offset), READONLY, "Offset of the matched data" }, { "matched_data", T_OBJECT_EX, offsetof(StringMatchInstance, matched_data), READONLY, "Matched data" }, { "matched_length", T_OBJECT_EX, offsetof(StringMatchInstance, matched_length), READONLY, "Length of matched data" }, { "xor_key", T_OBJECT_EX, offsetof(StringMatchInstance, xor_key), READONLY, "XOR key found for xor strings" }, { NULL } // End marker }; static PyObject* StringMatchInstance_NEW( uint64_t offset, PyObject* matched_data, int32_t match_length, uint8_t xor_key); static void StringMatchInstance_dealloc( PyObject* self); static PyObject* StringMatchInstance_repr( PyObject* self); static PyObject* StringMatchInstance_getattro( PyObject* self, PyObject* name); static Py_hash_t StringMatchInstance_hash( PyObject* self); static PyObject* StringMatchInstance_plaintext( PyObject* self, PyObject* args); static PyMethodDef StringMatchInstance_methods[] = { { "plaintext", (PyCFunction) StringMatchInstance_plaintext, METH_NOARGS, "Return matched data after xor key applied." }, { NULL }, }; static PyTypeObject StringMatchInstance_Type = { PyVarObject_HEAD_INIT(NULL, 0) "yara.StringMatchInstance", /*tp_name*/ sizeof(StringMatchInstance), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)StringMatchInstance_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ StringMatchInstance_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ StringMatchInstance_hash, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ StringMatchInstance_getattro, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "StringMatchInstance class", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ // XXX: Implement richcompare? 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ StringMatchInstance_methods, /* tp_methods */ StringMatchInstance_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; // Rule object typedef struct { PyObject_HEAD PyObject* identifier; PyObject* tags; PyObject* meta; PyObject* global; PyObject* private; } Rule; static void Rule_dealloc( PyObject* self); static PyObject* Rule_getattro( PyObject* self, PyObject* name); static PyMemberDef Rule_members[] = { { "is_global", T_OBJECT_EX, offsetof(Rule, global), READONLY, "Rule is global" }, { "is_private", T_OBJECT_EX, offsetof(Rule, private), READONLY, "Rule is private" }, { "identifier", T_OBJECT_EX, offsetof(Rule, identifier), READONLY, "Name of the rule" }, { "tags", T_OBJECT_EX, offsetof(Rule, tags), READONLY, "Tags for the rule" }, { "meta", T_OBJECT_EX, offsetof(Rule, meta), READONLY, "Meta for the rule" }, { NULL } // End marker }; static PyMethodDef Rule_methods[] = { { NULL, NULL } }; static PyTypeObject Rule_Type = { PyVarObject_HEAD_INIT(NULL, 0) "yara.Rule", /*tp_name*/ sizeof(Rule), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor) Rule_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ Rule_getattro, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "Rule class", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Rule_methods, /* tp_methods */ Rule_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; // Rules object typedef struct { PyObject_HEAD PyObject* externals; PyObject* warnings; YR_RULES* rules; YR_RULE* iter_current_rule; } Rules; static Rules* Rules_NEW(void); static void Rules_dealloc( PyObject* self); static PyObject* Rules_match( PyObject* self, PyObject* args, PyObject* keywords); static PyObject* Rules_save( PyObject* self, PyObject* args, PyObject* keywords); static PyObject* Rules_profiling_info( PyObject* self, PyObject* args); static PyObject* Rules_getattro( PyObject* self, PyObject* name); static PyObject* Rules_next( PyObject* self); static PyMemberDef Rules_members[] = { { "warnings", T_OBJECT_EX, offsetof(Rules, warnings), READONLY, "List of compiler warnings" }, { NULL } // End marker }; static PyMethodDef Rules_methods[] = { { "match", (PyCFunction) Rules_match, METH_VARARGS | METH_KEYWORDS }, { "save", (PyCFunction) Rules_save, METH_VARARGS | METH_KEYWORDS }, { "profiling_info", (PyCFunction) Rules_profiling_info, METH_NOARGS }, { NULL, NULL } }; static PyTypeObject Rules_Type = { PyVarObject_HEAD_INIT(NULL, 0) "yara.Rules", /*tp_name*/ sizeof(Rules), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor) Rules_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ Rules_getattro, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "Rules class", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc) Rules_next, /* tp_iternext */ Rules_methods, /* tp_methods */ Rules_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; typedef struct _CALLBACK_DATA { PyObject* matches; PyObject* callback; PyObject* modules_data; PyObject* modules_callback; PyObject* warnings_callback; PyObject* console_callback; int which; bool allow_duplicate_metadata; } CALLBACK_DATA; static PyStructSequence_Field RuleString_Fields[] = { {"namespace", "Namespace of the rule"}, {"rule", "Identifier of the rule"}, {"string", "Identifier of the string"}, {NULL} }; static PyStructSequence_Desc RuleString_Desc = { "RuleString", "Named tuple tying together rule identifier and string identifier", RuleString_Fields, (sizeof(RuleString_Fields) / sizeof(RuleString_Fields[0])) - 1 }; static PyTypeObject RuleString_Type = {0}; // Forward declarations for handling module data. PyObject* convert_structure_to_python( YR_OBJECT_STRUCTURE* structure); PyObject* convert_array_to_python( YR_OBJECT_ARRAY* array); PyObject* convert_dictionary_to_python( YR_OBJECT_DICTIONARY* dictionary); PyObject* convert_object_to_python( YR_OBJECT* object) { PyObject* result = NULL; if (object == NULL) return NULL; switch(object->type) { case OBJECT_TYPE_INTEGER: if (object->value.i != YR_UNDEFINED) result = Py_BuildValue("l", object->value.i); break; case OBJECT_TYPE_STRING: if (object->value.ss != NULL) result = PyBytes_FromStringAndSize( object->value.ss->c_string, object->value.ss->length); break; case OBJECT_TYPE_STRUCTURE: result = convert_structure_to_python(object_as_structure(object)); break; case OBJECT_TYPE_ARRAY: result = convert_array_to_python(object_as_array(object)); break; case OBJECT_TYPE_FUNCTION: // Do nothing with functions... break; case OBJECT_TYPE_DICTIONARY: result = convert_dictionary_to_python(object_as_dictionary(object)); break; case OBJECT_TYPE_FLOAT: if (!isnan(object->value.d)) result = Py_BuildValue("d", object->value.d); break; default: break; } return result; } PyObject* convert_structure_to_python( YR_OBJECT_STRUCTURE* structure) { YR_STRUCTURE_MEMBER* member; PyObject* py_object; PyObject* py_dict = PyDict_New(); if (py_dict == NULL) return py_dict; member = structure->members; while (member != NULL) { py_object = convert_object_to_python(member->object); if (py_object != NULL) { PyDict_SetItemString(py_dict, member->object->identifier, py_object); Py_DECREF(py_object); } member =member->next; } return py_dict; } PyObject* convert_array_to_python( YR_OBJECT_ARRAY* array) { PyObject* py_object; PyObject* py_list = PyList_New(0); if (py_list == NULL) return py_list; // If there is nothing in the list, return an empty Python list if (array->items == NULL) return py_list; for (int i = 0; i < array->items->length; i++) { py_object = convert_object_to_python(array->items->objects[i]); if (py_object != NULL) { PyList_Append(py_list, py_object); Py_DECREF(py_object); } } return py_list; } PyObject* convert_dictionary_to_python( YR_OBJECT_DICTIONARY* dictionary) { PyObject* py_object; PyObject* py_dict = PyDict_New(); if (py_dict == NULL) return py_dict; // If there is nothing in the YARA dictionary, return an empty Python dict if (dictionary->items == NULL) return py_dict; for (int i = 0; i < dictionary->items->used; i++) { py_object = convert_object_to_python(dictionary->items->objects[i].obj); if (py_object != NULL) { PyDict_SetItemString( py_dict, dictionary->items->objects[i].key->c_string, py_object); Py_DECREF(py_object); } } return py_dict; } static int handle_import_module( YR_MODULE_IMPORT* module_import, CALLBACK_DATA* data) { if (data->modules_data == NULL) return CALLBACK_CONTINUE; PyGILState_STATE gil_state = PyGILState_Ensure(); PyObject* module_data = PyDict_GetItemString( data->modules_data, module_import->module_name); #if PY_MAJOR_VERSION >= 3 if (module_data != NULL && PyBytes_Check(module_data)) #else if (module_data != NULL && PyString_Check(module_data)) #endif { Py_ssize_t data_size; #if PY_MAJOR_VERSION >= 3 PyBytes_AsStringAndSize( module_data, (char**) &module_import->module_data, &data_size); #else PyString_AsStringAndSize( module_data, (char**) &module_import->module_data, &data_size); #endif module_import->module_data_size = data_size; } PyGILState_Release(gil_state); return CALLBACK_CONTINUE; } static int handle_module_imported( void* message_data, CALLBACK_DATA* data) { if (data->modules_callback == NULL) return CALLBACK_CONTINUE; PyGILState_STATE gil_state = PyGILState_Ensure(); PyObject* module_info_dict = convert_structure_to_python( object_as_structure(message_data)); if (module_info_dict == NULL) { PyGILState_Release(gil_state); return CALLBACK_CONTINUE; } PyObject* object = PY_STRING(object_as_structure(message_data)->identifier); PyDict_SetItemString(module_info_dict, "module", object); Py_DECREF(object); Py_INCREF(data->modules_callback); PyObject* callback_result = PyObject_CallFunctionObjArgs( data->modules_callback, module_info_dict, NULL); int result = CALLBACK_CONTINUE; if (callback_result != NULL) { #if PY_MAJOR_VERSION >= 3 if (PyLong_Check(callback_result)) #else if (PyLong_Check(callback_result) || PyInt_Check(callback_result)) #endif { result = (int) PyLong_AsLong(callback_result); } } else { result = CALLBACK_ERROR; } Py_XDECREF(callback_result); Py_DECREF(module_info_dict); Py_DECREF(data->modules_callback); PyGILState_Release(gil_state); return result; } static int handle_console_log( void* message_data, CALLBACK_DATA* data) { PyGILState_STATE gil_state = PyGILState_Ensure(); int result = CALLBACK_CONTINUE; if (data->console_callback == NULL) { // If the user does not specify a console callback we dump to stdout. // If we want to support 3.2 and newer only we can use // https://docs.python.org/3/c-api/sys.html?highlight=stdout#c.PySys_FormatStdout // instead of this call with the limit. PySys_WriteStdout("%.1000s\n", (char*) message_data); } else { PyObject* log_string = PY_STRING((char*) message_data); Py_INCREF(data->console_callback); PyObject* callback_result = PyObject_CallFunctionObjArgs( data->console_callback, log_string, NULL); if (callback_result != NULL) { #if PY_MAJOR_VERSION >= 3 if (PyLong_Check(callback_result)) #else if (PyLong_Check(callback_result) || PyInt_Check(callback_result)) #endif { result = (int) PyLong_AsLong(callback_result); } } else { result = CALLBACK_ERROR; } Py_DECREF(log_string); Py_XDECREF(callback_result); Py_DECREF(data->console_callback); } PyGILState_Release(gil_state); return result; } static int handle_too_many_matches( YR_SCAN_CONTEXT* context, YR_STRING* string, CALLBACK_DATA* data) { PyGILState_STATE gil_state = PyGILState_Ensure(); PyObject* warning_type = NULL; PyObject* string_identifier = NULL; PyObject* rule_identifier = NULL; PyObject* namespace_identifier = NULL; PyObject* rule_string = NULL; YR_RULE* rule = NULL; int result = CALLBACK_CONTINUE; if (data->warnings_callback == NULL) { char message[200]; snprintf( message, sizeof(message), "too many matches for string %s in rule \"%s\"", string->identifier, context->rules->rules_table[string->rule_idx].identifier); if (PyErr_WarnEx(PyExc_RuntimeWarning, message, 1) == -1) result = CALLBACK_ERROR; } else { Py_INCREF(data->warnings_callback); string_identifier = PY_STRING(string->identifier); if (string_identifier == NULL) { result = CALLBACK_ERROR; goto _exit; } rule = &context->rules->rules_table[string->rule_idx]; rule_identifier = PY_STRING(rule->identifier); if (rule_identifier == NULL) { result = CALLBACK_ERROR; goto _exit; } namespace_identifier = PY_STRING(rule->ns->name); if (namespace_identifier == NULL) { result = CALLBACK_ERROR; goto _exit; } rule_string = PyStructSequence_New(&RuleString_Type); if (rule_string == NULL) { result = CALLBACK_ERROR; goto _exit; } PyStructSequence_SET_ITEM(rule_string, 0, namespace_identifier); PyStructSequence_SET_ITEM(rule_string, 1, rule_identifier); PyStructSequence_SET_ITEM(rule_string, 2, string_identifier); // PyStructSequenece steals the reference so we NULL these // so that Py_XDECREF() can be used in _exit label namespace_identifier = NULL; rule_identifier = NULL; string_identifier = NULL; warning_type = PyLong_FromLong(CALLBACK_MSG_TOO_MANY_MATCHES); if (warning_type == NULL) { result = CALLBACK_ERROR; goto _exit; } PyObject* callback_result = PyObject_CallFunctionObjArgs( data->warnings_callback, warning_type, rule_string, NULL); if (callback_result != NULL) { #if PY_MAJOR_VERSION >= 3 if (PyLong_Check(callback_result)) #else if (PyLong_Check(callback_result) || PyInt_Check(callback_result)) #endif { result = (int) PyLong_AsLong(callback_result); } } else { result = CALLBACK_ERROR; } Py_XDECREF(callback_result); } _exit: Py_XDECREF(namespace_identifier); Py_XDECREF(rule_identifier); Py_XDECREF(string_identifier); Py_XDECREF(rule_string); Py_XDECREF(warning_type); Py_XDECREF(data->warnings_callback); PyGILState_Release(gil_state); return result; } #define CALLBACK_MATCHES 0x01 #define CALLBACK_NON_MATCHES 0x02 #define CALLBACK_ALL CALLBACK_MATCHES | CALLBACK_NON_MATCHES int yara_callback( YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) { YR_STRING* string; YR_MATCH* m; YR_META* meta; YR_RULE* rule; const char* tag; PyObject* tag_list = NULL; PyObject* string_instance_list = NULL; PyObject* string_list = NULL; PyObject* meta_list = NULL; PyObject* string_match_instance = NULL; PyObject* match; PyObject* callback_dict; PyObject* object; PyObject* matches = ((CALLBACK_DATA*) user_data)->matches; PyObject* callback = ((CALLBACK_DATA*) user_data)->callback; PyObject* callback_result; int which = ((CALLBACK_DATA*) user_data)->which; switch(message) { case CALLBACK_MSG_IMPORT_MODULE: return handle_import_module(message_data, user_data); case CALLBACK_MSG_MODULE_IMPORTED: return handle_module_imported(message_data, user_data); case CALLBACK_MSG_TOO_MANY_MATCHES: return handle_too_many_matches(context, message_data, user_data); case CALLBACK_MSG_SCAN_FINISHED: return CALLBACK_CONTINUE; case CALLBACK_MSG_RULE_NOT_MATCHING: // In cases where the rule doesn't match and the user didn't provided a // callback function or is not interested in getting notified about // non-matches, there's nothing more do to here, keep executing the function // if otherwise. if (callback == NULL || (which & CALLBACK_NON_MATCHES) != CALLBACK_NON_MATCHES) return CALLBACK_CONTINUE; break; case CALLBACK_MSG_CONSOLE_LOG: return handle_console_log(message_data, user_data); } // At this point we have handled all the other cases of when this callback // can be called. The only things left are: // // 1. A matching rule. // // 2 A non-matching rule and the user has requested to see non-matching rules. // // In both cases, we need to create the data that will be either passed back // to the python callback or stored in the matches list. int result = CALLBACK_CONTINUE; rule = (YR_RULE*) message_data; PyGILState_STATE gil_state = PyGILState_Ensure(); tag_list = PyList_New(0); string_list = PyList_New(0); meta_list = PyDict_New(); if (tag_list == NULL || string_list == NULL || meta_list == NULL) { Py_XDECREF(tag_list); Py_XDECREF(string_list); Py_XDECREF(meta_list); PyGILState_Release(gil_state); return CALLBACK_ERROR; } yr_rule_tags_foreach(rule, tag) { object = PY_STRING(tag); PyList_Append(tag_list, object); Py_DECREF(object); } yr_rule_metas_foreach(rule, meta) { if (meta->type == META_TYPE_INTEGER) object = Py_BuildValue("i", meta->integer); else if (meta->type == META_TYPE_BOOLEAN) object = PyBool_FromLong((long) meta->integer); else object = PY_STRING(meta->string); if (((CALLBACK_DATA*) user_data)->allow_duplicate_metadata){ // Check if we already have an array under this key PyObject* existing_item = PyDict_GetItemString(meta_list, meta->identifier); // Append object to existing list if (existing_item) PyList_Append(existing_item, object); else{ //Otherwise, instantiate array and append object as first item PyObject* new_list = PyList_New(0); PyList_Append(new_list, object); PyDict_SetItemString(meta_list, meta->identifier, new_list); Py_DECREF(new_list); } } else{ PyDict_SetItemString(meta_list, meta->identifier, object); Py_DECREF(object); } } yr_rule_strings_foreach(rule, string) { // If this string is not a match, skip it. We have to check for this here // and not rely on it in yr_string_matches_foreach macro because we need // to create the string match instance list before we make the items that // go in it. if (context->matches[string->idx].head == NULL) continue; string_instance_list = PyList_New(0); if (string_instance_list == NULL) { PyErr_Format(PyExc_TypeError, "out of memory"); return CALLBACK_ERROR; } yr_string_matches_foreach(context, string, m) { object = PyBytes_FromStringAndSize((char*) m->data, m->data_length); string_match_instance = StringMatchInstance_NEW( m->base + m->offset, object, m->match_length, m->xor_key); if (string_match_instance == NULL) { Py_DECREF(object); PyErr_Format(PyExc_TypeError, "out of memory"); return CALLBACK_ERROR; } PyList_Append(string_instance_list, string_match_instance); Py_DECREF(object); Py_DECREF(string_match_instance); } object = StringMatch_NEW( string->identifier, string->flags, string_instance_list); if (object == NULL) { PyErr_Format(PyExc_TypeError, "out of memory"); return CALLBACK_ERROR; } Py_DECREF(string_instance_list); PyList_Append(string_list, object); Py_DECREF(object); } if (message == CALLBACK_MSG_RULE_MATCHING) { match = Match_NEW( rule->identifier, rule->ns->name, tag_list, meta_list, string_list); if (match != NULL) { PyList_Append(matches, match); Py_DECREF(match); } else { Py_DECREF(tag_list); Py_DECREF(string_list); Py_DECREF(meta_list); PyGILState_Release(gil_state); return CALLBACK_ERROR; } } if (callback != NULL && ((message == CALLBACK_MSG_RULE_MATCHING && (which & CALLBACK_MATCHES)) || (message == CALLBACK_MSG_RULE_NOT_MATCHING && (which & CALLBACK_NON_MATCHES)))) { Py_INCREF(callback); callback_dict = PyDict_New(); object = PyBool_FromLong(message == CALLBACK_MSG_RULE_MATCHING); PyDict_SetItemString(callback_dict, "matches", object); Py_DECREF(object); object = PY_STRING(rule->identifier); PyDict_SetItemString(callback_dict, "rule", object); Py_DECREF(object); object = PY_STRING(rule->ns->name); PyDict_SetItemString(callback_dict, "namespace", object); Py_DECREF(object); PyDict_SetItemString(callback_dict, "tags", tag_list); PyDict_SetItemString(callback_dict, "meta", meta_list); PyDict_SetItemString(callback_dict, "strings", string_list); callback_result = PyObject_CallFunctionObjArgs( callback, callback_dict, NULL); if (callback_result != NULL) { #if PY_MAJOR_VERSION >= 3 if (PyLong_Check(callback_result)) #else if (PyLong_Check(callback_result) || PyInt_Check(callback_result)) #endif { result = (int) PyLong_AsLong(callback_result); } Py_DECREF(callback_result); } else { result = CALLBACK_ERROR; } Py_DECREF(callback_dict); Py_DECREF(callback); } Py_DECREF(tag_list); Py_DECREF(string_list); Py_DECREF(meta_list); PyGILState_Release(gil_state); return result; } /* YR_STREAM read method for "file-like objects" */ static size_t flo_read( void* ptr, size_t size, size_t count, void* user_data) { size_t i; for (i = 0; i < count; i++) { PyGILState_STATE gil_state = PyGILState_Ensure(); PyObject* bytes = PyObject_CallMethod( (PyObject*) user_data, "read", "n", (Py_ssize_t) size); if (bytes == NULL) { PyGILState_Release(gil_state); return i; } Py_ssize_t len; char* buffer; int result = PyBytes_AsStringAndSize(bytes, &buffer, &len); if (result == -1 || (size_t) len < size) { Py_DECREF(bytes); PyGILState_Release(gil_state); return i; } memcpy((char*) ptr + i * size, buffer, size); Py_DECREF(bytes); PyGILState_Release(gil_state); } return count; } /* YR_STREAM write method for "file-like objects" */ static size_t flo_write( const void* ptr, size_t size, size_t count, void* user_data) { size_t i; for (i = 0; i < count; i++) { PyGILState_STATE gil_state = PyGILState_Ensure(); PyObject* result = PyObject_CallMethod( #if PY_MAJOR_VERSION >= 3 (PyObject*) user_data, "write", "y#", (char*) ptr + i * size, size); #else (PyObject*) user_data, "write", "s#", (char*) ptr + i * size, size); #endif Py_XDECREF(result); PyGILState_Release(gil_state); if (result == NULL) return i; } return count; } PyObject* handle_error( int error, char* extra) { switch(error) { case ERROR_COULD_NOT_ATTACH_TO_PROCESS: return PyErr_Format( YaraError, "access denied"); case ERROR_INSUFFICIENT_MEMORY: return PyErr_NoMemory(); case ERROR_COULD_NOT_OPEN_FILE: return PyErr_Format( YaraError, "could not open file \"%s\"", extra); case ERROR_COULD_NOT_MAP_FILE: return PyErr_Format( YaraError, "could not map file \"%s\" into memory", extra); case ERROR_INVALID_FILE: return PyErr_Format( YaraError, "invalid rules file \"%s\"", extra); case ERROR_CORRUPT_FILE: return PyErr_Format( YaraError, "corrupt rules file \"%s\"", extra); case ERROR_SCAN_TIMEOUT: return PyErr_Format( YaraTimeoutError, "scanning timed out"); case ERROR_INVALID_EXTERNAL_VARIABLE_TYPE: return PyErr_Format( YaraError, "external variable \"%s\" was already defined with a different type", extra); case ERROR_UNSUPPORTED_FILE_VERSION: return PyErr_Format( YaraError, "rules file \"%s\" is incompatible with this version of YARA", extra); default: return PyErr_Format( YaraError, "internal error: %d", error); } } int process_compile_externals( PyObject* externals, YR_COMPILER* compiler) { PyObject* key; PyObject* value; Py_ssize_t pos = 0; char* identifier = NULL; int result; while (PyDict_Next(externals, &pos, &key, &value)) { if (!PY_STRING_CHECK(key)) { PyErr_Format( PyExc_TypeError, "keys of externals dict must be strings"); return ERROR_INVALID_ARGUMENT; } identifier = PY_STRING_TO_C(key); if (PyBool_Check(value)) { result = yr_compiler_define_boolean_variable( compiler, identifier, PyObject_IsTrue(value)); } #if PY_MAJOR_VERSION >= 3 else if (PyLong_Check(value)) #else else if (PyLong_Check(value) || PyInt_Check(value)) #endif { result = yr_compiler_define_integer_variable( compiler, identifier, PyLong_AsLongLong(value)); } else if (PyFloat_Check(value)) { result = yr_compiler_define_float_variable( compiler, identifier, PyFloat_AsDouble(value)); } else if (PY_STRING_CHECK(value)) { char* str = PY_STRING_TO_C(value); if (str == NULL) return ERROR_INVALID_ARGUMENT; result = yr_compiler_define_string_variable( compiler, identifier, str); } else { PyErr_Format( PyExc_TypeError, "external values must be of type integer, float, boolean or string"); return ERROR_INVALID_ARGUMENT; } if (result != ERROR_SUCCESS) { handle_error(result, identifier); return result; } } return ERROR_SUCCESS; } int process_match_externals( PyObject* externals, YR_SCANNER* scanner) { PyObject* key; PyObject* value; Py_ssize_t pos = 0; char* identifier = NULL; int result; while (PyDict_Next(externals, &pos, &key, &value)) { if (!PY_STRING_CHECK(key)) { PyErr_Format( PyExc_TypeError, "keys of externals dict must be strings"); return ERROR_INVALID_ARGUMENT; } identifier = PY_STRING_TO_C(key); if (PyBool_Check(value)) { result = yr_scanner_define_boolean_variable( scanner, identifier, PyObject_IsTrue(value)); } #if PY_MAJOR_VERSION >= 3 else if (PyLong_Check(value)) #else else if (PyLong_Check(value) || PyInt_Check(value)) #endif { result = yr_scanner_define_integer_variable( scanner, identifier, PyLong_AsLongLong(value)); } else if (PyFloat_Check(value)) { result = yr_scanner_define_float_variable( scanner, identifier, PyFloat_AsDouble(value)); } else if (PY_STRING_CHECK(value)) { char* str = PY_STRING_TO_C(value); if (str == NULL) return ERROR_INVALID_ARGUMENT; result = yr_scanner_define_string_variable( scanner, identifier, str); } else { PyErr_Format( PyExc_TypeError, "external values must be of type integer, float, boolean or string"); return ERROR_INVALID_ARGUMENT; } // yr_scanner_define_xxx_variable returns ERROR_INVALID_ARGUMENT if the // variable wasn't previously defined in the compilation phase. Ignore // those errors because we don't want the "scan" method being aborted // because of the "externals" dictionary having more keys than those used // during compilation. if (result != ERROR_SUCCESS && result != ERROR_INVALID_ARGUMENT) { handle_error(result, identifier); return result; } } return ERROR_SUCCESS; } static PyObject* Match_NEW( const char* rule, const char* ns, PyObject* tags, PyObject* meta, PyObject* strings) { Match* object = PyObject_NEW(Match, &Match_Type); if (object != NULL) { object->rule = PY_STRING(rule); object->ns = PY_STRING(ns); object->tags = tags; object->meta = meta; object->strings = strings; Py_INCREF(tags); Py_INCREF(meta); Py_INCREF(strings); } return (PyObject*) object; } static void Match_dealloc( PyObject* self) { Match* object = (Match*) self; Py_DECREF(object->rule); Py_DECREF(object->ns); Py_DECREF(object->tags); Py_DECREF(object->meta); Py_DECREF(object->strings); PyObject_Del(self); } static PyObject* Match_repr( PyObject* self) { Match* object = (Match*) self; Py_INCREF(object->rule); return object->rule; } static PyObject* Match_getattro( PyObject* self, PyObject* name) { return PyObject_GenericGetAttr(self, name); } static PyObject* Match_richcompare( PyObject* self, PyObject* other, int op) { PyObject* result = NULL; Match* a = (Match*) self; Match* b = (Match*) other; if(PyObject_TypeCheck(other, &Match_Type)) { switch(op) { case Py_EQ: if (PyObject_RichCompareBool(a->rule, b->rule, Py_EQ) && PyObject_RichCompareBool(a->ns, b->ns, Py_EQ)) result = Py_True; else result = Py_False; Py_INCREF(result); break; case Py_NE: if (PyObject_RichCompareBool(a->rule, b->rule, Py_NE) || PyObject_RichCompareBool(a->ns, b->ns, Py_NE)) result = Py_True; else result = Py_False; Py_INCREF(result); break; case Py_LT: case Py_LE: case Py_GT: case Py_GE: if (PyObject_RichCompareBool(a->rule, b->rule, Py_EQ)) result = PyObject_RichCompare(a->ns, b->ns, op); else result = PyObject_RichCompare(a->rule, b->rule, op); break; } } else { result = PyErr_Format( PyExc_TypeError, "'Match' objects must be compared with objects of the same class"); } return result; } static Py_hash_t Match_hash( PyObject* self) { Match* match = (Match*) self; return PyObject_Hash(match->rule) + PyObject_Hash(match->ns); } //////////////////////////////////////////////////////////////////////////////// static PyObject* StringMatch_NEW( const char* identifier, uint64_t flags, PyObject* instance_list) { StringMatch* object = PyObject_NEW(StringMatch, &StringMatch_Type); if (object != NULL) { object->identifier = PY_STRING(identifier); object->flags = flags; object->instances = instance_list; Py_INCREF(instance_list); } return (PyObject*) object; } static void StringMatch_dealloc( PyObject* self) { StringMatch* object = (StringMatch*) self; Py_DECREF(object->identifier); Py_DECREF(object->instances); PyObject_Del(self); } static PyObject* StringMatch_repr( PyObject* self) { StringMatch* object = (StringMatch*) self; Py_INCREF(object->identifier); return object->identifier; } static PyObject* StringMatch_getattro( PyObject* self, PyObject* name) { return PyObject_GenericGetAttr(self, name); } // Hashing on just identifiers can be tricky as there can be duplicate // identifiers between rules and there are anonymous strings too. Be careful // when using this! static Py_hash_t StringMatch_hash( PyObject* self) { return PyObject_Hash(((StringMatch*) self)->identifier); } static PyObject* StringMatch_is_xor( PyObject* self, PyObject* args) { if (((StringMatch*) self)->flags & STRING_FLAGS_XOR) Py_RETURN_TRUE; Py_RETURN_FALSE; } //////////////////////////////////////////////////////////////////////////////// static PyObject* StringMatchInstance_NEW( uint64_t offset, PyObject* matched_data, int32_t match_length, uint8_t xor_key) { StringMatchInstance* object = PyObject_NEW(StringMatchInstance, &StringMatchInstance_Type); if (object != NULL) { object->offset = PyLong_FromLongLong(offset); object->matched_data = matched_data; object->matched_length = PyLong_FromLong(match_length); object->xor_key = PyLong_FromUnsignedLong((uint32_t) xor_key); Py_INCREF(matched_data); } return (PyObject*) object; } static void StringMatchInstance_dealloc( PyObject* self) { StringMatchInstance* object = (StringMatchInstance*) self; Py_DECREF(object->offset); Py_DECREF(object->matched_data); Py_DECREF(object->matched_length); Py_DECREF(object->xor_key); PyObject_Del(self); } static PyObject* StringMatchInstance_repr( PyObject* self) { StringMatchInstance* object = (StringMatchInstance*) self; return PyCodec_Decode(object->matched_data, "utf-8", "backslashreplace"); } static PyObject* StringMatchInstance_getattro( PyObject* self, PyObject* name) { return PyObject_GenericGetAttr(self, name); } static Py_hash_t StringMatchInstance_hash( PyObject* self) { return PyObject_Hash(((StringMatchInstance*) self)->matched_data); } static PyObject* StringMatchInstance_plaintext( PyObject* self, PyObject* args) { char* pb; Py_ssize_t length; StringMatchInstance* instance = (StringMatchInstance*) self; uint64_t xor_key = PyLong_AsUnsignedLongLong(instance->xor_key); if (xor_key == 0) { Py_INCREF(instance->matched_data); return instance->matched_data; } int result = PyBytes_AsStringAndSize(instance->matched_data, &pb, &length); if (result == -1) return NULL; // pb points to an internal buffer of the bytes object which we can not // modify. Allocate a new buffer, copy the contents over and do the xor, then // create a new bytes object to return. uint8_t* buf = (uint8_t*) calloc(length, sizeof(uint8_t)); if (buf == NULL) return PyErr_Format(PyExc_TypeError, "Out of memory"); memcpy(buf, pb, length); for (size_t i = 0; i < length; i++) { buf[i] = ((uint8_t) pb[i]) ^ xor_key; } PyObject* object = PyBytes_FromStringAndSize((char*) buf, length); free(buf); return object; } //////////////////////////////////////////////////////////////////////////////// static void Rule_dealloc( PyObject* self) { Rule* object = (Rule*) self; Py_XDECREF(object->identifier); Py_XDECREF(object->tags); Py_XDECREF(object->meta); Py_XDECREF(object->global); Py_XDECREF(object->private); PyObject_Del(self); } static PyObject* Rule_getattro( PyObject* self, PyObject* name) { return PyObject_GenericGetAttr(self, name); } static Rules* Rules_NEW(void) { Rules* rules = PyObject_NEW(Rules, &Rules_Type); if (rules != NULL) { rules->rules = NULL; rules->externals = NULL; rules->warnings = NULL; } return rules; } static void Rules_dealloc( PyObject* self) { Rules* object = (Rules*) self; Py_XDECREF(object->externals); Py_XDECREF(object->warnings); if (object->rules != NULL) yr_rules_destroy(object->rules); PyObject_Del(self); } static PyObject* Rules_next( PyObject* self) { PyObject* tag_list; PyObject* object; PyObject* meta_list; YR_META* meta; const char* tag; Rule* rule; Rules* rules = (Rules *) self; // Generate new Rule object based upon iter_current_rule and increment // iter_current_rule. if (RULE_IS_NULL(rules->iter_current_rule)) { rules->iter_current_rule = rules->rules->rules_table; PyErr_SetNone(PyExc_StopIteration); return NULL; } rule = PyObject_NEW(Rule, &Rule_Type); tag_list = PyList_New(0); meta_list = PyDict_New(); if (rule != NULL && tag_list != NULL && meta_list != NULL) { yr_rule_tags_foreach(rules->iter_current_rule, tag) { object = PY_STRING(tag); PyList_Append(tag_list, object); Py_DECREF(object); } yr_rule_metas_foreach(rules->iter_current_rule, meta) { if (meta->type == META_TYPE_INTEGER) object = Py_BuildValue("i", meta->integer); else if (meta->type == META_TYPE_BOOLEAN) object = PyBool_FromLong((long) meta->integer); else object = PY_STRING(meta->string); PyDict_SetItemString(meta_list, meta->identifier, object); Py_DECREF(object); } rule->global = PyBool_FromLong(rules->iter_current_rule->flags & RULE_FLAGS_GLOBAL); rule->private = PyBool_FromLong(rules->iter_current_rule->flags & RULE_FLAGS_PRIVATE); rule->identifier = PY_STRING(rules->iter_current_rule->identifier); rule->tags = tag_list; rule->meta = meta_list; rules->iter_current_rule++; return (PyObject*) rule; } else { Py_XDECREF(tag_list); Py_XDECREF(meta_list); return PyErr_Format(PyExc_TypeError, "Out of memory"); } } static PyObject* Rules_match( PyObject* self, PyObject* args, PyObject* keywords) { static char* kwlist[] = { "filepath", "pid", "data", "externals", "callback", "fast", "timeout", "modules_data", "modules_callback", "which_callbacks", "warnings_callback", "console_callback", "allow_duplicate_metadata", NULL }; char* filepath = NULL; Py_buffer data = {0}; int pid = -1; int timeout = 0; int error = ERROR_SUCCESS; PyObject* externals = NULL; PyObject* fast = NULL; Rules* object = (Rules*) self; YR_SCANNER* scanner; CALLBACK_DATA callback_data; callback_data.matches = NULL; callback_data.callback = NULL; callback_data.modules_data = NULL; callback_data.modules_callback = NULL; callback_data.warnings_callback = NULL; callback_data.console_callback = NULL; callback_data.which = CALLBACK_ALL; callback_data.allow_duplicate_metadata = false; if (PyArg_ParseTupleAndKeywords( args, keywords, "|sis*OOOiOOiOOb", kwlist, &filepath, &pid, &data, &externals, &callback_data.callback, &fast, &timeout, &callback_data.modules_data, &callback_data.modules_callback, &callback_data.which, &callback_data.warnings_callback, &callback_data.console_callback, &callback_data.allow_duplicate_metadata)) { if (filepath == NULL && data.buf == NULL && pid == -1) { return PyErr_Format( PyExc_TypeError, "match() takes at least one argument"); } if (callback_data.callback != NULL) { if (!PyCallable_Check(callback_data.callback)) { PyBuffer_Release(&data); return PyErr_Format( PyExc_TypeError, "'callback' must be callable"); } } if (callback_data.modules_callback != NULL) { if (!PyCallable_Check(callback_data.modules_callback)) { PyBuffer_Release(&data); return PyErr_Format( PyExc_TypeError, "'modules_callback' must be callable"); } } if (callback_data.warnings_callback != NULL) { if (!PyCallable_Check(callback_data.warnings_callback)) { PyBuffer_Release(&data); return PyErr_Format( PyExc_TypeError, "'warnings_callback' must be callable"); } } if (callback_data.console_callback != NULL) { if (!PyCallable_Check(callback_data.console_callback)) { PyBuffer_Release(&data); return PyErr_Format( PyExc_TypeError, "'console_callback' must be callable"); } } if (callback_data.modules_data != NULL) { if (!PyDict_Check(callback_data.modules_data)) { PyBuffer_Release(&data); return PyErr_Format( PyExc_TypeError, "'modules_data' must be a dictionary"); } } if (callback_data.allow_duplicate_metadata == NULL) callback_data.allow_duplicate_metadata = false; if (yr_scanner_create(object->rules, &scanner) != 0) { return PyErr_Format( PyExc_Exception, "could not create scanner"); } if (externals != NULL && externals != Py_None) { if (PyDict_Check(externals)) { if (process_match_externals(externals, scanner) != ERROR_SUCCESS) { PyBuffer_Release(&data); yr_scanner_destroy(scanner); return NULL; } } else { PyBuffer_Release(&data); yr_scanner_destroy(scanner); return PyErr_Format( PyExc_TypeError, "'externals' must be a dictionary"); } } if (fast != NULL && PyObject_IsTrue(fast) == 1) { yr_scanner_set_flags(scanner, SCAN_FLAGS_FAST_MODE); } yr_scanner_set_timeout(scanner, timeout); yr_scanner_set_callback(scanner, yara_callback, &callback_data); if (filepath != NULL) { callback_data.matches = PyList_New(0); Py_BEGIN_ALLOW_THREADS error = yr_scanner_scan_file(scanner, filepath); Py_END_ALLOW_THREADS } else if (data.buf != NULL) { callback_data.matches = PyList_New(0); Py_BEGIN_ALLOW_THREADS error = yr_scanner_scan_mem( scanner, (unsigned char*) data.buf, (size_t) data.len); Py_END_ALLOW_THREADS } else if (pid != -1) { callback_data.matches = PyList_New(0); Py_BEGIN_ALLOW_THREADS error = yr_scanner_scan_proc(scanner, pid); Py_END_ALLOW_THREADS } PyBuffer_Release(&data); yr_scanner_destroy(scanner); if (error != ERROR_SUCCESS) { Py_DECREF(callback_data.matches); if (error != ERROR_CALLBACK_ERROR) { if (filepath != NULL) { handle_error(error, filepath); } else if (pid != -1) { handle_error(error, ""); } else { handle_error(error, ""); } #ifdef PROFILING_ENABLED PyObject* exception = PyErr_Occurred(); if (exception != NULL && error == ERROR_SCAN_TIMEOUT) { PyObject_SetAttrString( exception, "profiling_info", Rules_profiling_info(self, NULL)); } #endif } return NULL; } } return callback_data.matches; } static PyObject* Rules_save( PyObject* self, PyObject* args, PyObject* keywords) { static char* kwlist[] = { "filepath", "file", NULL }; char* filepath = NULL; PyObject* file = NULL; Rules* rules = (Rules*) self; int error; if (!PyArg_ParseTupleAndKeywords( args, keywords, "|sO", kwlist, &filepath, &file)) { return NULL; } if (filepath != NULL) { Py_BEGIN_ALLOW_THREADS error = yr_rules_save(rules->rules, filepath); Py_END_ALLOW_THREADS if (error != ERROR_SUCCESS) return handle_error(error, filepath); } else if (file != NULL && PyObject_HasAttrString(file, "write")) { YR_STREAM stream; stream.user_data = file; stream.write = flo_write; Py_BEGIN_ALLOW_THREADS; error = yr_rules_save_stream(rules->rules, &stream); Py_END_ALLOW_THREADS; if (error != ERROR_SUCCESS) return handle_error(error, ""); } else { return PyErr_Format( PyExc_TypeError, "load() expects either a file path or a file-like object"); } Py_RETURN_NONE; } static PyObject* Rules_profiling_info( PyObject* self, PyObject* args) { #ifdef PROFILING_ENABLED PyObject* object; PyObject* result; YR_RULES* rules = ((Rules*) self)->rules; YR_RULE* rule; YR_STRING* string; char key[512]; uint64_t clock_ticks; result = PyDict_New(); yr_rules_foreach(rules, rule) { clock_ticks = rule->clock_ticks; yr_rule_strings_foreach(rule, string) { clock_ticks += string->clock_ticks; } snprintf(key, sizeof(key), "%s:%s", rule->ns->name, rule->identifier); object = PyLong_FromLongLong(clock_ticks); PyDict_SetItemString(result, key, object); Py_DECREF(object); } return result; #else return PyErr_Format(YaraError, "libyara compiled without profiling support"); #endif } static PyObject* Rules_getattro( PyObject* self, PyObject* name) { return PyObject_GenericGetAttr(self, name); } void raise_exception_on_error( int error_level, const char* file_name, int line_number, const YR_RULE* rule, const char* message, void* user_data) { PyGILState_STATE gil_state = PyGILState_Ensure(); if (error_level == YARA_ERROR_LEVEL_ERROR) { if (file_name != NULL) PyErr_Format( YaraSyntaxError, "%s(%d): %s", file_name, line_number, message); else PyErr_Format( YaraSyntaxError, "line %d: %s", line_number, message); } else { PyObject* warnings = (PyObject*)user_data; PyObject* warning_msg; if (file_name != NULL) warning_msg = PY_STRING_FORMAT( "%s(%d): %s", file_name, line_number, message); else warning_msg = PY_STRING_FORMAT( "line %d: %s", line_number, message); PyList_Append(warnings, warning_msg); Py_DECREF(warning_msg); } PyGILState_Release(gil_state); } //////////////////////////////////////////////////////////////////////////////// const char* yara_include_callback( const char* include_name, const char* calling_rule_filename, const char* calling_rule_namespace, void* user_data) { PyObject* result; PyObject* callback = (PyObject*) user_data; PyObject* py_incl_name = NULL; PyObject* py_calling_fn = NULL; PyObject* py_calling_ns = NULL; PyObject* type = NULL; PyObject* value = NULL; PyObject* traceback = NULL; const char* cstring_result = NULL; PyGILState_STATE gil_state = PyGILState_Ensure(); if (include_name != NULL) { py_incl_name = PY_STRING(include_name); } else //safeguard: should never happen for 'include_name' { py_incl_name = Py_None; Py_INCREF(py_incl_name); } if (calling_rule_filename != NULL) { py_calling_fn = PY_STRING(calling_rule_filename); } else { py_calling_fn = Py_None; Py_INCREF(py_calling_fn); } if (calling_rule_namespace != NULL) { py_calling_ns = PY_STRING(calling_rule_namespace); } else { py_calling_ns = Py_None; Py_INCREF(py_calling_ns); } PyErr_Fetch(&type, &value, &traceback); result = PyObject_CallFunctionObjArgs( callback, py_incl_name, py_calling_fn, py_calling_ns, NULL); PyErr_Restore(type, value, traceback); Py_DECREF(py_incl_name); Py_DECREF(py_calling_fn); Py_DECREF(py_calling_ns); if (result != NULL && result != Py_None && PY_STRING_CHECK(result)) { //transferring string ownership to C code cstring_result = strdup(PY_STRING_TO_C(result)); } else { if (PyErr_Occurred() == NULL) { PyErr_Format(PyExc_TypeError, "'include_callback' function must return a yara rules as an ascii " "or unicode string"); } } Py_XDECREF(result); PyGILState_Release(gil_state); return cstring_result; } void yara_include_free( const char* result_ptr, void* user_data) { if (result_ptr != NULL) { free((void*) result_ptr); } } //////////////////////////////////////////////////////////////////////////////// static PyObject* yara_set_config( PyObject* self, PyObject* args, PyObject* keywords) { /* * It is recommended that this be kept up to date with the config * options present in yara/libyara.c yr_set_configuration(...) - ck */ static char *kwlist[] = { "stack_size", "max_strings_per_rule", "max_match_data", NULL}; unsigned int stack_size = 0; unsigned int max_strings_per_rule = 0; unsigned int max_match_data = 0; int error = 0; if (PyArg_ParseTupleAndKeywords( args, keywords, "|III", kwlist, &stack_size, &max_strings_per_rule, &max_match_data)) { if (stack_size != 0) { error = yr_set_configuration( YR_CONFIG_STACK_SIZE, &stack_size); if ( error != ERROR_SUCCESS) return handle_error(error, NULL); } if (max_strings_per_rule != 0) { error = yr_set_configuration( YR_CONFIG_MAX_STRINGS_PER_RULE, &max_strings_per_rule); if (error != ERROR_SUCCESS) return handle_error(error, NULL); } if (max_match_data != 0) { error = yr_set_configuration( YR_CONFIG_MAX_MATCH_DATA, &max_match_data); if (error != ERROR_SUCCESS) return handle_error(error, NULL); } } Py_RETURN_NONE; } static PyObject* yara_compile( PyObject* self, PyObject* args, PyObject* keywords) { static char *kwlist[] = { "filepath", "source", "file", "filepaths", "sources", "includes", "externals", "error_on_warning", "strict_escape", "include_callback", NULL}; YR_COMPILER* compiler; YR_RULES* yara_rules; FILE* fh; Rules* rules; PyObject* key; PyObject* value; PyObject* result = NULL; PyObject* file = NULL; PyObject* sources_dict = NULL; PyObject* filepaths_dict = NULL; PyObject* includes = NULL; PyObject* externals = NULL; PyObject* error_on_warning = NULL; PyObject* strict_escape = NULL; PyObject* include_callback = NULL; Py_ssize_t pos = 0; int fd; int error = 0; char* filepath = NULL; char* source = NULL; char* ns = NULL; PyObject* warnings = PyList_New(0); bool warning_error = false; if (PyArg_ParseTupleAndKeywords( args, keywords, "|ssOOOOOOOO", kwlist, &filepath, &source, &file, &filepaths_dict, &sources_dict, &includes, &externals, &error_on_warning, &strict_escape, &include_callback)) { char num_args = 0; if (filepath != NULL) num_args++; if (source != NULL) num_args++; if (file != NULL) num_args++; if (filepaths_dict != NULL) num_args++; if (sources_dict != NULL) num_args++; if (num_args > 1) return PyErr_Format( PyExc_TypeError, "compile is receiving too many arguments"); error = yr_compiler_create(&compiler); if (error != ERROR_SUCCESS) return handle_error(error, NULL); yr_compiler_set_callback(compiler, raise_exception_on_error, warnings); if (error_on_warning != NULL) { if (PyBool_Check(error_on_warning)) { if (PyObject_IsTrue(error_on_warning) == 1) { warning_error = true; } } else { yr_compiler_destroy(compiler); return PyErr_Format( PyExc_TypeError, "'error_on_warning' param must be of boolean type"); } } if (strict_escape != NULL) { if (PyBool_Check(strict_escape)) { compiler->strict_escape = PyObject_IsTrue(strict_escape); } else { yr_compiler_destroy(compiler); return PyErr_Format( PyExc_TypeError, "'strict_escape' param must be of boolean type"); } } if (includes != NULL) { if (PyBool_Check(includes)) { // PyObject_IsTrue can return -1 in case of error if (PyObject_IsTrue(includes) == 0) yr_compiler_set_include_callback(compiler, NULL, NULL, NULL); } else { yr_compiler_destroy(compiler); return PyErr_Format( PyExc_TypeError, "'includes' param must be of boolean type"); } } if (include_callback != NULL) { if (!PyCallable_Check(include_callback)) { yr_compiler_destroy(compiler); return PyErr_Format( PyExc_TypeError, "'include_callback' must be callable"); } yr_compiler_set_include_callback( compiler, yara_include_callback, yara_include_free, include_callback); } if (externals != NULL && externals != Py_None) { if (PyDict_Check(externals)) { if (process_compile_externals(externals, compiler) != ERROR_SUCCESS) { yr_compiler_destroy(compiler); return NULL; } } else { yr_compiler_destroy(compiler); return PyErr_Format( PyExc_TypeError, "'externals' must be a dictionary"); } } Py_XINCREF(include_callback); if (filepath != NULL) { fh = fopen(filepath, "r"); if (fh != NULL) { Py_BEGIN_ALLOW_THREADS error = yr_compiler_add_file(compiler, fh, NULL, filepath); fclose(fh); Py_END_ALLOW_THREADS } else { result = PyErr_SetFromErrno(YaraError); } } else if (source != NULL) { Py_BEGIN_ALLOW_THREADS error = yr_compiler_add_string(compiler, source, NULL); Py_END_ALLOW_THREADS } else if (file != NULL) { fd = PyObject_AsFileDescriptor(file); if (fd != -1) { Py_BEGIN_ALLOW_THREADS fh = fdopen(dup(fd), "r"); error = yr_compiler_add_file(compiler, fh, NULL, NULL); fclose(fh); Py_END_ALLOW_THREADS } else { result = PyErr_Format( PyExc_TypeError, "'file' is not a file object"); } } else if (sources_dict != NULL) { if (PyDict_Check(sources_dict)) { while (PyDict_Next(sources_dict, &pos, &key, &value)) { source = PY_STRING_TO_C(value); ns = PY_STRING_TO_C(key); if (source != NULL && ns != NULL) { Py_BEGIN_ALLOW_THREADS error = yr_compiler_add_string(compiler, source, ns); Py_END_ALLOW_THREADS if (error > 0) break; } else { result = PyErr_Format( PyExc_TypeError, "keys and values of the 'sources' dictionary must be " "of string type"); break; } } } else { result = PyErr_Format( PyExc_TypeError, "'sources' must be a dictionary"); } } else if (filepaths_dict != NULL) { if (PyDict_Check(filepaths_dict)) { while (PyDict_Next(filepaths_dict, &pos, &key, &value)) { filepath = PY_STRING_TO_C(value); ns = PY_STRING_TO_C(key); if (filepath != NULL && ns != NULL) { fh = fopen(filepath, "r"); if (fh != NULL) { Py_BEGIN_ALLOW_THREADS error = yr_compiler_add_file(compiler, fh, ns, filepath); fclose(fh); Py_END_ALLOW_THREADS if (error > 0) break; } else { result = PyErr_SetFromErrno(YaraError); break; } } else { result = PyErr_Format( PyExc_TypeError, "keys and values of the filepaths dictionary must be of " "string type"); break; } } } else { result = PyErr_Format( PyExc_TypeError, "filepaths must be a dictionary"); } } else { result = PyErr_Format( PyExc_TypeError, "compile() takes 1 argument"); } if (warning_error && PyList_Size(warnings) > 0) { PyErr_SetObject(YaraWarningError, warnings); } if (PyErr_Occurred() == NULL) { rules = Rules_NEW(); if (rules != NULL) { Py_BEGIN_ALLOW_THREADS error = yr_compiler_get_rules(compiler, &yara_rules); Py_END_ALLOW_THREADS if (error == ERROR_SUCCESS) { rules->rules = yara_rules; rules->iter_current_rule = rules->rules->rules_table; rules->warnings = warnings; if (externals != NULL && externals != Py_None) rules->externals = PyDict_Copy(externals); result = (PyObject*) rules; } else { Py_DECREF(rules); result = handle_error(error, NULL); } } else { result = handle_error(ERROR_INSUFFICIENT_MEMORY, NULL); } } yr_compiler_destroy(compiler); Py_XDECREF(include_callback); } return result; } static PyObject* yara_load( PyObject* self, PyObject* args, PyObject* keywords) { static char* kwlist[] = { "filepath", "file", NULL }; YR_EXTERNAL_VARIABLE* external; Rules* rules = NULL; PyObject* file = NULL; char* filepath = NULL; int error; if (!PyArg_ParseTupleAndKeywords( args, keywords, "|sO", kwlist, &filepath, &file)) { return NULL; } if (filepath != NULL) { rules = Rules_NEW(); if (rules == NULL) return PyErr_NoMemory(); Py_BEGIN_ALLOW_THREADS; error = yr_rules_load(filepath, &rules->rules); Py_END_ALLOW_THREADS; if (error != ERROR_SUCCESS) { Py_DECREF(rules); return handle_error(error, filepath); } } else if (file != NULL && PyObject_HasAttrString(file, "read")) { YR_STREAM stream; stream.user_data = file; stream.read = flo_read; rules = Rules_NEW(); if (rules == NULL) return PyErr_NoMemory(); Py_BEGIN_ALLOW_THREADS; error = yr_rules_load_stream(&stream, &rules->rules); Py_END_ALLOW_THREADS; if (error != ERROR_SUCCESS) { Py_DECREF(rules); return handle_error(error, ""); } } else { return PyErr_Format( PyExc_TypeError, "load() expects either a file path or a file-like object"); } external = rules->rules->ext_vars_table; rules->iter_current_rule = rules->rules->rules_table; if (!EXTERNAL_VARIABLE_IS_NULL(external)) rules->externals = PyDict_New(); while (!EXTERNAL_VARIABLE_IS_NULL(external)) { switch(external->type) { case EXTERNAL_VARIABLE_TYPE_BOOLEAN: PyDict_SetItemString( rules->externals, external->identifier, PyBool_FromLong((long) external->value.i)); break; case EXTERNAL_VARIABLE_TYPE_INTEGER: PyDict_SetItemString( rules->externals, external->identifier, PyLong_FromLong((long) external->value.i)); break; case EXTERNAL_VARIABLE_TYPE_FLOAT: PyDict_SetItemString( rules->externals, external->identifier, PyFloat_FromDouble(external->value.f)); break; case EXTERNAL_VARIABLE_TYPE_STRING: PyDict_SetItemString( rules->externals, external->identifier, PY_STRING(external->value.s)); break; } external++; } return (PyObject*) rules; } void finalize(void) { yr_finalize(); } static PyMethodDef yara_methods[] = { { "compile", (PyCFunction) yara_compile, METH_VARARGS | METH_KEYWORDS, "Compiles a YARA rules file and returns an instance of class Rules" }, { "load", (PyCFunction) yara_load, METH_VARARGS | METH_KEYWORDS, "Loads a previously saved YARA rules file and returns an instance of class Rules" }, { "set_config", (PyCFunction) yara_set_config, METH_VARARGS | METH_KEYWORDS, "Set a yara configuration variable (stack_size, max_strings_per_rule, or max_match_data)" }, { NULL, NULL } }; #if PY_MAJOR_VERSION >= 3 #define MOD_ERROR_VAL NULL #define MOD_SUCCESS_VAL(val) val #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) #define MOD_DEF(ob, name, doc, methods) \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ ob = PyModule_Create(&moduledef); #else #define MOD_ERROR_VAL #define MOD_SUCCESS_VAL(val) #define MOD_INIT(name) void init##name(void) #define MOD_DEF(ob, name, doc, methods) \ ob = Py_InitModule3(name, methods, doc); #endif static PyObject* YaraWarningError_getwarnings(PyObject *self, void* closure) { PyObject *args = PyObject_GetAttrString(self, "args"); if (!args) { return NULL; } PyObject* ret = PyTuple_GetItem(args, 0); Py_XINCREF(ret); Py_XDECREF(args); return ret; } static PyGetSetDef YaraWarningError_getsetters[] = { {"warnings", YaraWarningError_getwarnings, NULL, NULL, NULL}, {NULL} }; MOD_INIT(yara) { PyObject* m; MOD_DEF(m, "yara", YARA_DOC, yara_methods) if (m == NULL) return MOD_ERROR_VAL; /* initialize module variables/constants */ PyModule_AddIntConstant(m, "CALLBACK_CONTINUE", 0); PyModule_AddIntConstant(m, "CALLBACK_ABORT", 1); PyModule_AddIntConstant(m, "CALLBACK_MATCHES", CALLBACK_MATCHES); PyModule_AddIntConstant(m, "CALLBACK_NON_MATCHES", CALLBACK_NON_MATCHES); PyModule_AddIntConstant(m, "CALLBACK_ALL", CALLBACK_ALL); PyModule_AddIntConstant(m, "CALLBACK_TOO_MANY_MATCHES", CALLBACK_MSG_TOO_MANY_MATCHES); PyModule_AddStringConstant(m, "__version__", YR_VERSION); PyModule_AddStringConstant(m, "YARA_VERSION", YR_VERSION); PyModule_AddIntConstant(m, "YARA_VERSION_HEX", YR_VERSION_HEX); #if PYTHON_API_VERSION >= 1007 YaraError = PyErr_NewException("yara.Error", PyExc_Exception, NULL); YaraSyntaxError = PyErr_NewException("yara.SyntaxError", YaraError, NULL); YaraTimeoutError = PyErr_NewException("yara.TimeoutError", YaraError, NULL); YaraWarningError = PyErr_NewException("yara.WarningError", YaraError, NULL); PyTypeObject *YaraWarningError_type = (PyTypeObject *) YaraWarningError; PyObject* descr = PyDescr_NewGetSet(YaraWarningError_type, YaraWarningError_getsetters); if (PyDict_SetItem(YaraWarningError_type->tp_dict, PyDescr_NAME(descr), descr) < 0) { Py_DECREF(m); Py_DECREF(descr); } Py_DECREF(descr); #else YaraError = Py_BuildValue("s", "yara.Error"); YaraSyntaxError = Py_BuildValue("s", "yara.SyntaxError"); YaraTimeoutError = Py_BuildValue("s", "yara.TimeoutError"); YaraWarningError = Py_BuildValue("s", "yara.WarningError"); #endif if (PyType_Ready(&Rule_Type) < 0) return MOD_ERROR_VAL; if (PyType_Ready(&Rules_Type) < 0) return MOD_ERROR_VAL; if (PyType_Ready(&Match_Type) < 0) return MOD_ERROR_VAL; if (PyType_Ready(&StringMatch_Type) < 0) return MOD_ERROR_VAL; if (PyType_Ready(&StringMatchInstance_Type) < 0) return MOD_ERROR_VAL; PyStructSequence_InitType(&RuleString_Type, &RuleString_Desc); PyModule_AddObject(m, "Rule", (PyObject*) &Rule_Type); PyModule_AddObject(m, "Rules", (PyObject*) &Rules_Type); PyModule_AddObject(m, "Match", (PyObject*) &Match_Type); PyModule_AddObject(m, "StringMatch", (PyObject*) &StringMatch_Type); PyModule_AddObject(m, "StringMatchInstance", (PyObject*) &StringMatchInstance_Type); PyModule_AddObject(m, "Error", YaraError); PyModule_AddObject(m, "SyntaxError", YaraSyntaxError); PyModule_AddObject(m, "TimeoutError", YaraTimeoutError); PyModule_AddObject(m, "WarningError", YaraWarningError); if (yr_initialize() != ERROR_SUCCESS) { PyErr_SetString(YaraError, "initialization error"); return MOD_ERROR_VAL; } PyObject* module_names_list = PyList_New(0); if (module_names_list == NULL) { PyErr_SetString(YaraError, "module list error"); return MOD_ERROR_VAL; } for (YR_MODULE* module = yr_modules_get_table(); module->name != NULL; module++) { PyObject* module_name = PY_STRING(module->name); if (module_name == NULL) { PyErr_SetString(YaraError, "module name error"); return MOD_ERROR_VAL; } if (PyList_Append(module_names_list, module_name) < 0) { PyErr_SetString(YaraError, "module name error"); return MOD_ERROR_VAL; } } PyModule_AddObject(m, "modules", module_names_list); Py_AtExit(finalize); return MOD_SUCCESS_VAL(m); }