pax_global_header00006660000000000000000000000064135476216110014520gustar00rootroot0000000000000052 comment=188cb6e85137f715fef563f61c6b4f21ad026562 yara-python-3.11.0/000077500000000000000000000000001354762161100140555ustar00rootroot00000000000000yara-python-3.11.0/.gitmodules000066400000000000000000000001131354762161100162250ustar00rootroot00000000000000[submodule "yara"] path = yara url=https://github.com/VirusTotal/yara.git yara-python-3.11.0/.travis.yml000066400000000000000000000005521354762161100161700ustar00rootroot00000000000000language: python python: - "2.7" - "3.4" - "3.5" - "3.6" - "3.7" matrix: include: - python: "3.7" dist: xenial sudo: true before_install: - sudo apt-get -qq update - sudo apt-get install -y libmagic-dev install: - python setup.py build --enable-magic --enable-dotnet - python setup.py install script: - python tests.py yara-python-3.11.0/LICENSE000066400000000000000000000261361354762161100150720ustar00rootroot00000000000000 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-3.11.0/MANIFEST.in000066400000000000000000000001441354762161100156120ustar00rootroot00000000000000include *.c include yara/libyara/modules/module_list recursive-include yara *.c *.h include LICENSE yara-python-3.11.0/README.rst000066400000000000000000000037271354762161100155550ustar00rootroot00000000000000.. image:: https://travis-ci.org/VirusTotal/yara-python.svg :target: https://travis-ci.org/VirusTotal/yara-python .. 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) [(10L, '$a', 'lmn')] Installation ------------ The easiest way of installing 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 $ sudo python setup.py install --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-3.11.0/appveyor.yml000077500000000000000000000163731354762161100164620ustar00rootroot00000000000000environment: 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.10" matrix: # Pre-installed Python versions, which Appveyor may upgrade to # a later point release. # See: http://www.appveyor.com/docs/installed-software#python - PYTHON: "C:\\Python27" PYTHON_VERSION: "2.7.x" # currently 2.7.16 PYTHON_ARCH: "32" OPENSSL_LIB: "openssl-1.1.0e-vs2008" VS: "Visual Studio 9 2008" - PYTHON: "C:\\Python27-x64" PYTHON_VERSION: "2.7.x" # currently 2.7.16 PYTHON_ARCH: "64" OPENSSL_LIB: "openssl-1.1.0e-vs2008" VS: "Visual Studio 9 2008 Win64" - PYTHON: "C:\\Python33" PYTHON_VERSION: "3.3.x" # currently 3.3.5 PYTHON_ARCH: "32" OPENSSL_LIB: "openssl-1.1.0e-vs2010" VS: "Visual Studio 10 2010" - PYTHON: "C:\\Python33-x64" PYTHON_VERSION: "3.3.x" # currently 3.3.5 PYTHON_ARCH: "64" OPENSSL_LIB: "openssl-1.1.0e-vs2010" VS: "Visual Studio 10 2010 Win64" - PYTHON: "C:\\Python34" PYTHON_VERSION: "3.4.x" # currently 3.4.4 PYTHON_ARCH: "32" OPENSSL_LIB: "openssl-1.1.0e-vs2010" VS: "Visual Studio 10 2010" - PYTHON: "C:\\Python34-x64" PYTHON_VERSION: "3.4.x" # currently 3.4.4 PYTHON_ARCH: "64" OPENSSL_LIB: "openssl-1.1.0e-vs2010" VS: "Visual Studio 10 2010 Win64" - PYTHON: "C:\\Python35" PYTHON_VERSION: "3.5.x" # currently 3.5.4 PYTHON_ARCH: "32" OPENSSL_LIB: "openssl-1.1.0e-vs2015" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python35-x64" PYTHON_VERSION: "3.5.x" # currently 3.5.4 PYTHON_ARCH: "64" OPENSSL_LIB: "openssl-1.1.0e-vs2015" VS: "Visual Studio 14 2015 Win64" - PYTHON: "C:\\Python36" PYTHON_VERSION: "3.6.x" # currently 3.6.8 PYTHON_ARCH: "32" OPENSSL_LIB: "openssl-1.1.0e-vs2015" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python36-x64" PYTHON_VERSION: "3.6.x" # currently 3.6.8 PYTHON_ARCH: "64" OPENSSL_LIB: "openssl-1.1.0e-vs2015" VS: "Visual Studio 14 2015 Win64" - PYTHON: "C:\\Python37" PYTHON_VERSION: "3.7.x" # currently 3.7.0 PYTHON_ARCH: "32" OPENSSL_LIB: "openssl-1.1.0e-vs2015" VS: "Visual Studio 14 2015" - PYTHON: "C:\\Python37-x64" PYTHON_VERSION: "3.7.x" # currently 3.7.0 PYTHON_ARCH: "64" OPENSSL_LIB: "openssl-1.1.0e-vs2015" 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. # wheel version 0.29.0 is enforced because version 0.30.0 doesn't support # Python 2.6 anymore. Once we drop support for Python 2.6 we can use the # latest version of wheel. - "%CMD_IN_ENV% pip install wheel==0.29.0" - cd .. - ps: Invoke-WebRequest "https://www.npcglib.org/~stathis/downloads/$env:OPENSSL_LIB.7z" -OutFile "openssl.7z" - 7z x openssl.7z - cd yara-python - ps: >- If ($env:PYTHON_ARCH -Match "32") { $env:OPENSSL_LIB_DIR="lib" } Else { $env:OPENSSL_LIB_DIR="lib64" } # This is workaround for solving an issue caused by CMake not finding an # appropriate compilet for Visual Studio 9 2008 Win64. This workaround was # copied from: https://github.com/conda/conda-build/blob/master/appveyor.yml - call appveyor\setup_x64.bat # Download and build jansson library - cd .. - 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 - 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 checkout -qf %APPVEYOR_REPO_COMMIT% build_script: # Build the compiled extension - "%CMD_IN_ENV% python setup.py build_ext --enable-cuckoo --enable-dotnet -L../jansson-%JANSSON_VERSION%/build/lib/Release;../%OPENSSL_LIB%/%OPENSSL_LIB_DIR% -I../jansson-%JANSSON_VERSION%/build/include;../%OPENSSL_LIB%/include -DHASH_MODULE,HAVE_LIBCRYPTO -llibcryptoMT" after_build: - "%CMD_IN_ENV% python setup.py install" test_script: # Run the project tests - "%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" - "%CMD_IN_ENV% python setup.py bdist_wininst" - "%CMD_IN_ENV% python setup.py bdist_msi" 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-3.11.0/appveyor/000077500000000000000000000000001354762161100157225ustar00rootroot00000000000000yara-python-3.11.0/appveyor/install.ps1000077500000000000000000000160331354762161100200230ustar00rootroot00000000000000# 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/ $MINICONDA_URL = "http://repo.continuum.io/miniconda/" $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 DownloadMiniconda ($python_version, $platform_suffix) { if ($python_version -eq "3.4") { $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe" } else { $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe" } $url = $MINICONDA_URL + $filename $filepath = Download $filename $url return $filepath } function InstallMiniconda ($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 = "x86" } else { $platform_suffix = "x86_64" } $filepath = DownloadMiniconda $python_version $platform_suffix Write-Host "Installing" $filepath "to" $python_home $install_log = $python_home + ".log" $args = "/S /D=$python_home" Write-Host $filepath $args Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru 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 InstallMinicondaPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $conda_path = $python_home + "\Scripts\conda.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $args = "install --yes pip" Write-Host $conda_path $args Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru } else { Write-Host "pip already installed." } } function main () { InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON InstallPip $env:PYTHON } main yara-python-3.11.0/appveyor/run_with_env.cmd000077500000000000000000000064461354762161100211330ustar00rootroot00000000000000:: 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-3.11.0/appveyor/setup_x64.bat000077500000000000000000000020461354762161100202600ustar00rootroot00000000000000regedit /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-3.11.0/appveyor/x64/000077500000000000000000000000001354762161100163435ustar00rootroot00000000000000yara-python-3.11.0/appveyor/x64/600dd186-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011461354762161100235200ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/600dd187-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011361354762161100235200ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/600dd188-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011201354762161100235120ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/600dd189-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011441354762161100235210ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/656d875f-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011421354762161100235360ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/656d8760-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011241354762161100234510ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/656d8763-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011441354762161100234560ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/656d8766-2429-11d7-8bf6-00b0d03daa06.reg000077500000000000000000000011501354762161100234560ustar00rootroot00000000000000ÿþ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-3.11.0/appveyor/x64/VC_OBJECTS_PLATFORM_INFO.reg000077500000000000000000000066041354762161100226530ustar00rootroot00000000000000ÿþ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-3.11.0/setup.cfg000066400000000000000000000001301354762161100156700ustar00rootroot00000000000000[metadata] description-file = README.md license_file = LICENSE [test] test_suite=tests yara-python-3.11.0/setup.py000066400000000000000000000251171354762161100155750ustar00rootroot00000000000000# # Copyright (c) 2007-2013. 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 distutils.command.build import build from distutils.command.build_ext import build_ext from setuptools import setup, Command, Extension 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-dotnet', None, 'enable "dotnet" module'), ('enable-dex', None, 'enable "dex" module'), ('enable-macho', None, 'enable "macho" module'), ('enable-profiling', None, 'enable profiling features')] BOOLEAN_OPTIONS = [ 'dynamic-linking', 'enable-cuckoo', 'enable-magic', 'enable-dotnet', 'enable-dex', 'enable-macho', 'enable-profiling'] @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, libraries=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, libraries=libraries) if os.path.exists('a.out'): os.remove('a.out') return result 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_dotnet = None self.enable_dex = None self.enable_macho = None self.enable_profiling = 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_dotnet = None self.enable_dex = None self.enable_macho = None self.enable_profiling = 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_dotnet', 'enable_dotnet'), ('enable_dex', 'enable_dex'), ('enable_macho', 'enable_macho'), ('enable_profiling', 'enable_profiling')) 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_dotnet and self.dynamic_linking: raise distutils.errors.DistutilsOptionError( '--enable-dotnet 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') 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_linux: module.define_macros.append(('USE_LINUX_PROC', '1')) 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(('USE_MACH_PROC', '1')) 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') elif building_for_freebsd: 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(('USE_OPENBSD_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') else: module.define_macros.append(('USE_NO_PROC', '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(('PROFILING_ENABLED', '1')) if self.dynamic_linking: module.libraries.append('yara') else: if not self.define or not ('HASH_MODULE', '1') in self.define: if (has_function('MD5_Init', libraries=['crypto']) and has_function('SHA256_Init', libraries=['crypto'])): module.define_macros.append(('HASH_MODULE', '1')) module.define_macros.append(('HAVE_LIBCRYPTO', '1')) module.libraries.append('crypto') else: exclusions.append('yara/libyara/modules/hash.c') if self.enable_magic: module.define_macros.append(('MAGIC_MODULE', '1')) module.libraries.append('magic') else: exclusions.append('yara/libyara/modules/magic.c') if self.enable_cuckoo: module.define_macros.append(('CUCKOO_MODULE', '1')) module.libraries.append('jansson') else: exclusions.append('yara/libyara/modules/cuckoo.c') if self.enable_dotnet: module.define_macros.append(('DOTNET_MODULE', '1')) else: exclusions.append('yara/libyara/modules/dotnet.c') if self.enable_dex: module.define_macros.append(('DEX_MODULE', '1')) else: exclusions.append('yara/libyara/modules/dex.c') if self.enable_macho: module.define_macros.append(('MACHO_MODULE', '1')) else: exclusions.append('yara/libyara/modules/macho.c') exclusions = [os.path.normpath(x) for x in exclusions] for directory, _, files in os.walk('yara/libyara/'): for x in files: x = os.path.normpath(os.path.join(directory, x)) if x.endswith('.c') and x not in exclusions: module.sources.append(x) 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='3.11.0', description='Python interface for YARA', long_description=readme, 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/', '.'], sources=['yara-python.c'])]) yara-python-3.11.0/tests.py000066400000000000000000001145331354762161100156000ustar00rootroot00000000000000# # Copyright (c) 2007-2014. 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'), ('a[\s\S]b', 'a b', SUCCEED, 'a b'), ('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'), ('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'), ('a\wc', 'abc', SUCCEED, 'abc'), ('a\wc', 'a_c', SUCCEED, 'a_c'), ('a\wc', 'a0c', SUCCEED, 'a0c'), ('a\wc', 'a*c', FAIL), ('\w+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'), ('[\w]+', '--ab_cd0123--', SUCCEED, 'ab_cd0123'), ('\D+', '1234abc5678', SUCCEED, 'abc'), ('[\d]+', '0123456789', SUCCEED, '0123456789'), ('[\D]+', '1234abc5678', SUCCEED, 'abc'), ('[\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'), ] 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] if sys.version_info[0] >= 3: self.assertTrue(matching_string == bytes(test[3], 'utf-8')) else: self.assertTrue(matching_string == 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([ 'rule test { condition: (1 + 1) * 2 == (9 - 1) \ 2 }', 'rule test { condition: 5 % 2 == 1 }', 'rule test { condition: 1.5 + 1.5 == 3}', 'rule test { condition: 3 \ 2 == 1}', '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, '$a', bytes('ab', 'utf-8'))]) else: self.assertTrue(matches[0].strings == [(0, '$a', '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 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 }', 'rule test { strings: $a = /ppi\tmi/ condition: $a }', '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 }', ]) def testFilesize(self): self.assertTrueRules([ 'rule test { condition: filesize == %d }' % len(PE32_FILE), ], PE32_FILE) 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 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) if __name__ == "__main__": unittest.main() yara-python-3.11.0/yara/000077500000000000000000000000001354762161100150115ustar00rootroot00000000000000yara-python-3.11.0/yara-python.c000066400000000000000000001523441354762161100165050ustar00rootroot00000000000000/* Copyright (c) 2007-2013. 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" #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_FromString(x) #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_TO_C(x) PyString_AsString(x) #define PY_STRING_CHECK(x) (PyString_Check(x) || PyUnicode_Check(x)) #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://plusvic.github.io/yara\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, "Dictionary 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 */ }; // Rule object typedef struct { PyObject_HEAD PyObject* identifier; PyObject* tags; PyObject* meta; } Rule; static void Rule_dealloc( PyObject* self); static PyObject* Rule_getattro( PyObject* self, PyObject* name); static PyMemberDef Rule_members[] = { { "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; 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 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 */ 0, /* 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; int which; } CALLBACK_DATA; // 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 != 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) { int i; 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 (i = 0; i < array->items->count; 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) { int i; 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 (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, py_object); Py_DECREF(py_object); } } return py_dict; } #define CALLBACK_MATCHES 0x01 #define CALLBACK_NON_MATCHES 0x02 #define CALLBACK_ALL CALLBACK_MATCHES | CALLBACK_NON_MATCHES int yara_callback( int message, void* message_data, void* user_data) { YR_STRING* string; YR_MATCH* m; YR_META* meta; YR_RULE* rule; YR_MODULE_IMPORT* module_import; const char* tag; PyObject* tag_list = NULL; PyObject* string_list = NULL; PyObject* meta_list = NULL; PyObject* match; PyObject* callback_dict; PyObject* object; PyObject* tuple; PyObject* matches = ((CALLBACK_DATA*) user_data)->matches; PyObject* callback = ((CALLBACK_DATA*) user_data)->callback; PyObject* modules_data = ((CALLBACK_DATA*) user_data)->modules_data; PyObject* modules_callback = ((CALLBACK_DATA*) user_data)->modules_callback; PyObject* module_data; PyObject* callback_result; PyObject* module_info_dict; int which = ((CALLBACK_DATA*) user_data)->which; Py_ssize_t data_size; PyGILState_STATE gil_state; int result = CALLBACK_CONTINUE; if (message == CALLBACK_MSG_SCAN_FINISHED) return CALLBACK_CONTINUE; if (message == CALLBACK_MSG_IMPORT_MODULE && modules_data == NULL) return CALLBACK_CONTINUE; if (message == CALLBACK_MSG_MODULE_IMPORTED && modules_callback == NULL) return CALLBACK_CONTINUE; if (message == CALLBACK_MSG_IMPORT_MODULE) { gil_state = PyGILState_Ensure(); module_import = (YR_MODULE_IMPORT*) message_data; module_data = PyDict_GetItemString( 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 { #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; } if (message == CALLBACK_MSG_MODULE_IMPORTED) { gil_state = PyGILState_Ensure(); module_info_dict = convert_structure_to_python( object_as_structure(message_data)); if (module_info_dict == NULL) return CALLBACK_CONTINUE; object = PY_STRING(object_as_structure(message_data)->identifier); PyDict_SetItemString(module_info_dict, "module", object); Py_DECREF(object); Py_INCREF(modules_callback); callback_result = PyObject_CallFunctionObjArgs( modules_callback, module_info_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(module_info_dict); Py_DECREF(modules_callback); PyGILState_Release(gil_state); return result; } rule = (YR_RULE*) message_data; 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); PyDict_SetItemString(meta_list, meta->identifier, object); Py_DECREF(object); } yr_rule_strings_foreach(rule, string) { yr_string_matches_foreach(string, m) { object = PyBytes_FromStringAndSize((char*) m->data, m->data_length); tuple = Py_BuildValue( "(L,s,O)", m->base + m->offset, string->identifier, object); PyList_Append(string_list, tuple); Py_DECREF(object); Py_DECREF(tuple); } } 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); PyGILState_Release(gil_state); if (bytes != NULL) { Py_ssize_t len; char* buffer; int result = PyBytes_AsStringAndSize(bytes, &buffer, &len); if (result == -1 || (size_t) len < size) { Py_DECREF(bytes); return i; } memcpy((char*) ptr + i * size, buffer, size); Py_DECREF(bytes); } else { return i; } } 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 PyGILState_Release(gil_state); if (result == NULL) return i; Py_DECREF(result); } 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)) { 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_AsLong(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_RULES* rules) { PyObject* key; PyObject* value; Py_ssize_t pos = 0; char* identifier = NULL; int result; while (PyDict_Next(externals, &pos, &key, &value)) { identifier = PY_STRING_TO_C(key); if (PyBool_Check(value)) { result = yr_rules_define_boolean_variable( rules, 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_rules_define_integer_variable( rules, identifier, PyLong_AsLong(value)); } else if (PyFloat_Check(value)) { result = yr_rules_define_float_variable( rules, 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_rules_define_string_variable( rules, identifier, str); } else { PyErr_Format( PyExc_TypeError, "external values must be of type integer, float, boolean or string"); return ERROR_INVALID_ARGUMENT; } // yr_rules_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 void Rule_dealloc( PyObject* self) { Rule* object = (Rule*) self; Py_XDECREF(object->identifier); Py_XDECREF(object->tags); Py_XDECREF(object->meta); 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; } return rules; } static void Rules_dealloc( PyObject* self) { Rules* object = (Rules*) self; Py_XDECREF(object->externals); 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_list_head; 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->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", NULL }; char* filepath = NULL; char* data = NULL; int pid = 0; int timeout = 0; Py_ssize_t length = 0; int error = ERROR_SUCCESS; int fast_mode = 0; PyObject* externals = NULL; PyObject* fast = NULL; Rules* object = (Rules*) self; CALLBACK_DATA callback_data; callback_data.matches = NULL; callback_data.callback = NULL; callback_data.modules_data = NULL; callback_data.modules_callback = NULL; callback_data.which = CALLBACK_ALL; if (PyArg_ParseTupleAndKeywords( args, keywords, "|sis#OOOiOOi", kwlist, &filepath, &pid, &data, &length, &externals, &callback_data.callback, &fast, &timeout, &callback_data.modules_data, &callback_data.modules_callback, &callback_data.which)) { if (filepath == NULL && data == NULL && pid == 0) { return PyErr_Format( PyExc_TypeError, "match() takes at least one argument"); } if (callback_data.callback != NULL) { if (!PyCallable_Check(callback_data.callback)) { return PyErr_Format( PyExc_TypeError, "'callback' must be callable"); } } if (callback_data.modules_callback != NULL) { if (!PyCallable_Check(callback_data.modules_callback)) { return PyErr_Format( PyExc_TypeError, "'modules_callback' must be callable"); } } if (callback_data.modules_data != NULL) { if (!PyDict_Check(callback_data.modules_data)) { return PyErr_Format( PyExc_TypeError, "'modules_data' must be a dictionary"); } } if (externals != NULL && externals != Py_None) { if (PyDict_Check(externals)) { if (process_match_externals(externals, object->rules) != ERROR_SUCCESS) { // Restore original externals provided during compiling. process_match_externals(object->externals, object->rules); return NULL; } } else { return PyErr_Format( PyExc_TypeError, "'externals' must be a dictionary"); } } if (fast != NULL) { fast_mode = (PyObject_IsTrue(fast) == 1); } if (filepath != NULL) { callback_data.matches = PyList_New(0); Py_BEGIN_ALLOW_THREADS error = yr_rules_scan_file( object->rules, filepath, fast_mode ? SCAN_FLAGS_FAST_MODE : 0, yara_callback, &callback_data, timeout); Py_END_ALLOW_THREADS } else if (data != NULL) { callback_data.matches = PyList_New(0); Py_BEGIN_ALLOW_THREADS error = yr_rules_scan_mem( object->rules, (unsigned char*) data, (size_t) length, fast_mode ? SCAN_FLAGS_FAST_MODE : 0, yara_callback, &callback_data, timeout); Py_END_ALLOW_THREADS } else if (pid != 0) { callback_data.matches = PyList_New(0); Py_BEGIN_ALLOW_THREADS error = yr_rules_scan_proc( object->rules, pid, fast_mode ? SCAN_FLAGS_FAST_MODE : 0, yara_callback, &callback_data, timeout); Py_END_ALLOW_THREADS } // Restore original externals provided during compiling. if (object->externals != NULL) { if (process_match_externals( object->externals, object->rules) != ERROR_SUCCESS) { Py_DECREF(callback_data.matches); return NULL; } } if (error != ERROR_SUCCESS) { Py_DECREF(callback_data.matches); if (error != ERROR_CALLBACK_ERROR) { if (filepath != NULL) { handle_error(error, filepath); } else if (data != NULL) { handle_error(error, ""); } else if (pid != 0) { 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 char* message, void* user_data) { 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); } } void raise_exception_on_error_or_warning( int error_level, const char* file_name, int line_number, const char* message, void* user_data) { 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 { if (file_name != NULL) PyErr_Format( YaraWarningError, "%s(%d): %s", file_name, line_number, message); else PyErr_Format( YaraWarningError, "line %d: %s", line_number, message); } } //////////////////////////////////////////////////////////////////////////////// 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", NULL}; unsigned int stack_size = 0; unsigned int max_strings_per_rule = 0; int error = 0; if (PyArg_ParseTupleAndKeywords( args, keywords, "|II", kwlist, &stack_size, &max_strings_per_rule)) { 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); } } 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", "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* include_callback = NULL; Py_ssize_t pos = 0; int fd; int error = 0; char* filepath = NULL; char* source = NULL; char* ns = NULL; if (PyArg_ParseTupleAndKeywords( args, keywords, "|ssOOOOOOO", kwlist, &filepath, &source, &file, &filepaths_dict, &sources_dict, &includes, &externals, &error_on_warning, &include_callback)) { error = yr_compiler_create(&compiler); if (error != ERROR_SUCCESS) return handle_error(error, NULL); yr_compiler_set_callback(compiler, raise_exception_on_error, NULL); if (error_on_warning != NULL) { if (PyBool_Check(error_on_warning)) { if (PyObject_IsTrue(error_on_warning) == 1) { yr_compiler_set_callback( compiler, raise_exception_on_error_or_warning, NULL); } } else { yr_compiler_destroy(compiler); return PyErr_Format( PyExc_TypeError, "'error_on_warning' 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) { error = yr_compiler_add_file(compiler, fh, NULL, filepath); fclose(fh); } else { result = PyErr_SetFromErrno(YaraError); } } else if (source != NULL) { error = yr_compiler_add_string(compiler, source, NULL); } else if (file != NULL) { fd = dup(PyObject_AsFileDescriptor(file)); if (fd != -1) { fh = fdopen(fd, "r"); error = yr_compiler_add_file(compiler, fh, NULL, NULL); fclose(fh); } 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) { error = yr_compiler_add_string(compiler, source, ns); 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) { error = yr_compiler_add_file(compiler, fh, ns, filepath); fclose(fh); 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 (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_list_head; 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->externals_list_head; rules->iter_current_rule = rules->rules->rules_list_head; 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 or max_strings_per_rule)" }, { 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 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_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); #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; PyModule_AddObject(m, "Rule", (PyObject*) &Rule_Type); PyModule_AddObject(m, "Rules", (PyObject*) &Rules_Type); PyModule_AddObject(m, "Match", (PyObject*) &Match_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; } Py_AtExit(finalize); return MOD_SUCCESS_VAL(m); }