defmodule Infer.App do
@moduledoc """
Application type matchers based on the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming))
"""
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a WASM.
See: http://webassembly.github.io/spec/core/binary/modules.html#binary-magic
## Examples
iex> binary = File.read!("test/app/sample.wasm")
iex> Infer.App.wasm?(binary)
true
iex> binary = File.read!("test/app/sample.exe")
iex> Infer.App.wasm?(binary)
false
"""
@spec wasm?(binary()) :: boolean()
def wasm?(<<0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, _rest::binary>>), do: true
def wasm?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a EXE or DLL.
DLL and EXE share the same magic number.
## Examples
iex> binary = File.read!("test/app/sample.exe")
iex> Infer.App.exe?(binary)
true
iex> binary = File.read!("test/app/sample.wasm")
iex> Infer.App.exe?(binary)
false
"""
@spec exe?(binary()) :: boolean()
def exe?(<<0x4D, 0x5A, _rest::binary>>), do: true
def exe?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a EXE or DLL.
DLL and EXE share the same magic number.
## Examples
iex> binary = File.read!("test/app/sample.exe")
iex> Infer.App.dll?(binary)
true
iex> binary = File.read!("test/app/sample.wasm")
iex> Infer.App.dll?(binary)
false
"""
@spec dll?(binary()) :: boolean()
def dll?(<<0x4D, 0x5A, _rest::binary>>), do: true
def dll?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a elf.
DLL and EXE share the same magic number.
## Examples
iex> binary = File.read!("test/app/sample_elf")
iex> Infer.App.elf?(binary)
true
iex> binary = File.read!("test/app/sample.wasm")
iex> Infer.App.elf?(binary)
false
"""
@spec elf?(binary()) :: boolean()
def elf?(<<0x7F, 0x45, 0x4C, 0x46, _rest::binary>> = binary) when byte_size(binary) > 52, do: true
def elf?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's compiled java bytecode.
"""
@spec java?(binary()) :: boolean()
def java?(<<0x43, 0x41, 0x76, 0x45, 0x42, 0x01, 0x42, 0x45, _rest::binary>>), do: true
def java?(<<0x43, 0x41, 0x76, 0x45, 0x44, 0x30, 0x30, 0x44, _rest::binary>>), do: true
def java?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's LLVM bitcode.
"""
@spec llvm?(binary()) :: boolean()
def llvm?(<<0x42, 0x43, _rest::binary>>), do: true
def llvm?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a Mach-O binary.
Mach-O binaries can be one of four variants: x86, x64, PowerPC, "Fat" (x86 + PowerPC)
See: https://ilostmynotes.blogspot.com/2014/05/mach-o-filetype-identification.html
## Examples
iex> binary = File.read!("test/app/sample_mach_fat")
iex> Infer.App.mach?(binary)
true
iex> binary = File.read!("test/app/sample_mach_ppc")
iex> Infer.App.mach?(binary)
true
iex> binary = File.read!("test/app/sample_mach_x64")
iex> Infer.App.mach?(binary)
true
iex> binary = File.read!("test/app/sample_mach_x86")
iex> Infer.App.mach?(binary)
true
iex> binary = File.read!("test/app/sample.wasm")
iex> Infer.App.mach?(binary)
false
"""
@spec mach?(binary()) :: boolean()
def mach?(<<width, 0xFA, 0xED, 0xFE, _rest::binary>>) when width in [0xCF, 0xCE], do: true
def mach?(<<0xFE, 0xED, 0xFA, width, _rest::binary>>) when width in [0xCF, 0xCE], do: true
def mach?(<<0xCA, 0xFE, 0xBA, 0xBE, _rest::binary>>), do: true
def mach?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a Dalvik Executable (DEX).
See: https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
"""
@spec dex?(binary()) :: boolean()
def dex?(<<0x64, 0x65, 0x78, 0x0A, _data::binary-size(32), 0x70, _rest::binary>>), do: true
def dex?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a Optimized Dalvik Executable (ODEX).
See: https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
"""
@spec dey?(binary()) :: boolean()
def dey?(<<0x64, 0x65, 0x79, 0x0A, _data::binary-size(36), dex::binary-size(60), _rest::binary>>), do: dex?(dex)
def dey?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a DER encoded X.509 certificate.
See: https://github.com/ReFirmLabs/binwalk/blob/master/src/binwalk/magic/crypto#L25-L37
See: https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs
## Examples
iex> binary = File.read!("test/app/sample.der")
iex> Infer.App.der?(binary)
true
iex> binary = File.read!("test/app/sample.wasm")
iex> Infer.App.der?(binary)
false
"""
@spec der?(binary()) :: boolean()
def der?(<<0x30, 0x82, _rest::binary>>), do: true
def der?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a Common Object File Format for i386 architecture.
"""
@spec coff_i386?(binary()) :: boolean()
def coff_i386?(<<0x4C, 0x01, _rest::binary>>), do: true
def coff_i386?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a Common Object File Format for x64 architecture.
"""
@spec coff_x64?(binary()) :: boolean()
def coff_x64?(<<0x64, 0x86, _rest::binary>>), do: true
def coff_x64?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a Common Object File Format for Itanium architecture.
"""
@spec coff_ia64?(binary()) :: boolean()
def coff_ia64?(<<0x00, 0x02, _rest::binary>>), do: true
def coff_ia64?(_binary), do: false
@doc """
Takes the binary file contents as arguments. Returns `true` if it's a Common Object File Format.
"""
@spec coff?(binary()) :: boolean()
def coff?(binary), do: coff_x64?(binary) || coff_i386?(binary) || coff_ia64?(binary)
end