
PowerShell-инструментарий для анализа границ обнаружения AMSI/Defender и статической триаж-сортировки вредоносных программ, сопоставляет смещения байтов с триггерами обнаружения, а также включает анализ YARA, энтропии, строк и PE/imphash. Дополнение к OffsetScan.
<p align="center">
<img src="https://assets.kitploit.com/production/public/readmes/9991/ec3b617ff761b38a1eb9296df3d62b7435bcf1684421cce77108da0b7ac12e92.png" alt="Dread Host Research" width="800">
</p>
<p align="center">
<a href="https://github.com/warpedatom/OffsetInspect/releases"><img src="https://img.shields.io/github/v/release/warpedatom/OffsetInspect" alt="Release"></a>
<a href="https://github.com/warpedatom/offsetinspect/blob/main/LICENSE"><img src="https://img.shields.io/github/license/warpedatom/OffsetInspect" alt="License"></a>
<img src="https://img.shields.io/badge/PowerShell-5.1%20%7C%207.x-5391FE" alt="PowerShell 5.1 and 7.x">
<img src="https://img.shields.io/badge/Core-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey" alt="Cross-platform core">
<img src="https://img.shields.io/badge/Threat%20Providers-Windows-0078D4" alt="Windows threat providers">
<img src="https://img.shields.io/github/actions/workflow/status/warpedatom/OffsetInspect/ci.yml?branch=main&label=CI" alt="CI">
<a href="https://github.com/warpedatom/offsetinspect/blob/main/SECURITY.md"><img src="https://img.shields.io/badge/Security-Policy-green" alt="Security policy"></a>
<a href="https://x.com/warped_atom"><img src="https://img.shields.io/badge/X-@warped__atom-000000?logo=x&logoColor=white" alt="X (Twitter): @warped_atom"></a>
</p>
# OffsetInspect
**Инструментарий PowerShell с ограниченным потреблением памяти для проверки байтовых смещений, корреляции с исходным кодом, сравнения двоичных файлов и анализа границ обнаружения в целях защиты.**
OffsetInspect отвечает на практический вопрос аналитика:
> Какое содержимое находится по этому байтовому смещению, и какой исходный или бинарный контекст его окружает?
Он также предоставляет собственный рабочий процесс определения границ обнаружения, вдохновлённый той же задачей аналитика, которую решает ThreatCheck, без включения в комплект его исходников или бинарников: он находит самый ранний префикс содержимого, который AMSI или Microsoft Defender всё ещё обнаруживает, многократно проверяет границу и передаёт полученное смещение напрямую инспектору контекста. Поверх этого ядра он добавляет набор для red-team анализа и статической триаж-сортировки — обнаружение нескольких регионов, сканирование корпуса, сравнение результатов обнаружения, корреляцию триггеров обнаружения, ведение журнала дрейфа, отчёты об испытаниях, энтропийный анализ, извлечение строк и парсинг PE/imphash — всё только для чтения, плюс тестер устойчивости сигнатур для авторизованного использования, который изменяет образцы только в памяти и никогда не отключает и не перенастраивает защиту конечных точек.
## Сопутствующий инструмент
Для статического триажа в масштабе корпуса (парсинг PE, энтропия, строки, IOC) без
накладных расходов PowerShell используйте **[OffsetScan](https://github.com/warpedatom/OffsetScan)**
— нативный бинарник на Rust с той же схемой вывода JSON. OffsetInspect 3.1.0+
принимает IOC JSON от OffsetScan напрямую через `-IocJsonPath`.
---
## Возможности
- Открывает каждый уникальный проверяемый файл через стабильный дескриптор чтения и обрабатывает все запрошенные смещения совместно.
- Использует потоковый проход с ограниченной памятью для построения карты строк, вместо повторного чтения всего файла для каждого смещения.
- Считывает только запрошенные байтовые окна для hex-вывода и сравнения.
- Сопоставляет байтовые смещения UTF-8 и UTF-16 со строками исходного кода и позициями символов.
- Реализует предыдущий и последующий контекст исходного кода через `-ContextLines`.
- Поддерживает выходные контракты: человекочитаемый, объектный, JSON, CSV и CSV-файл.
- Поддерживает планы «один ко многим», «многие к одному» и парные планы файл/смещение.
- Сравнивает целевой байт со вторым файлом без повторной загрузки этого файла.
- Добавляет независимо реализованный слой провайдеров AMSI и Microsoft Defender с явными состояниями: ошибка, таймаут, блокировка и неопределённое состояние.
- Для каждой пробы ведёт аудит (`ProbeLog`/`ProbeCount`) каждого отдельного вызова провайдера, передаваемый в реальном времени в `-Verbose`, для готовой к отчёту расшифровки фактической стоимости провайдера при сканировании.
- Обнаруживает несколько независимо детектируемых областей в одном файле с помощью сканирования AMSI в памяти (ничего из обнаруженного не записывается на диск) и сопоставляет каждую границу с абсолютным смещением.
- Сканирует корпус в консолидированную матрицу обнаружений, сравнивает результаты двух сканирований и экспортирует отчёты об испытаниях в Markdown/HTML (опционально на основе JSON нативного движка OffsetScan для панелей IOC корпусного масштаба).
- Соотносит границу обнаружения с содержимым, которое её вызвало: разделом PE, энтропией фрагмента вплоть до границы и строками, заканчивающимися на границе или пересекающими её, как кандидатами в сигнатурное содержимое.
- Ведёт журнал обнаружений во времени (хэш файла **и** локальная версия сигнатур Defender), чтобы изменение обнаруживаемости можно было отнести к файлу, обновлению базы сигнатур или недетерминированному результату провайдера.
- Проверяет устойчивость сигнатур для авторизованных испытаний, изменяя обнаруженный образец **в памяти** (регистр, конкатенация, комментарий, пробелы) и сообщая, какие классы трансформаций уклоняются от обнаружения — ни один вариант никогда не записывается на диск.
- Соотносит сканирование с генерируемой им телеметрией Windows (`-CaptureTelemetry`): было ли создано оповещение Microsoft Defender, с каким контекстом, и какие источники телеметрии оказались «слепыми» — воплощая принцип «исходи из видимости, затем проверяй её». Только чтение, без прав администратора, только Windows.
- Добавляет вспомогательные средства статической сортировки вредоносных программ: пооконная энтропия (упакованные/зашифрованные области), извлечение строк ASCII/UTF-16LE со смещениями и парсинг заголовков/разделов/импортов PE с imphash и определением overlay-данных.
- Проверяет происхождение Authenticode (`Get-OffsetSignature`): используя реальную проверку доверия платформы, сообщает, является ли файл действительным и доверенным по подписи, кто его подписал, а также является ли подпись встроенной или каталоговой, — сигнал подписанта, дополняющий imphash и отпечаток цепочки сборки (импорты против цепочки инструментов против подписанта). Только Windows.
- Никогда не изменяет исключения Defender, защиту в реальном времени или конфигурацию безопасности системы.
- Поставляется как автономный пакет PowerShell Gallery без внешних зависимостей времени выполнения; единственные необязательные исключения — сканирование YARA и ClamAV, каждое из которых требует собственный внешний движок.
## Команды
| Команда | Назначение | Платформа |
|---|---|---|
| `Invoke-OffsetInspect` | Сопоставляет байтовые смещения с контекстом исходного кода/бинарника, hex и сравнением | Кроссплатформенно |
| `Invoke-OffsetThreatScan` | Поиск границы обнаружения AMSI/Defender для одного файла | Windows |
| `Invoke-OffsetThreatScanBatch` | Сканирует корпус файлов; `-Summary` возвращает матрицу обнаружений | Windows |
| `Invoke-OffsetThreatScanRegion` | Обнаружение нескольких областей через AMSI в памяти (без записи на диск) | Windows |
| `Invoke-OffsetMutationTest` | Проверка устойчивости сигнатур: изменение обнаруженного образца в памяти, отчёт о том, какие трансформации уклоняются (только для авторизованного использования) | Windows |
| `Compare-OffsetThreatResult` | Сравнивает два результата сканирования (например, при обновлении сигнатур) | Кроссплатформенно |
| `Get-OffsetDetectionTrigger` | Соотносит границу обнаружения с содержимым, которое, вероятнее всего, её вызвало | Кроссплатформенно |
| `Add-OffsetDriftEntry` | Записывает в журнал снимок обнаружения (хэш файла + версия сигнатур Defender) | Кроссплатформенно² |
| `Get-OffsetDrift` | Объясняет, как изменилась обнаруживаемость файла: изменение файла, обновление сигнатур или недетерминированный результат | Кроссплатформенно |
| `Export-OffsetThreatReport` | Преобразует результаты сканирования в отчёт об испытаниях Markdown/HTML | Кроссплатформенно |
| `Invoke-OffsetYaraScan` | Сопоставляет файл с правилами YARA; возвращает совпадения с байтовыми смещениями | Кроссплатформенно¹ |
| `Invoke-OffsetClamScan` | Сканирует файл движком ClamAV; нормализованный результат обнаружения | Кроссплатформенно¹ |
| `Get-OffsetEntropy` | Пооконная энтропия Шеннона для поиска упакованных/зашифрованных областей | Кроссплатформенно |
| `Get-OffsetString` | Извлекает строки ASCII/UTF-16LE с байтовыми смещениями | Кроссплатформенно |
| `Get-OffsetPEInfo` | Заголовки PE, разделы, импорты/imphash, overlay, смещение→раздел | Кроссплатформенно |
| `Get-OffsetIOC` | Консолидированная панель индикаторов: хэши, энтропия, PE/imphash, строки | Кроссплатформенно |
| `Get-OffsetSignature` | Подпись Authenticode: действительно ли файл подписан и доверен, кем, встроенная или каталоговая подпись | Windows |
¹ У этих двух команд есть опциональные внешние зависимости: `Invoke-OffsetYaraScan` требует движок YARA (`winget install VirusTotal.YARA`), а `Invoke-OffsetClamScan` требует ClamAV с базами сигнатур (`winget install Cisco.ClamAV`, затем `freshclam`). Все остальные команды автономны. Здесь ClamAV — детектор для одиночных файлов, а не движок поиска границ: `clamscan` загружает полную базу при каждом вызове, поэтому для бисекции потребовался бы демон `clamd`.
² `Add-OffsetDriftEntry` ведёт журнал кроссплатформенно, но поля версии сигнатур/движка Defender заполняются только в Windows (через `Get-MpComputerStatus`); в других системах они записываются как null, а остальная часть снимка всё равно сохраняется.
Ядро проверки смещений и все вспомогательные средства статической сортировки кроссплатформенны (Windows, Linux, macOS); провайдеры угроз AMSI/Defender доступны только в Windows.
## Установка
### PowerShell Gallery```powershell
Install-Module OffsetInspect -Scope CurrentUser
Import-Module OffsetInspect
```
### Получение репозитория```powershell
git clone https://github.com/warpedatom/OffsetInspect.git
cd OffsetInspect
Import-Module ./module/OffsetInspect/OffsetInspect.psd1 -Force
```
Репозиторий также включает тонкие CLI-обёртки:```powershell
./OffsetInspect.ps1 <file> <offset>
./OffsetThreatScan.ps1 <file> -Engine AMSI
```
## Проверка смещения
### Человекочитаемый вывод```powershell
Invoke-OffsetInspect ./sample.bin 0x200
```
$ ls -l ~/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2```powershell
$inspectParameters = @{
FilePaths = './script.ps1'
OffsetInputs = 128, 256, 512
ByteWindow = 64
ContextLines = 4
}
Invoke-OffsetInspect @inspectParameters
```
### Структурированные объекты```powershell
$inspectParameters = @{
FilePaths = './script.ps1'
OffsetInputs = 0x80, 0x100
PassThru = $true
}
$results = Invoke-OffsetInspect @inspectParameters
$results | Where-Object BytesDiffer
```
### JSON и CSV```powershell
Invoke-OffsetInspect ./sample.bin 0x200 -Json
Invoke-OffsetInspect ./sample.bin 0x200 -Csv
Invoke-OffsetInspect ./sample.bin 0x200 -CsvPath ./artifacts/offsets.csv
```
Режим JSON всегда выводит массив, в том числе для одного результата.
### Бинарное сравнение```powershell
$compareParameters = @{
FilePaths = './before.bin'
OffsetInputs = 0x200
CompareFile = './after.bin'
PassThru = $true
}
Invoke-OffsetInspect @compareParameters
```
### Форматы смещений
| Вход | Интерпретация |
|---|---:|
| `512` | Десятичное 512 |
| `0x200` или `0X200` | Шестнадцатеричное 0x200 |
| `200h` | Шестнадцатеричное 0x200 |
| `E1AB1` | Шестнадцатеричное без префикса, поскольку содержит A-F |
Числовые значения без префикса или суффикса намеренно интерпретируются как десятичные.
### Режимы кодировки
| Режим | Поведение |
|---|---|
| `Auto` | Обнаруживает BOM UTF-8/UTF-16; в противном случае используется UTF-8 |
| `Default` | Использует кодировку по умолчанию операционной системы хоста |
| `UTF8` | Сопоставление исходного текста UTF-8 |
| `UTF16LE` | Сопоставление исходного текста UTF-16 с прямым порядком байт |
| `UTF16BE` | Сопоставление исходного текста UTF-16 с обратным порядком байт |
| `ASCII` | Сопоставление исходного текста ASCII |
В выводе указываются как `BytePositionInLine`, так и `CharacterPosition`. Это различие важно, когда исходный файл содержит многобайтовые символы.
## Анализ границ угроз
Анализ через поставщика угроз доступен только в Windows. Обычная команда проверки смещений остаётся кроссплатформенной.
### Сканирование текста AMSI```powershell
$scanParameters = @{
FilePath = './script.ps1'
Engine = 'AMSI'
ScanMode = 'Text'
RepeatCount = 3
PassThru = $true
}
$result = Invoke-OffsetThreatScan @scanParameters
```
### AMSI-сканирование в режиме сырых байтов
Текстовый режим использует `AmsiScanString`, ищет префиксы, состоящие из скалярных значений Unicode, не разбивая суррогатные пары, пропускает обнаруженный префикс через проверенную кодировку источника и возвращает индексы скалярных значений Unicode, кодовых единиц UTF-16 и байтов исходного файла. Встроенные NUL-символы в текстовом режиме отклоняются; для таких файлов используйте режим сырых байтов.```powershell
Invoke-OffsetThreatScan ./content.bin -Engine AMSI -ScanMode RawBytes
```
### Сканирование Microsoft Defender```powershell
$scanParameters = @{
FilePath = './sample.bin'
Engine = 'Defender'
RepeatCount = 3
TimeoutSeconds = 45
}
Invoke-OffsetThreatScan @scanParameters
```
Провайдер Defender:
- Определяет путь к платформе последнего установленного `MpCmdRun.exe`.
- Записывает префиксы в уникальный временный каталог пользователя.
- Использует специальное сканирование с `-DisableRemediation`.
- Рассматривает тайм-ауты, ошибки провайдера, локализованный/неизвестный вывод и неоднозначные маркеры как неопределённые.
- Удаляет временное рабочее пространство по завершении сканирования.
### Семантика границы
Результат вида `DetectionPrefixLength = 841` означает:
- Префикс длиной 840 был классифицирован как чистый/не обнаруженный.
- Префикс длиной 841 был классифицирован как обнаруженный/заблокированный.
- Повторные проверки определяют, стабилен ли этот переход.
Это **не** доказывает, что байт 840 является полной сигнатурой, единственным влияющим байтом или полным вредоносным диапазоном. Решения антивируса могут зависеть от токенизации, окружающего контекста, типа файла, состояния провайдера и обновлений сигнатур.
### Пример из практики: два движка, один файл
Сканирование одного и того же образца (PowerUp.ps1, публичный скрипт red-team, 445 954 байта) обоими провайдерами показывает, что такое граница и насколько можно доверять согласованности двух движков. AMSI в текстовом режиме:```text
Threat boundary scan: C:\Ops\Samples\PowerUp.ps1
SHA-256: 7abc87d9620aef493617a4fc1f823850f32fb26ca9ae0f3befeadb04971e0246
Engine: AMSI
Scan mode: Text
Initial status: Detected
Scans performed: 25
Provider probes: 25 (see -Verbose or the ProbeLog property for the full audit trail)
Duration: 22771.419 ms
Known clean prefix: 445953
Detected prefix: 445954
Boundary offset: 445953 (0x6CE01)
Unicode scalar index: 445953
UTF-16 code-unit idx: 445953
Stable: True
Confidence: High
Line number: 4586
Byte in line: 46
Target byte: 0A (10)
--- Source Context ---
4585 | Set-Alias Get-CurrentUserTokenGroupSid Get-ProcessTokenGroup
4586 | Set-Alias Invoke-AllChecks Invoke-PrivescAudit
^
```
Microsoft Defender в режиме raw-byte, тот же файл:```text
Engine: Defender
Scan mode: RawBytes
Initial status: Detected
Scans performed: 25
Duration: 15370.562 ms
Known clean prefix: 445951
Detected prefix: 445952
Boundary offset: 445951 (0x6CDFF)
Stable: True
Confidence: High
Signature: Trojan:Win32/Kepavll!rfn
Line number: 4586
Byte in line: 44
Target byte: 69 (105)
--- Hex Dump ---
0006CDBF 74 2D 50 72 6F 63 65 73 73 54 6F 6B 65 6E 47 72 t-ProcessTokenGr
0006CDCF 6F 75 70 0A 53 65 74 2D 41 6C 69 61 73 20 49 6E oup.Set-Alias In
0006CDDF 76 6F 6B 65 2D 41 6C 6C 43 68 65 63 6B 73 20 49 voke-AllChecks I
0006CDEF 6E 76 6F 6B 65 2D 50 72 69 76 65 73 63 41 75 64 nvoke-PrivescAud
0006CDFF 69 74 0A it.
```
Оба движка сходятся на **строке 4586** — граница обнаружения Defender попадает внутрь хвостового `it` в `Invoke-PrivescAudit`, а граница AMSI — на символ новой строки, завершающий ту же строку, двумя байтами позже. Ни одно из смещений не является «сигнатурой»: это самые ранние префиксы, которые каждый провайдер всё ещё помечает, а расхождение в два байта — ровно тот эффект токенизации/контекста, что описан выше. Defender дополнительно называет то, с чем произошло совпадение (`Trojan:Win32/Kepavll!rfn`); AMSI не сообщает имени сигнатуры, именно поэтому существуют `Invoke-OffsetThreatScanRegion` и `Get-OffsetDetectionTrigger` для характеристики срабатывания AMSI.
Оба сканирования обошлись в 25 проб провайдера для файла размером ~436 КиБ — биссекция логарифмична по размеру файла, и каждая проба фиксируется в `ProbeLog`.
См. [проектирование сканирования угроз](https://github.com/warpedatom/offsetinspect/blob/main/docs/THREAT-SCANNING.md) — контракт провайдера и рекомендации по интерпретации, [интерфейс провайдера](https://github.com/warpedatom/offsetinspect/blob/main/docs/PROVIDER-INTERFACE.md) — контракт сканера и о том, как добавить провайдера, не затрагивая ядро поиска, [происхождение результатов сканирования угроз](https://github.com/warpedatom/offsetinspect/blob/main/docs/PROVENANCE.md) — границы реализации и атрибуцию, а также [схему выходных данных](https://github.com/warpedatom/offsetinspect/blob/main/docs/OUTPUT-SCHEMA.md) — версионируемый контракт объектов.
### Отчёты о границах обнаружения
`Export-OffsetThreatReport` превращает один или несколько результатов сканирования в автономный отчёт Markdown или HTML — сводку по каждому файлу, метаданные провайдера/сигнатуры/движка, полный журнал аудита `ProbeLog` и предупреждения — для прикрепления к отчёту о тестировании. Он только читает результаты и никогда не пересканирует, поэтому работает кроссплатформенно. Добавьте `-IncludeIoc`, чтобы включить панель индикаторов хеша/энтропии/PE (те же данные, что и `Get-OffsetIOC`) в каждую запись отчёта, и `-IncludeTrigger`, чтобы добавить анализ триггера обнаружения (см. ниже) для каждого результата с границей.```powershell
Invoke-OffsetThreatScan ./sample.ps1 -Engine AMSI -ScanMode Text -PassThru |
Export-OffsetThreatReport -Path ./report.html -Format Html
# Aggregate many scans into one report, with an indicators panel and trigger analysis per file:
$results | Export-OffsetThreatReport -Path ./engagement.md -IncludeIoc -IncludeTrigger
```
Для отчётов корпусного масштаба `-IncludeIoc` повторно сканирует каждый файл в PowerShell, что медленно. Вспомогательный нативный движок [OffsetScan](https://github.com/warpedatom/OffsetScan) генерирует схематически идентичный IOC JSON гораздо быстрее; укажите на него в отчёте с помощью `-IocJsonPath`, и он будет брать данные для каждой панели из этого JSON (возвращаясь к живому `Get-OffsetIOC` только для файлов, отсутствующих в нём):```powershell
offsetscan ioc ./corpus --recurse > ./ioc.json
$results | Export-OffsetThreatReport -Path ./engagement.md -IocJsonPath ./ioc.json
```
### Пакетное / корпусное сканирование
`Invoke-OffsetThreatScanBatch` раскрывает файлы, каталоги и подстановочные знаки в список файлов, сканирует каждый (продолжая работу, несмотря на сбои по отдельным файлам) и возвращает по одному результату на файл. `-Summary` возвращает сводную матрицу обнаружений; полные результаты передаются напрямую в генератор отчётов. Сканирование провайдеров доступно только в Windows.```powershell
Invoke-OffsetThreatScanBatch ./payloads -Recurse -Engine AMSI |
Export-OffsetThreatReport -Path ./engagement.html -Format Html
Invoke-OffsetThreatScanBatch ./samples -Summary |
Format-Table File, DetectionPrefixLength, Confidence, ProbeCount
```
### Дифф обнаружения / регрессия
`Compare-OffsetThreatResult` сравнивает два результата сканирования — например, один и тот же файл до и после обновления сигнатур — и классифицирует изменение (`NewlyDetected`, `NoLongerDetected`, `BoundaryEarlier`, `BoundaryLater`, `BoundaryUnchanged`, `BothClean`) с указанием дельты границы и изменённых полей.```powershell
$before = Invoke-OffsetThreatScan ./sample.ps1 -Engine Defender -PassThru
# ... update Defender signature definitions ...
$after = Invoke-OffsetThreatScan ./sample.ps1 -Engine Defender -PassThru
Compare-OffsetThreatResult -Reference $before -Difference $after
```
### Multi-region discovery
The prefix search finds the *first* detection boundary. `Invoke-OffsetThreatScanRegion` finds *multiple* independently-detectable regions by splitting the file into segments and scanning each in isolation through AMSI **entirely in memory** - nothing detected is written to disk, so Defender real-time protection is never triggered or reconfigured. Each hit is bisected within its segment to map the exact triggering boundary to an absolute file offset.
---
### Обнаружение нескольких регионов
Префиксный поиск находит *первую* границу обнаружения. `Invoke-OffsetThreatScanRegion` находит *несколько* независимо обнаруживаемых регионов, разбивая файл на сегменты и сканируя каждый по отдельности через AMSI **полностью в памяти** — ничто из обнаруженного не записывается на диск, поэтому защита в реальном времени Defender никогда не срабатывает и не перенастраивается. Каждое срабатывание бисекционно делится в пределах своего сегмента, чтобы сопоставить точную границу срабатывания с абсолютным смещением в файле.```powershell
Invoke-OffsetThreatScanRegion ./payload.bin -SegmentCount 16 |
Select-Object -ExpandProperty DetectedRegions |
Format-Table SegmentIndex, StartOffset, EndOffset, AbsoluteBoundaryOffset, SignatureName
```
---
[Read more](https://github.com/warpedatom/offsetinspect)