diff --git a/.github/workflows/build_multi.yml b/.github/workflows/build_multi.yml
index 607df76..e899183 100644
--- a/.github/workflows/build_multi.yml
+++ b/.github/workflows/build_multi.yml
@@ -132,8 +132,69 @@ jobs:
path: target/*-osx64.jar
overwrite: true
+ build_windows64:
+ runs-on: windows-latest
+
+ strategy:
+ matrix:
+ jdk: [21]
+ libyara_tag: ["v4.5.2-subreption"]
+ arch: [windows64]
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up JDK ${{ matrix.jdk }}
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'oracle'
+ java-version: ${{ matrix.jdk }}
+ settings-path: ${{ github.workspace }}
+
+ - name: Set up MSVC developer environment (x64)
+ uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: x64
+
+ - name: Set up NuGet
+ uses: NuGet/setup-nuget@v2
+
+ - name: Clone libyara (${{ matrix.libyara_tag }})
+ uses: actions/checkout@v4
+ with:
+ repository: subreption/yara
+ path: yara
+ ref: ${{ matrix.libyara_tag }}
+ token: ${{ secrets.GH_YARA_REPO_PAT }}
+
+ # packages.config is not auto-restored by MSBuild.
+ - name: Restore libyara NuGet packages
+ run: nuget restore yara/windows/vs2017/yara.sln
+
+ # libyara only (JNI embedded; no yara/yarac).
+ - name: Build libyara (MSVC Release|x64)
+ run: |
+ msbuild "yara/windows/vs2017/libyara/libyara.vcxproj" /p:Configuration=Release /p:Platform=x64 /v:minimal
+ if ($LASTEXITCODE -ne 0) { throw "msbuild failed for libyara" }
+
+ - name: Export YARA_HOME
+ run: echo "YARA_HOME=${{ github.workspace }}\yara" >> $env:GITHUB_ENV
+
+ - name: Build YARA-Java (Windows)
+ run: mvn -B package --file pom.xml
+ env:
+ YARA_HOME: ${{ env.YARA_HOME }}
+
+ - name: Upload windows64 build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.arch }}-artifacts
+ path: target/*-windows64.jar
+ overwrite: true
+
bundle:
- needs: [build_linux, build_mac]
+ needs: [build_linux, build_mac, build_windows64]
runs-on: ubuntu-latest
steps:
@@ -149,15 +210,22 @@ jobs:
name: osx64-artifacts
path: build/osx64
+ - name: Download windows64 build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: windows64-artifacts
+ path: build/windows64
+
- name: List current working directory
run: ls -lhRt
- # Step to bundle Linux and macOS JARs
+ # Step to bundle Linux, macOS, and Windows JARs
- name: Bundle multiplatform JARs
run: |
mkdir -p bundled-jars
cp build/linux64/*.jar bundled-jars/
cp build/osx64/*.jar bundled-jars/
+ cp build/windows64/*.jar bundled-jars/
- name: Upload bundled JARs
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/build_windows64.yml b/.github/workflows/build_windows64.yml
new file mode 100644
index 0000000..95061f0
--- /dev/null
+++ b/.github/workflows/build_windows64.yml
@@ -0,0 +1,71 @@
+name: Build (windows64)
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+
+jobs:
+
+ build_windows64:
+ runs-on: windows-latest
+
+ strategy:
+ matrix:
+ jdk: [21]
+ libyara_tag: ["v4.5.2-subreption"]
+ arch: [windows64]
+
+ steps:
+ # Step 1: Checkout the repository
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ # Step 2: Set up JDK
+ - name: Set up JDK ${{ matrix.jdk }}
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'oracle'
+ java-version: ${{ matrix.jdk }}
+
+ # Step 3: MSVC dev environment (exported to all later steps).
+ - name: Set up MSVC developer environment (x64)
+ uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: x64
+
+ - name: Set up NuGet
+ uses: NuGet/setup-nuget@v2
+
+ # Step 4: Clone libyara.
+ - name: Clone libyara (${{ matrix.libyara_tag }})
+ uses: actions/checkout@v4
+ with:
+ repository: subreption/yara
+ path: yara
+ ref: ${{ matrix.libyara_tag }}
+ token: ${{ secrets.GH_YARA_REPO_PAT }}
+
+ # Step 5: Restore NuGet deps (packages.config is not auto-restored).
+ - name: Restore libyara NuGet packages
+ run: nuget restore yara/windows/vs2017/yara.sln
+
+ # Step 6: Build libyara only (JNI embedded; no yara/yarac).
+ - name: Build libyara (MSVC Release|x64)
+ run: |
+ msbuild "yara/windows/vs2017/libyara/libyara.vcxproj" /p:Configuration=Release /p:Platform=x64 /v:minimal
+ if ($LASTEXITCODE -ne 0) { throw "msbuild failed for libyara" }
+
+ - name: Export YARA_HOME
+ run: echo "YARA_HOME=${{ github.workspace }}\yara" >> $env:GITHUB_ENV
+
+ # Step 7: Build + test yara-java (windows-amd64 profile auto-activates).
+ - name: Build and test yara-java (mvn package)
+ run: mvn -B package --file pom.xml
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.arch }}-artifacts
+ path: target/*.jar
+ if-no-files-found: error
diff --git a/.github/workflows/ci_nightly.yml b/.github/workflows/ci_nightly.yml
index f53e259..c2493e9 100644
--- a/.github/workflows/ci_nightly.yml
+++ b/.github/workflows/ci_nightly.yml
@@ -173,8 +173,85 @@ jobs:
outputs:
mac-artifact: ${{ steps.build.outputs.maven-artifact }}
+ build_windows64:
+ runs-on: windows-latest
+
+ strategy:
+ matrix:
+ jdk: [21]
+ libyara_tag: [ "v4.5.2-subreption" ]
+ arch: [windows64]
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up JDK ${{ matrix.jdk }}
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'oracle'
+ java-version: ${{ matrix.jdk }}
+ settings-path: ${{ github.workspace }}
+
+ - name: Set up MSVC developer environment (x64)
+ uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: x64
+
+ - name: Set up NuGet
+ uses: NuGet/setup-nuget@v2
+
+ - name: Clone libyara (${{ matrix.libyara_tag }})
+ uses: actions/checkout@v4
+ with:
+ repository: subreption/yara
+ path: yara
+ ref: ${{ matrix.libyara_tag }}
+ token: ${{ secrets.GH_YARA_REPO_PAT }}
+
+ # packages.config is not auto-restored by MSBuild.
+ - name: Restore libyara NuGet packages
+ run: nuget restore yara/windows/vs2017/yara.sln
+
+ # libyara only (JNI embedded; no yara/yarac).
+ - name: Build libyara (MSVC Release|x64)
+ run: |
+ msbuild "yara/windows/vs2017/libyara/libyara.vcxproj" /p:Configuration=Release /p:Platform=x64 /v:minimal
+ if ($LASTEXITCODE -ne 0) { throw "msbuild failed for libyara" }
+
+ - name: Export YARA_HOME
+ run: echo "YARA_HOME=${{ github.workspace }}\yara" >> $env:GITHUB_ENV
+
+ - name: Build YARA-Java (Windows)
+ run: mvn -B package --file pom.xml
+ env:
+ YARA_HOME: ${{ env.YARA_HOME }}
+
+ - name: Upload windows64 build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.arch }}-artifacts
+ path: target/*-windows64.jar
+ overwrite: true
+
+ - name: Set up Maven settings.xml for GitHub Packages
+ shell: bash
+ run: |
+ mkdir -p ~/.m2
+ echo "github${{ github.actor }}${{ secrets.GITHUB_TOKEN }}" > ~/.m2/settings.xml
+
+ - name: Deploy Maven artifacts
+ shell: bash
+ run: mvn deploy -s ~/.m2/settings.xml -DaltDeploymentRepository=github::default::https://maven.pkg.github.com/${{ github.repository }} || echo "Deployment failed but continuing..."
+ env:
+ YARA_HOME: ${{ env.YARA_HOME }}
+
+ - name: Cleanup
+ shell: bash
+ run: rm -rf ~/.m2/settings.xml
+
bundle:
- needs: [build_linux, build_mac]
+ needs: [build_linux, build_mac, build_windows64]
runs-on: ubuntu-latest
steps:
@@ -190,15 +267,22 @@ jobs:
name: osx64-artifacts
path: build/osx64
+ - name: Download windows64 build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: windows64-artifacts
+ path: build/windows64
+
- name: List current working directory
run: ls -lhRt
- # Step to bundle Linux and macOS JARs
+ # Step to bundle Linux, macOS, and Windows JARs
- name: Bundle multiplatform JARs
run: |
mkdir -p bundled-jars
cp build/linux64/*.jar bundled-jars/
cp build/osx64/*.jar bundled-jars/
+ cp build/windows64/*.jar bundled-jars/
- name: Upload bundled JARs
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/ci_release.yml b/.github/workflows/ci_release.yml
index 5f1cb86..7658e28 100644
--- a/.github/workflows/ci_release.yml
+++ b/.github/workflows/ci_release.yml
@@ -179,8 +179,85 @@ jobs:
outputs:
mac-artifact: ${{ steps.build.outputs.maven-artifact }}
+ build_windows64:
+ runs-on: windows-latest
+
+ strategy:
+ matrix:
+ jdk: [21]
+ libyara_tag: [ "v4.5.2-subreption" ]
+ arch: [windows64]
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up JDK ${{ matrix.jdk }}
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'oracle'
+ java-version: ${{ matrix.jdk }}
+ settings-path: ${{ github.workspace }}
+
+ - name: Set up MSVC developer environment (x64)
+ uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: x64
+
+ - name: Set up NuGet
+ uses: NuGet/setup-nuget@v2
+
+ - name: Clone libyara (${{ matrix.libyara_tag }})
+ uses: actions/checkout@v4
+ with:
+ repository: subreption/yara
+ path: yara
+ ref: ${{ matrix.libyara_tag }}
+ token: ${{ secrets.GH_YARA_REPO_PAT }}
+
+ # packages.config is not auto-restored by MSBuild.
+ - name: Restore libyara NuGet packages
+ run: nuget restore yara/windows/vs2017/yara.sln
+
+ # libyara only (JNI embedded; no yara/yarac).
+ - name: Build libyara (MSVC Release|x64)
+ run: |
+ msbuild "yara/windows/vs2017/libyara/libyara.vcxproj" /p:Configuration=Release /p:Platform=x64 /v:minimal
+ if ($LASTEXITCODE -ne 0) { throw "msbuild failed for libyara" }
+
+ - name: Export YARA_HOME
+ run: echo "YARA_HOME=${{ github.workspace }}\yara" >> $env:GITHUB_ENV
+
+ - name: Build YARA-Java (Windows)
+ run: mvn -B package --file pom.xml
+ env:
+ YARA_HOME: ${{ env.YARA_HOME }}
+
+ - name: Upload windows64 build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.arch }}-artifacts
+ path: target/*-windows64.jar
+ overwrite: true
+
+ - name: Set up Maven settings.xml for GitHub Packages
+ shell: bash
+ run: |
+ mkdir -p ~/.m2
+ echo "github${{ github.actor }}${{ secrets.GITHUB_TOKEN }}" > ~/.m2/settings.xml
+
+ - name: Deploy Maven artifacts
+ shell: bash
+ run: mvn deploy -s ~/.m2/settings.xml -DaltDeploymentRepository=github::default::https://maven.pkg.github.com/${{ github.repository }} || echo "Deployment failed but continuing..."
+ env:
+ YARA_HOME: ${{ env.YARA_HOME }}
+
+ - name: Cleanup
+ shell: bash
+ run: rm -rf ~/.m2/settings.xml
+
bundle:
- needs: [build_linux64, build_osx64]
+ needs: [build_linux64, build_osx64, build_windows64]
runs-on: ubuntu-latest
steps:
@@ -196,12 +273,19 @@ jobs:
name: osx64-artifacts
path: build/osx64
- # Step to bundle Linux and macOS JARs
+ - name: Download windows64 build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: windows64-artifacts
+ path: build/windows64
+
+ # Step to bundle Linux, macOS, and Windows JARs
- name: Bundle multiplatform JARs
run: |
mkdir -p bundled-jars
cp build/linux64/*.jar bundled-jars/
cp build/osx64/*.jar bundled-jars/
+ cp build/windows64/*.jar bundled-jars/
- name: Upload bundled JARs
uses: actions/upload-artifact@v4
diff --git a/README.md b/README.md
index cf52e4b..67138ea 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-[](https://github.com/subreption/yara-java/actions/workflows/build_linux64.yml) [](https://github.com/subreption/yara-java/actions/workflows/build_multi.yml) [](https://github.com/subreption/yara-java/actions/workflows/build_macos.yml) [](https://github.com/subreption/yara-java/actions/workflows/ci_nightly.yml) [](https://github.com/subreption/yara-java/actions/workflows/ci_release.yml)
+[](https://github.com/subreption/yara-java/actions/workflows/build_linux64.yml) [](https://github.com/subreption/yara-java/actions/workflows/build_multi.yml) [](https://github.com/subreption/yara-java/actions/workflows/build_macos.yml) [](https://github.com/subreption/yara-java/actions/workflows/build_windows64.yml) [](https://github.com/subreption/yara-java/actions/workflows/ci_nightly.yml) [](https://github.com/subreption/yara-java/actions/workflows/ci_release.yml)
## Introduction
@@ -62,6 +62,22 @@ cd yara-java
mvn clean install
```
+### Windows x64 (MSVC)
+
+On Windows, libyara is built with MSVC from the Visual Studio solution in subreption/yara. From a
+*x64 Native Tools Command Prompt* (or after running `vcvars64.bat`):
+
+```
+git clone https://github.com/subreption/yara.git
+cd yara
+git checkout tags/v4.5.2-subreption
+nuget restore windows/vs2017/yara.sln
+msbuild windows/vs2017/libyara/libyara.vcxproj /p:Configuration=Release /p:Platform=x64
+set YARA_HOME=%CD%
+```
+
+Then build yara-java as above (`mvn clean install`); the `windows-amd64` profile activates automatically.
+
### Building in hardened environments
It is preferable, especially in hardened environments (where `/tmp` might not be executable or even
@@ -75,8 +91,13 @@ extended support.
## Releases
-We have added CI workflows to generate *jars* for the supported platforms upon every stable *tag* in this
-repository.
+CI workflows have been added to generate *jars* for the supported platforms (`linux64`, `osx64`, `windows64`)
+upon every stable *tag* in this repository.
+
+The `windows64` native library is built with MSVC and links the Microsoft Visual C++ runtime dynamically.
+On Windows 10/11 no action is required — the JRE provides that runtime (`vcruntime140.dll`) in its own `bin`
+directory. On older Windows (7/8) lacking the Universal C Runtime, the Visual C++ Redistributable (2015-2022)
+or the Universal C Runtime update (KB2999226) may be required.
## Using the library
diff --git a/pom.xml b/pom.xml
index 3b9cbb7..9234bb0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -100,6 +100,7 @@
${hawtjni-version}
+ build-yara-wrapper
compile
generate
@@ -282,5 +283,74 @@
+
+ windows-amd64
+
+
+ windows
+ amd64
+
+
+
+
+
+
+ org.fusesource.hawtjni
+ hawtjni-maven-plugin
+ ${hawtjni-version}
+
+
+ build-yara-wrapper
+
+ true
+ msbuild
+ v143
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 3.1.0
+
+
+ test-native
+
+
+
+
+
+
+
+ package-native
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+
+ **/external/**
+
+
+
+
+
+
diff --git a/src/main/native-package/vs2010.custom.props b/src/main/native-package/vs2010.custom.props
new file mode 100644
index 0000000..89e936a
--- /dev/null
+++ b/src/main/native-package/vs2010.custom.props
@@ -0,0 +1,15 @@
+
+
+
+
+
+ $(YARA_HOME)\libyara\include;%(AdditionalIncludeDirectories)
+
+ JNI64;%(PreprocessorDefinitions)
+
+
+ $(YARA_HOME)\windows\vs2017\libyara\Release\libyara64.lib;%(AdditionalDependencies)
+
+
+
diff --git a/src/test/java/com/github/subreption/yara/embedded/YaraAreaModuleSweepTest.java b/src/test/java/com/github/subreption/yara/embedded/YaraAreaModuleSweepTest.java
new file mode 100644
index 0000000..20abb89
--- /dev/null
+++ b/src/test/java/com/github/subreption/yara/embedded/YaraAreaModuleSweepTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2024 Subreption LLC. All rights reserved.
+ * Copyright (c) 2015-2022 Paul Apostolescu. 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.
+ */
+package com.github.subreption.yara.embedded;
+
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import com.github.subreption.yara.YaraCompilationCallback;
+import com.github.subreption.yara.YaraCompiler;
+import com.github.subreption.yara.YaraScanCallback;
+import com.github.subreption.yara.YaraScanner;
+
+/**
+ * area module breadth and repeated-scan safety, driven by real rules from the
+ * /rules/signsrch-area-sample.yar fixture. Each rule's scan buffer is rebuilt from its own
+ * signature and scanned via scan(byte[]).
+ */
+public class YaraAreaModuleSweepTest {
+
+ private YaraImpl yara;
+
+ @BeforeEach
+ public void setup() {
+ this.yara = new YaraImpl();
+ }
+
+ @AfterEach
+ public void teardown() throws Exception {
+ yara.close();
+ }
+
+ static final class AreaRule {
+ final String name;
+ final String ruleText;
+ final byte[] buffer;
+ AreaRule(String name, String ruleText, byte[] buffer) {
+ this.name = name; this.ruleText = ruleText; this.buffer = buffer;
+ }
+ @Override public String toString() { return name; }
+ }
+
+ private static final Pattern RULE_BLOCK = Pattern.compile("(?s)rule\\s+\\w+\\b.*?\\n\\}");
+ private static final Pattern RULE_NAME = Pattern.compile("rule\\s+(\\w+)");
+ private static final Pattern FIRST_HEX = Pattern.compile("\\$first\\s*=\\s*\\{\\s*([0-9A-Fa-f]+)\\s*\\}");
+ private static final Pattern AREA_SCAN =
+ Pattern.compile("area\\.scan\\(@first,\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*\"([^\"]*)\"");
+ private static final Pattern HEX_ESCAPE = Pattern.compile("\\\\x([0-9A-Fa-f]{2})");
+
+ static List loadFixtureRules() throws Exception {
+ URL url = YaraAreaModuleSweepTest.class.getResource("/rules/signsrch-area-sample.yar");
+ assertNotNull(url, "signsrch-area-sample.yar not on the test classpath");
+ String text = new String(Files.readAllBytes(Paths.get(url.toURI())), StandardCharsets.UTF_8);
+
+ List rules = new ArrayList<>();
+ Matcher blocks = RULE_BLOCK.matcher(text);
+ while (blocks.find()) {
+ String block = blocks.group();
+
+ Matcher nm = RULE_NAME.matcher(block);
+ Matcher fm = FIRST_HEX.matcher(block);
+ Matcher sm = AREA_SCAN.matcher(block);
+ assertTrue(nm.find() && fm.find() && sm.find(), "unparseable rule block:\n" + block);
+
+ int valueSize = Integer.parseInt(sm.group(1)) / 8;
+ byte[] first = hexToBytes(fm.group(1));
+ byte[] hexData = escapedToBytes(sm.group(4));
+
+ // $first + hex_data values + one value_size of padding == (value_count + 1) * value_size.
+ byte[] buffer = new byte[first.length + hexData.length + valueSize];
+ System.arraycopy(first, 0, buffer, 0, first.length);
+ System.arraycopy(hexData, 0, buffer, first.length, hexData.length);
+
+ rules.add(new AreaRule(nm.group(1), "import \"area\"\n" + block + "\n", buffer));
+ }
+ return rules;
+ }
+
+ private static byte[] hexToBytes(String hex) {
+ int n = hex.length() / 2;
+ byte[] out = new byte[n];
+ for (int i = 0; i < n; i++) {
+ out[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
+ }
+ return out;
+ }
+
+ private static byte[] escapedToBytes(String escaped) {
+ Matcher m = HEX_ESCAPE.matcher(escaped);
+ List bytes = new ArrayList<>();
+ while (m.find()) {
+ bytes.add((byte) Integer.parseInt(m.group(1), 16));
+ }
+ byte[] out = new byte[bytes.size()];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = bytes.get(i);
+ }
+ return out;
+ }
+
+ static Stream areaRules() throws Exception {
+ return loadFixtureRules().stream().map(r -> Arguments.of(r));
+ }
+
+ private boolean scanForMatch(String ruleText, byte[] buffer, String expectedId) throws Exception {
+ YaraCompilationCallback compileCallback =
+ (errorLevel, fileName, lineNumber, message) -> fail("compile error [" + expectedId + "]: " + message);
+ final AtomicBoolean matched = new AtomicBoolean(false);
+ YaraScanCallback scanCallback = v -> {
+ if (expectedId.equals(v.getIdentifier())) {
+ matched.set(true);
+ }
+ };
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback(compileCallback);
+ compiler.addRulesContent(ruleText, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ scanner.setCallback(scanCallback);
+ scanner.scan(buffer);
+ }
+ }
+ return matched.get();
+ }
+
+ @Test
+ public void fixtureParsesAllRules() throws Exception {
+ assertEquals(7, loadFixtureRules().size());
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("areaRules")
+ public void realAreaRuleMatchesItsSignature(AreaRule rule) throws Exception {
+ assertTrue(scanForMatch(rule.ruleText, rule.buffer, rule.name), rule.name);
+ }
+
+ // One scanner, reused across many setCallback+scan(byte[]) calls (the analyzer's block loop).
+ @Test
+ public void reusedScannerRepeatedScansDoNotCrash() throws Exception {
+ List rules = loadFixtureRules();
+
+ StringBuilder allRules = new StringBuilder("import \"area\"\n");
+ for (AreaRule r : rules) {
+ allRules.append(r.ruleText.replaceFirst("(?s)import \"area\"\\n", ""));
+ }
+
+ final int rounds = 200;
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback((errorLevel, fileName, lineNumber, message) -> fail("compile error: " + message));
+ compiler.addRulesContent(allRules.toString(), null);
+
+ try (YaraScanner scanner = compiler.createScanner()) {
+ for (int round = 0; round < rounds; round++) {
+ for (AreaRule r : rules) {
+ final Set matchedIds = new HashSet<>();
+ scanner.setCallback(v -> matchedIds.add(v.getIdentifier()));
+ scanner.scan(r.buffer);
+ assertTrue(matchedIds.contains(r.name), r.name + " round " + round + ": " + matchedIds);
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void compilerScannerLifecycleChurnDoesNotCrash() throws Exception {
+ AreaRule rule = loadFixtureRules().get(0);
+ for (int i = 0; i < 100; i++) {
+ assertTrue(scanForMatch(rule.ruleText, rule.buffer, rule.name), rule.name);
+ }
+ }
+}
diff --git a/src/test/java/com/github/subreption/yara/embedded/YaraAreaModuleTest.java b/src/test/java/com/github/subreption/yara/embedded/YaraAreaModuleTest.java
new file mode 100644
index 0000000..5302fd3
--- /dev/null
+++ b/src/test/java/com/github/subreption/yara/embedded/YaraAreaModuleTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2024 Subreption LLC. All rights reserved.
+ * Copyright (c) 2015-2022 Paul Apostolescu. 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.
+ */
+package com.github.subreption.yara.embedded;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.github.subreption.yara.YaraCompilationCallback;
+import com.github.subreption.yara.YaraCompiler;
+import com.github.subreption.yara.YaraMatch;
+import com.github.subreption.yara.YaraMeta;
+import com.github.subreption.yara.YaraScanCallback;
+import com.github.subreption.yara.YaraScanner;
+
+/**
+ * area module tests. Rules are taken from subreption/ghidra_yara's signsrch_le_be.yar.
+ * Scan buffers must be at least (value_count + 1) * value_size bytes or area.scan returns 0.
+ */
+public class YaraAreaModuleTest {
+
+ private static final String DES_RULE =
+ "import \"area\"\n" +
+ "rule DES : AND {\n" +
+ " strings:\n" +
+ " $first = { 0F0F0F0F }\n" +
+ " condition:\n" +
+ " $first and area.scan(@first, 32, 5, 640,\n" +
+ " \"\\xFF\\xFF\\x00\\x00\\x33\\x33\\x33\\x33\\xFF\\x00\\xFF\\x00\\x55\\x55\\x55\\x55\")\n" +
+ "}\n";
+
+ // $first + the four area values + one value_size of padding (24 == (5 + 1) * 4).
+ private static final byte[] DES_BUFFER = {
+ 0x0F, 0x0F, 0x0F, 0x0F,
+ (byte) 0xFF, (byte) 0xFF, 0x00, 0x00,
+ 0x33, 0x33, 0x33, 0x33,
+ (byte) 0xFF, 0x00, (byte) 0xFF, 0x00,
+ 0x55, 0x55, 0x55, 0x55,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ private YaraImpl yara;
+
+ @BeforeEach
+ public void setup() {
+ this.yara = new YaraImpl();
+ }
+
+ @AfterEach
+ public void teardown() throws Exception {
+ yara.close();
+ }
+
+ private static File tempFileWith(byte[] data) throws Exception {
+ File temp = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
+ Files.write(Paths.get(temp.getAbsolutePath()), data, StandardOpenOption.WRITE);
+ return temp;
+ }
+
+ private boolean scan(String rules, byte[] buffer, String expectedId) throws Exception {
+ File temp = tempFileWith(buffer);
+ YaraCompilationCallback compileCallback =
+ (errorLevel, fileName, lineNumber, message) -> fail("compile error: " + message);
+ final AtomicBoolean matched = new AtomicBoolean(false);
+ YaraScanCallback scanCallback = v -> {
+ if (expectedId.equals(v.getIdentifier())) {
+ matched.set(true);
+ }
+ };
+
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback(compileCallback);
+ compiler.addRulesContent(rules, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ scanner.setCallback(scanCallback);
+ scanner.scan(temp);
+ }
+ }
+ return matched.get();
+ }
+
+ @Test
+ public void testAreaModuleIsCompiledIn() throws Exception {
+ String rules = "import \"area\"\nrule AreaModulePresent { condition: true }\n";
+ YaraCompilationCallback compileCallback =
+ (errorLevel, fileName, lineNumber, message) -> fail("area import failed: " + message);
+
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback(compileCallback);
+ compiler.addRulesContent(rules, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ assertNotNull(scanner);
+ }
+ }
+ }
+
+ @Test
+ public void testAreaScanDes32BitMatches() throws Exception {
+ assertTrue(scan(DES_RULE, DES_BUFFER, "DES"));
+ }
+
+ @Test
+ public void testAreaScanRc564BitMatches() throws Exception {
+ String rules =
+ "import \"area\"\n" +
+ "rule RC5_RC5_64_P_and_RC5_64_Q : AND {\n" +
+ " strings:\n" +
+ " $first = { 6B2AED8A6251E1B7 }\n" +
+ " condition:\n" +
+ " $first and area.scan(@first, 64, 2, 512,\n" +
+ " \"\\x15\\x7C\\x4A\\x7F\\xB9\\x79\\x37\\x9E\")\n" +
+ "}\n";
+ byte[] buffer = {
+ 0x6B, 0x2A, (byte) 0xED, (byte) 0x8A, 0x62, 0x51, (byte) 0xE1, (byte) 0xB7,
+ 0x15, 0x7C, 0x4A, 0x7F, (byte) 0xB9, 0x79, 0x37, (byte) 0x9E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ assertTrue(scan(rules, buffer, "RC5_RC5_64_P_and_RC5_64_Q"));
+ }
+
+ @Test
+ public void testAreaScanDoesNotFalseMatch() throws Exception {
+ byte[] buffer = { // $first present, area values absent
+ 0x0F, 0x0F, 0x0F, 0x0F,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assertFalse(scan(DES_RULE, buffer, "DES"));
+ }
+
+ @Test
+ public void testAreaScanBufferOneUnderGuardDoesNotMatchOrCrash() throws Exception {
+ byte[] buffer = { // 23 bytes, one under (5 + 1) * 4
+ 0x0F, 0x0F, 0x0F, 0x0F,
+ (byte) 0xFF, (byte) 0xFF, 0x00, 0x00,
+ 0x33, 0x33, 0x33, 0x33,
+ (byte) 0xFF, 0x00, (byte) 0xFF, 0x00,
+ 0x55, 0x55, 0x55, 0x55,
+ 0x00, 0x00, 0x00
+ };
+ assertFalse(scan(DES_RULE, buffer, "DES"));
+ }
+
+ @Test
+ public void testAreaMatchExposesCorrectRuleData() throws Exception {
+ String rules =
+ "import \"area\"\n" +
+ "rule AreaMatchData : AND CRYPTO {\n" +
+ " meta:\n" +
+ " description = \"DES init constants\"\n" +
+ " author = \"Luigi Auriemma\"\n" +
+ " value_count = 5\n" +
+ " strings:\n" +
+ " $first = { 0F0F0F0F }\n" +
+ " condition:\n" +
+ " $first and area.scan(@first, 32, 5, 640,\n" +
+ " \"\\xFF\\xFF\\x00\\x00\\x33\\x33\\x33\\x33\\xFF\\x00\\xFF\\x00\\x55\\x55\\x55\\x55\")\n" +
+ "}\n";
+
+ final AtomicBoolean verified = new AtomicBoolean(false);
+ YaraScanCallback scanCallback = v -> {
+ assertEquals("AreaMatchData", v.getIdentifier());
+
+ Set tags = new HashSet<>();
+ v.getTags().forEachRemaining(tags::add);
+ assertTrue(tags.contains("AND") && tags.contains("CRYPTO"));
+
+ Map metas = new HashMap<>();
+ v.getMetadata().forEachRemaining(m -> metas.put(m.getIdentifier(), m));
+ assertEquals("DES init constants", metas.get("description").getString());
+ assertEquals("Luigi Auriemma", metas.get("author").getString());
+ assertEquals(5, metas.get("value_count").getInteger());
+
+ YaraMatch m = v.getStrings().next().getMatches().next();
+ assertArrayEquals(new byte[] { 0x0F, 0x0F, 0x0F, 0x0F }, m.getBytes());
+ assertEquals(0L, m.getOffset());
+
+ verified.set(true);
+ };
+
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback((errorLevel, fileName, lineNumber, message) -> fail("compile error: " + message));
+ compiler.addRulesContent(rules, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ scanner.setCallback(scanCallback);
+ scanner.scan(DES_BUFFER);
+ }
+ }
+ assertTrue(verified.get());
+ }
+
+ @Test
+ public void testAreaScanLargeBufferReportsCorrectOffset() throws Exception {
+ final int matchOffset = 8_000_000;
+ byte[] buffer = new byte[16_000_000];
+ System.arraycopy(DES_BUFFER, 0, buffer, matchOffset, DES_BUFFER.length);
+
+ final AtomicBoolean matched = new AtomicBoolean(false);
+ final AtomicLong reportedOffset = new AtomicLong(-1);
+ YaraScanCallback scanCallback = v -> {
+ if ("DES".equals(v.getIdentifier())) {
+ reportedOffset.set(v.getStrings().next().getMatches().next().getOffset());
+ matched.set(true);
+ }
+ };
+
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback((errorLevel, fileName, lineNumber, message) -> fail("compile error: " + message));
+ compiler.addRulesContent(DES_RULE, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ scanner.setCallback(scanCallback);
+ scanner.scan(buffer);
+ }
+ }
+ assertTrue(matched.get());
+ assertEquals(matchOffset, reportedOffset.get());
+ }
+
+ // One scan, one rule present and one absent: guards against a build that silently never matches.
+ @Test
+ public void testAreaScanMatchesAndRejectsInOneScan() throws Exception {
+ String rules =
+ "import \"area\"\n" +
+ "rule DES : AND {\n" +
+ " strings: $first = { 0F0F0F0F }\n" +
+ " condition: $first and area.scan(@first, 32, 5, 640,\n" +
+ " \"\\xFF\\xFF\\x00\\x00\\x33\\x33\\x33\\x33\\xFF\\x00\\xFF\\x00\\x55\\x55\\x55\\x55\")\n" +
+ "}\n" +
+ "rule DecoyAbsent : AND {\n" +
+ " strings: $first = \"foobar\"\n" +
+ " condition: $first and area.scan(@first, 32, 2, 256, \"\\x11\\x22\\x33\\x44\")\n" +
+ "}\n";
+
+ Set matched = new HashSet<>();
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback((errorLevel, fileName, lineNumber, message) -> fail("compile error: " + message));
+ compiler.addRulesContent(rules, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ scanner.setCallback(v -> matched.add(v.getIdentifier()));
+ scanner.scan(DES_BUFFER);
+ }
+ }
+ assertTrue(matched.contains("DES"));
+ assertFalse(matched.contains("DecoyAbsent"));
+ }
+}
diff --git a/src/test/java/com/github/subreption/yara/embedded/YaraMatchEdgeCasesTest.java b/src/test/java/com/github/subreption/yara/embedded/YaraMatchEdgeCasesTest.java
new file mode 100644
index 0000000..15ac3e0
--- /dev/null
+++ b/src/test/java/com/github/subreption/yara/embedded/YaraMatchEdgeCasesTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2024 Subreption LLC. All rights reserved.
+ * Copyright (c) 2015-2022 Paul Apostolescu. 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.
+ */
+package com.github.subreption.yara.embedded;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.github.subreption.yara.YaraCompiler;
+import com.github.subreption.yara.YaraMatch;
+import com.github.subreption.yara.YaraScanner;
+
+/**
+ * Match-behavior edge cases: no-match variety, multiple matches, and match-instance iteration.
+ */
+public class YaraMatchEdgeCasesTest {
+
+ private YaraImpl yara;
+
+ @BeforeEach
+ public void setup() {
+ this.yara = new YaraImpl();
+ }
+
+ @AfterEach
+ public void teardown() throws Exception {
+ yara.close();
+ }
+
+ private static final String DES_BODY =
+ "rule DES : AND {\n" +
+ " strings:\n" +
+ " $first = { 0F0F0F0F }\n" +
+ " condition:\n" +
+ " $first and area.scan(@first, 32, 5, 640,\n" +
+ " \"\\xFF\\xFF\\x00\\x00\\x33\\x33\\x33\\x33\\xFF\\x00\\xFF\\x00\\x55\\x55\\x55\\x55\")\n" +
+ "}\n";
+
+ private static final String RC5_BODY =
+ "rule RC5_RC5_64_P_and_RC5_64_Q : AND {\n" +
+ " strings:\n" +
+ " $first = { 6B2AED8A6251E1B7 }\n" +
+ " condition:\n" +
+ " $first and area.scan(@first, 64, 2, 512, \"\\x15\\x7C\\x4A\\x7F\\xB9\\x79\\x37\\x9E\")\n" +
+ "}\n";
+
+ private static final byte[] DES_BLOCK = {
+ 0x0F, 0x0F, 0x0F, 0x0F,
+ (byte) 0xFF, (byte) 0xFF, 0x00, 0x00,
+ 0x33, 0x33, 0x33, 0x33,
+ (byte) 0xFF, 0x00, (byte) 0xFF, 0x00,
+ 0x55, 0x55, 0x55, 0x55,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ private static final byte[] RC5_BLOCK = {
+ 0x6B, 0x2A, (byte) 0xED, (byte) 0x8A, 0x62, 0x51, (byte) 0xE1, (byte) 0xB7,
+ 0x15, 0x7C, 0x4A, 0x7F, (byte) 0xB9, 0x79, 0x37, (byte) 0x9E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ private static String areaRules(String... bodies) {
+ StringBuilder sb = new StringBuilder("import \"area\"\n");
+ for (String b : bodies) {
+ sb.append(b);
+ }
+ return sb.toString();
+ }
+
+ private List matchedIds(String rules, byte[] buffer) throws Exception {
+ final List ids = new ArrayList<>();
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback((errorLevel, fileName, lineNumber, message) -> fail("compile error: " + message));
+ compiler.addRulesContent(rules, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ scanner.setCallback(v -> ids.add(v.getIdentifier()));
+ scanner.scan(buffer);
+ }
+ }
+ return ids;
+ }
+
+ @Test
+ public void noMatchWhenPatternAbsent() throws Exception {
+ assertTrue(matchedIds(areaRules(DES_BODY), new byte[64]).isEmpty());
+ }
+
+ @Test
+ public void noMatchWhenAreaValuesOutOfRange() throws Exception {
+ byte[] buffer = new byte[6000];
+ System.arraycopy(DES_BLOCK, 0, buffer, 0, 4); // $first at offset 0
+ System.arraycopy(DES_BLOCK, 4, buffer, 5000, 16); // values well past scan_range (640)
+ assertTrue(matchedIds(areaRules(DES_BODY), buffer).isEmpty());
+ }
+
+ @Test
+ public void multipleDistinctRulesMatchOneBuffer() throws Exception {
+ byte[] buffer = new byte[200];
+ System.arraycopy(DES_BLOCK, 0, buffer, 0, DES_BLOCK.length);
+ System.arraycopy(RC5_BLOCK, 0, buffer, 100, RC5_BLOCK.length);
+
+ List ids = matchedIds(areaRules(DES_BODY, RC5_BODY), buffer);
+ assertTrue(ids.contains("DES"), "got " + ids);
+ assertTrue(ids.contains("RC5_RC5_64_P_and_RC5_64_Q"), "got " + ids);
+ }
+
+ @Test
+ public void stringMatchInstancesIteratedExactly() throws Exception {
+ final int n = 64;
+ final int stride = 8; // 4-byte pattern + 4-byte gap, non-overlapping
+ final byte[] pattern = { 'M', 'A', 'R', 'K' };
+
+ byte[] buffer = new byte[n * stride];
+ Set expectedOffsets = new HashSet<>();
+ for (int i = 0; i < n; i++) {
+ System.arraycopy(pattern, 0, buffer, i * stride, pattern.length);
+ expectedOffsets.add((long) (i * stride));
+ }
+ String rule = "rule Marks { strings: $a = \"MARK\" condition: #a == " + n + " }";
+
+ final Set seenOffsets = new HashSet<>();
+ final AtomicBoolean fired = new AtomicBoolean(false);
+ try (YaraCompiler compiler = yara.createCompiler()) {
+ compiler.setCallback((errorLevel, fileName, lineNumber, message) -> fail("compile error: " + message));
+ compiler.addRulesContent(rule, null);
+ try (YaraScanner scanner = compiler.createScanner()) {
+ scanner.setCallback(v -> {
+ fired.set(true);
+ for (Iterator it = v.getStrings().next().getMatches(); it.hasNext(); ) {
+ seenOffsets.add(it.next().getOffset());
+ }
+ });
+ scanner.scan(buffer);
+ }
+ }
+ assertTrue(fired.get());
+ assertEquals(expectedOffsets, seenOffsets);
+ }
+}
diff --git a/src/test/resources/rules/signsrch-area-sample.yar b/src/test/resources/rules/signsrch-area-sample.yar
new file mode 100644
index 0000000..da8b28f
--- /dev/null
+++ b/src/test/resources/rules/signsrch-area-sample.yar
@@ -0,0 +1,100 @@
+/*
+ * Curated sample of REAL area.scan rules, extracted verbatim from
+ * subreption/ghidra_yara's data/rules/signsrch/signsrch_le_be.yar
+ * (Luigi Auriemma's signsrch.sig, converted by Kevin Weatherman).
+ * Self-contained test fixture spanning value_bits 32/64 and value_count 2..88.
+ * Consumed by YaraAreaModuleSweepTest. Do not hand-edit.
+ */
+import "area"
+
+rule TEA_encryption_decryption_0xc6ef3720_0x9e3779b9 : AND
+{
+ meta:
+ description = "TEA encryption/decryption (0xc6ef3720 0x9e3779b9) [32.le.8&]"
+ author = "Luigi Auriemma"
+ strings:
+ $first = { 2037EFC6} // [0] 0xC6EF3720
+ condition:
+ $first and area.scan(@first, 32, 2, 256,
+ "\xB9\x79\x37\x9E")
+ /* [1] 0x9E3779B9 */
+}
+
+rule MD4_digest : AND
+{
+ meta:
+ description = "MD4 digest [32.le.24&]"
+ author = "Luigi Auriemma"
+ strings:
+ $first = { 01234567} // [0] 0x67452301
+ condition:
+ $first and area.scan(@first, 32, 6, 768,
+ "\x89\xAB\xCD\xEF\xFE\xDC\xBA\x98\x76\x54\x32\x10\x99\x79\x82\x5A\xA1\xEB\xD9\x6E")
+ /* [1] 0xEFCDAB89, [2] 0x98BADCFE, [3] 0x10325476, [4] 0x5A827999, [5] 0x6ED9EBA1 */
+}
+
+rule SSL2_define : AND
+{
+ meta:
+ description = "SSL2 #define [32.le.52&]"
+ author = "Luigi Auriemma"
+ strings:
+ $first = { 00000002} // [0] 0x2000000
+ condition:
+ $first and area.scan(@first, 32, 13, 1664,
+ "\x80\x00\x01\x02\x80\x00\x02\x02\x80\x00\x03\x02\x80\x00\x04\x02\x80\x00\x05\x02\x40\x00\x06\x02\x40\x01\x06\x02\xC0\x00\x07\x02\xC0\x01\x07\x02\x80\x00\x08\x02\x00\x08\xFF\x02\x10\x08\xFF\x02")
+ /* [1] 0x02010080, [2] 0x02020080, [3] 0x02030080, [4] 0x02040080, [5] 0x02050080, [6] 0x02060040, [7] 0x02060140, [8] 0x020700C0, [9] 0x020701C0, [10] 0x02080080, [11] 0x02FF0800, [12] 0x02FF0810 */
+}
+
+rule SHA224 : AND
+{
+ meta:
+ description = "SHA224 [32.le.288&]"
+ author = "Luigi Auriemma"
+ strings:
+ $first = { D89E05C1} // [0] 0xC1059ED8
+ condition:
+ $first and area.scan(@first, 32, 72, 3000,
+ "\x07\xD5\x7C\x36\x17\xDD\x70\x30\x39\x59\x0E\xF7\x31\x0B\xC0\xFF\x11\x15\x58\x68\xA7\x8F\xF9\x64\xA4\x4F\xFA\xBE\x98\x2F\x8A\x42\x91\x44\x37\x71\xCF\xFB\xC0\xB5\xA5\xDB\xB5\xE9\x5B\xC2\x56\x39\xF1\x11\xF1\x59\xA4\x82\x3F\x92\xD5\x5E\x1C\xAB\x98\xAA\x07\xD8\x01\x5B\x83\x12\xBE\x85\x31\x24\xC3\x7D\x0C\x55\x74\x5D\xBE\x72\xFE\xB1\xDE\x80\xA7\x06\xDC\x9B\x74\xF1\x9B\xC1\xC1\x69\x9B\xE4\x86\x47\xBE\xEF\xC6\x9D\xC1\x0F\xCC\xA1\x0C\x24\x6F\x2C\xE9\x2D\xAA\x84\x74\x4A\xDC\xA9\xB0\x5C\xDA\x88\xF9\x76\x52\x51\x3E\x98\x6D\xC6\x31\xA8\xC8\x27\x03\xB0\xC7\x7F\x59\xBF\xF3\x0B\xE0\xC6\x47\x91\xA7\xD5\x51\x63\xCA\x06\x67\x29\x29\x14\x85\x0A\xB7\x27\x38\x21\x1B\x2E\xFC\x6D\x2C\x4D\x13\x0D\x38\x53\x54\x73\x0A\x65\xBB\x0A\x6A\x76\x2E\xC9\xC2\x81\x85\x2C\x72\x92\xA1\xE8\xBF\xA2\x4B\x66\x1A\xA8\x70\x8B\x4B\xC2\xA3\x51\x6C\xC7\x19\xE8\x92\xD1\x24\x06\x99\xD6\x85\x35\x0E\xF4\x70\xA0\x6A\x10\x16\xC1\xA4\x19\x08\x6C\x37\x1E\x4C\x77\x48\x27\xB5\xBC\xB0\x34\xB3\x0C\x1C\x39\x4A\xAA\xD8\x4E\x4F\xCA\x9C\x5B\xF3\x6F\x2E\x68\xEE\x82\x8F\x74\x6F\x63\xA5\x78\x14\x78\xC8\x84\x08\x02\xC7\x8C\xFA\xFF\xBE\x90\xEB\x6C\x50\xA4\xF7\xA3\xF9\xBE\xF2\x78\x71\xC6")
+ /* [1] 0x367CD507, [2] 0x3070DD17, [3] 0xF70E5939, [4] 0xFFC00B31, [5] 0x68581511, [6] 0x64F98FA7, [7] 0xBEFA4FA4, [8] 0x428A2F98, [9] 0x71374491, [10] 0xB5C0FBCF, [11] 0xE9B5DBA5, [12] 0x3956C25B, [13] 0x59F111F1, [14] 0x923F82A4, [15] 0xAB1C5ED5, [16] 0xD807AA98, [17] 0x12835B01, [18] 0x243185BE, [19] 0x550C7DC3, [20] 0x72BE5D74, [21] 0x80DEB1FE, [22] 0x9BDC06A7, [23] 0xC19BF174, [24] 0xE49B69C1, [25] 0xEFBE4786, [26] 0x0FC19DC6, [27] 0x240CA1CC, [28] 0x2DE92C6F, [29] 0x4A7484AA, [30] 0x5CB0A9DC, [31] 0x76F988DA, [32] 0x983E5152, [33] 0xA831C66D, [34] 0xB00327C8, [35] 0xBF597FC7, [36] 0xC6E00BF3, [37] 0xD5A79147, [38] 0x06CA6351, [39] 0x14292967, [40] 0x27B70A85, [41] 0x2E1B2138, [42] 0x4D2C6DFC, [43] 0x53380D13, [44] 0x650A7354, [45] 0x766A0ABB, [46] 0x81C2C92E, [47] 0x92722C85, [48] 0xA2BFE8A1, [49] 0xA81A664B, [50] 0xC24B8B70, [51] 0xC76C51A3, [52] 0xD192E819, [53] 0xD6990624, [54] 0xF40E3585, [55] 0x106AA070, [56] 0x19A4C116, [57] 0x1E376C08, [58] 0x2748774C, [59] 0x34B0BCB5, [60] 0x391C0CB3, [61] 0x4ED8AA4A, [62] 0x5B9CCA4F, [63] 0x682E6FF3, [64] 0x748F82EE, [65] 0x78A5636F, [66] 0x84C87814, [67] 0x8CC70208, [68] 0x90BEFFFA, [69] 0xA4506CEB, [70] 0xBEF9A3F7, [71] 0xC67178F2 */
+}
+
+rule RC5_RC5_64_P_and_RC5_64_Q : AND
+{
+ meta:
+ description = "RC5 RC5_64_P and RC5_64_Q [64.le.16&]"
+ author = "Luigi Auriemma"
+ strings:
+ $first = { 6B2AED8A6251E1B7} // [0] 0xB7E151628AED2A6B
+ condition:
+ $first and area.scan(@first, 64, 2, 512,
+ "\x15\x7C\x4A\x7F\xB9\x79\x37\x9E")
+ /* [1] 0x9E3779B97F4A7C15 */
+}
+
+rule Initial_hash_value_H_for_SHA_384_and_SHA_512 : AND
+{
+ meta:
+ description = "Initial hash value H for SHA-384 and SHA-512 [64.le.64&]"
+ author = "Luigi Auriemma"
+ strings:
+ $first = { D89E05C15D9DBBCB} // [0] 0xCBBB9D5DC1059ED8
+ condition:
+ $first and area.scan(@first, 64, 8, 2048,
+ "\x07\xD5\x7C\x36\x2A\x29\x9A\x62\x17\xDD\x70\x30\x5A\x01\x59\x91\x39\x59\x0E\xF7\xD8\xEC\x2F\x15\x31\x0B\xC0\xFF\x67\x26\x33\x67\x11\x15\x58\x68\x87\x4A\xB4\x8E\xA7\x8F\xF9\x64\x0D\x2E\x0C\xDB\xA4\x4F\xFA\xBE\x1D\x48\xB5\x47")
+ /* [1] 0x629A292A367CD507, [2] 0x9159015A3070DD17, [3] 0x152FECD8F70E5939, [4] 0x67332667FFC00B31, [5] 0x8EB44A8768581511, [6] 0xDB0C2E0D64F98FA7, [7] 0x47B5481DBEFA4FA4 */
+}
+
+rule SHA384 : AND
+{
+ meta:
+ description = "SHA384 [64.le.704&]"
+ author = "Luigi Auriemma"
+ strings:
+ $first = { D89E05C15D9DBBCB} // [0] 0xCBBB9D5DC1059ED8
+ condition:
+ $first and area.scan(@first, 64, 88, 3000,
+ "\x07\xD5\x7C\x36\x2A\x29\x9A\x62\x17\xDD\x70\x30\x5A\x01\x59\x91\x39\x59\x0E\xF7\xD8\xEC\x2F\x15\x31\x0B\xC0\xFF\x67\x26\x33\x67\x11\x15\x58\x68\x87\x4A\xB4\x8E\xA7\x8F\xF9\x64\x0D\x2E\x0C\xDB\xA4\x4F\xFA\xBE\x1D\x48\xB5\x47\x22\xAE\x28\xD7\x98\x2F\x8A\x42\xCD\x65\xEF\x23\x91\x44\x37\x71\x2F\x3B\x4D\xEC\xCF\xFB\xC0\xB5\xBC\xDB\x89\x81\xA5\xDB\xB5\xE9\x38\xB5\x48\xF3\x5B\xC2\x56\x39\x19\xD0\x05\xB6\xF1\x11\xF1\x59\x9B\x4F\x19\xAF\xA4\x82\x3F\x92\x18\x81\x6D\xDA\xD5\x5E\x1C\xAB\x42\x02\x03\xA3\x98\xAA\x07\xD8\xBE\x6F\x70\x45\x01\x5B\x83\x12\x8C\xB2\xE4\x4E\xBE\x85\x31\x24\xE2\xB4\xFF\xD5\xC3\x7D\x0C\x55\x6F\x89\x7B\xF2\x74\x5D\xBE\x72\xB1\x96\x16\x3B\xFE\xB1\xDE\x80\x35\x12\xC7\x25\xA7\x06\xDC\x9B\x94\x26\x69\xCF\x74\xF1\x9B\xC1\xD2\x4A\xF1\x9E\xC1\x69\x9B\xE4\xE3\x25\x4F\x38\x86\x47\xBE\xEF\xB5\xD5\x8C\x8B\xC6\x9D\xC1\x0F\x65\x9C\xAC\x77\xCC\xA1\x0C\x24\x75\x02\x2B\x59\x6F\x2C\xE9\x2D\x83\xE4\xA6\x6E\xAA\x84\x74\x4A\xD4\xFB\x41\xBD\xDC\xA9\xB0\x5C\xB5\x53\x11\x83\xDA\x88\xF9\x76\xAB\xDF\x66\xEE\x52\x51\x3E\x98\x10\x32\xB4\x2D\x6D\xC6\x31\xA8\x3F\x21\xFB\x98\xC8\x27\x03\xB0\xE4\x0E\xEF\xBE\xC7\x7F\x59\xBF\xC2\x8F\xA8\x3D\xF3\x0B\xE0\xC6\x25\xA7\x0A\x93\x47\x91\xA7\xD5\x6F\x82\x03\xE0\x51\x63\xCA\x06\x70\x6E\x0E\x0A\x67\x29\x29\x14\xFC\x2F\xD2\x46\x85\x0A\xB7\x27\x26\xC9\x26\x5C\x38\x21\x1B\x2E\xED\x2A\xC4\x5A\xFC\x6D\x2C\x4D\xDF\xB3\x95\x9D\x13\x0D\x38\x53\xDE\x63\xAF\x8B\x54\x73\x0A\x65\xA8\xB2\x77\x3C\xBB\x0A\x6A\x76\xE6\xAE\xED\x47\x2E\xC9\xC2\x81\x3B\x35\x82\x14\x85\x2C\x72\x92\x64\x03\xF1\x4C\xA1\xE8\xBF\xA2\x01\x30\x42\xBC\x4B\x66\x1A\xA8\x91\x97\xF8\xD0\x70\x8B\x4B\xC2\x30\xBE\x54\x06\xA3\x51\x6C\xC7\x18\x52\xEF\xD6\x19\xE8\x92\xD1\x10\xA9\x65\x55\x24\x06\x99\xD6\x2A\x20\x71\x57\x85\x35\x0E\xF4\xB8\xD1\xBB\x32\x70\xA0\x6A\x10\xC8\xD0\xD2\xB8\x16\xC1\xA4\x19\x53\xAB\x41\x51\x08\x6C\x37\x1E\x99\xEB\x8E\xDF\x4C\x77\x48\x27\xA8\x48\x9B\xE1\xB5\xBC\xB0\x34\x63\x5A\xC9\xC5\xB3\x0C\x1C\x39\xCB\x8A\x41\xE3\x4A\xAA\xD8\x4E\x73\xE3\x63\x77\x4F\xCA\x9C\x5B\xA3\xB8\xB2\xD6\xF3\x6F\x2E\x68\xFC\xB2\xEF\x5D\xEE\x82\x8F\x74\x60\x2F\x17\x43\x6F\x63\xA5\x78\x72\xAB\xF0\xA1\x14\x78\xC8\x84\xEC\x39\x64\x1A\x08\x02\xC7\x8C\x28\x1E\x63\x23\xFA\xFF\xBE\x90\xE9\xBD\x82\xDE\xEB\x6C\x50\xA4\x15\x79\xC6\xB2\xF7\xA3\xF9\xBE\x2B\x53\x72\xE3\xF2\x78\x71\xC6\x9C\x61\x26\xEA\xCE\x3E\x27\xCA\x07\xC2\xC0\x21\xC7\xB8\x86\xD1\x1E\xEB\xE0\xCD\xD6\x7D\xDA\xEA\x78\xD1\x6E\xEE\x7F\x4F\x7D\xF5\xBA\x6F\x17\x72\xAA\x67\xF0\x06\xA6\x98\xC8\xA2\xC5\x7D\x63\x0A\xAE\x0D\xF9\xBE\x04\x98\x3F\x11\x1B\x47\x1C\x13\x35\x0B\x71\x1B\x84\x7D\x04\x23\xF5\x77\xDB\x28\x93\x24\xC7\x40\x7B\xAB\xCA\x32\xBC\xBE\xC9\x15\x0A\xBE\x9E\x3C\x4C\x0D\x10\x9C\xC4\x67\x1D\x43\xB6\x42\x3E\xCB\xBE\xD4\xC5\x4C\x2A\x7E\x65\xFC\x9C\x29\x7F\x59\xEC\xFA\xD6\x3A\xAB\x6F\xCB\x5F\x17\x58\x47\x4A\x8C\x19\x44\x6C")
+ /* [1] 0x629A292A367CD507, [2] 0x9159015A3070DD17, [3] 0x152FECD8F70E5939, [4] 0x67332667FFC00B31, [5] 0x8EB44A8768581511, [6] 0xDB0C2E0D64F98FA7, [7] 0x47B5481DBEFA4FA4, [8] 0x428A2F98D728AE22, [9] 0x7137449123EF65CD, [10] 0xB5C0FBCFEC4D3B2F, [11] 0xE9B5DBA58189DBBC, [12] 0x3956C25BF348B538, [13] 0x59F111F1B605D019, [14] 0x923F82A4AF194F9B, [15] 0xAB1C5ED5DA6D8118, [16] 0xD807AA98A3030242, [17] 0x12835B0145706FBE, [18] 0x243185BE4EE4B28C, [19] 0x550C7DC3D5FFB4E2, [20] 0x72BE5D74F27B896F, [21] 0x80DEB1FE3B1696B1, [22] 0x9BDC06A725C71235, [23] 0xC19BF174CF692694, [24] 0xE49B69C19EF14AD2, [25] 0xEFBE4786384F25E3, [26] 0x0FC19DC68B8CD5B5, [27] 0x240CA1CC77AC9C65, [28] 0x2DE92C6F592B0275, [29] 0x4A7484AA6EA6E483, [30] 0x5CB0A9DCBD41FBD4, [31] 0x76F988DA831153B5, [32] 0x983E5152EE66DFAB, [33] 0xA831C66D2DB43210, [34] 0xB00327C898FB213F, [35] 0xBF597FC7BEEF0EE4, [36] 0xC6E00BF33DA88FC2, [37] 0xD5A79147930AA725, [38] 0x06CA6351E003826F, [39] 0x142929670A0E6E70, [40] 0x27B70A8546D22FFC, [41] 0x2E1B21385C26C926, [42] 0x4D2C6DFC5AC42AED, [43] 0x53380D139D95B3DF, [44] 0x650A73548BAF63DE, [45] 0x766A0ABB3C77B2A8, [46] 0x81C2C92E47EDAEE6, [47] 0x92722C851482353B, [48] 0xA2BFE8A14CF10364, [49] 0xA81A664BBC423001, [50] 0xC24B8B70D0F89791, [51] 0xC76C51A30654BE30, [52] 0xD192E819D6EF5218, [53] 0xD69906245565A910, [54] 0xF40E35855771202A, [55] 0x106AA07032BBD1B8, [56] 0x19A4C116B8D2D0C8, [57] 0x1E376C085141AB53, [58] 0x2748774CDF8EEB99, [59] 0x34B0BCB5E19B48A8, [60] 0x391C0CB3C5C95A63, [61] 0x4ED8AA4AE3418ACB, [62] 0x5B9CCA4F7763E373, [63] 0x682E6FF3D6B2B8A3, [64] 0x748F82EE5DEFB2FC, [65] 0x78A5636F43172F60, [66] 0x84C87814A1F0AB72, [67] 0x8CC702081A6439EC, [68] 0x90BEFFFA23631E28, [69] 0xA4506CEBDE82BDE9, [70] 0xBEF9A3F7B2C67915, [71] 0xC67178F2E372532B, [72] 0xCA273ECEEA26619C, [73] 0xD186B8C721C0C207, [74] 0xEADA7DD6CDE0EB1E, [75] 0xF57D4F7FEE6ED178, [76] 0x06F067AA72176FBA, [77] 0x0A637DC5A2C898A6, [78] 0x113F9804BEF90DAE, [79] 0x1B710B35131C471B, [80] 0x28DB77F523047D84, [81] 0x32CAAB7B40C72493, [82] 0x3C9EBE0A15C9BEBC, [83] 0x431D67C49C100D4C, [84] 0x4CC5D4BECB3E42B6, [85] 0x597F299CFC657E2A, [86] 0x5FCB6FAB3AD6FAEC, [87] 0x6C44198C4A475817 */
+}
+