feat: Add native binary analyzer test utilities and implement SM2 signing tests
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
- Introduced `NativeTestBase` class for ELF, PE, and Mach-O binary parsing helpers and assertions. - Created `TestCryptoFactory` for SM2 cryptographic provider setup and key generation. - Implemented `Sm2SigningTests` to validate signing functionality with environment gate checks. - Developed console export service and store with comprehensive unit tests for export status management.
This commit is contained in:
@@ -1,20 +1,19 @@
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Scanner.Analyzers.Native;
|
||||
using StellaOps.Scanner.Analyzers.Native.Tests.Fixtures;
|
||||
using StellaOps.Scanner.Analyzers.Native.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Native.Tests;
|
||||
|
||||
public class ElfDynamicSectionParserTests
|
||||
public class ElfDynamicSectionParserTests : NativeTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ParsesMinimalElfWithNoDynamicSection()
|
||||
{
|
||||
// Minimal ELF64 with no program headers (static binary scenario)
|
||||
var buffer = new byte[64];
|
||||
SetupElf64Header(buffer, littleEndian: true);
|
||||
// Minimal ELF64 with no dependencies (static binary scenario)
|
||||
var elf = ElfBuilder.Static().Build();
|
||||
|
||||
using var stream = new MemoryStream(buffer);
|
||||
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
|
||||
var result = TryParseElf(elf, out var info);
|
||||
|
||||
result.Should().BeTrue();
|
||||
info.Dependencies.Should().BeEmpty();
|
||||
@@ -25,72 +24,13 @@ public class ElfDynamicSectionParserTests
|
||||
[Fact]
|
||||
public void ParsesElfWithDtNeeded()
|
||||
{
|
||||
// Build a minimal ELF64 with PT_DYNAMIC containing DT_NEEDED entries
|
||||
var buffer = new byte[2048];
|
||||
SetupElf64Header(buffer, littleEndian: true);
|
||||
// Build ELF with DT_NEEDED entries using the builder
|
||||
var elf = ElfBuilder.LinuxX64()
|
||||
.AddDependencies("libc.so.6", "libm.so.6", "libpthread.so.0")
|
||||
.Build();
|
||||
|
||||
// String table at offset 0x400
|
||||
var strtab = 0x400;
|
||||
var str1Offset = 1; // Skip null byte at start
|
||||
var str2Offset = str1Offset + WriteString(buffer, strtab + str1Offset, "libc.so.6") + 1;
|
||||
var str3Offset = str2Offset + WriteString(buffer, strtab + str2Offset, "libm.so.6") + 1;
|
||||
var strtabSize = str3Offset + WriteString(buffer, strtab + str3Offset, "libpthread.so.0") + 1;
|
||||
var info = ParseElf(elf);
|
||||
|
||||
// Section headers at offset 0x600
|
||||
var shoff = 0x600;
|
||||
var shentsize = 64; // Elf64_Shdr size
|
||||
var shnum = 2; // null + .dynstr
|
||||
|
||||
// Update ELF header with section header info
|
||||
BitConverter.GetBytes((ulong)shoff).CopyTo(buffer, 40); // e_shoff
|
||||
BitConverter.GetBytes((ushort)shentsize).CopyTo(buffer, 58); // e_shentsize
|
||||
BitConverter.GetBytes((ushort)shnum).CopyTo(buffer, 60); // e_shnum
|
||||
|
||||
// Section header 0: null section
|
||||
// Section header 1: .dynstr (type SHT_STRTAB = 3)
|
||||
var sh1 = shoff + shentsize;
|
||||
BitConverter.GetBytes((uint)3).CopyTo(buffer, sh1 + 4); // sh_type = SHT_STRTAB
|
||||
BitConverter.GetBytes((ulong)0x400).CopyTo(buffer, sh1 + 16); // sh_addr (virtual address)
|
||||
BitConverter.GetBytes((ulong)strtab).CopyTo(buffer, sh1 + 24); // sh_offset (file offset)
|
||||
BitConverter.GetBytes((ulong)strtabSize).CopyTo(buffer, sh1 + 32); // sh_size
|
||||
|
||||
// Dynamic section at offset 0x200
|
||||
var dynOffset = 0x200;
|
||||
var dynEntrySize = 16; // Elf64_Dyn size
|
||||
var dynIndex = 0;
|
||||
|
||||
// DT_STRTAB
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 5, 0x400); // DT_STRTAB = 5
|
||||
// DT_STRSZ
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 10, (ulong)strtabSize); // DT_STRSZ = 10
|
||||
// DT_NEEDED entries
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 1, (ulong)str1Offset); // libc.so.6
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 1, (ulong)str2Offset); // libm.so.6
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 1, (ulong)str3Offset); // libpthread.so.0
|
||||
// DT_NULL
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex, 0, 0);
|
||||
|
||||
var dynSize = dynEntrySize * (dynIndex + 1);
|
||||
|
||||
// Program header at offset 0x40 (right after ELF header)
|
||||
var phoff = 0x40;
|
||||
var phentsize = 56; // Elf64_Phdr size
|
||||
var phnum = 1;
|
||||
|
||||
// Update ELF header with program header info
|
||||
BitConverter.GetBytes((ulong)phoff).CopyTo(buffer, 32); // e_phoff
|
||||
BitConverter.GetBytes((ushort)phentsize).CopyTo(buffer, 54); // e_phentsize
|
||||
BitConverter.GetBytes((ushort)phnum).CopyTo(buffer, 56); // e_phnum
|
||||
|
||||
// PT_DYNAMIC program header
|
||||
BitConverter.GetBytes((uint)2).CopyTo(buffer, phoff); // p_type = PT_DYNAMIC
|
||||
BitConverter.GetBytes((ulong)dynOffset).CopyTo(buffer, phoff + 8); // p_offset
|
||||
BitConverter.GetBytes((ulong)dynSize).CopyTo(buffer, phoff + 32); // p_filesz
|
||||
|
||||
using var stream = new MemoryStream(buffer);
|
||||
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
|
||||
|
||||
result.Should().BeTrue();
|
||||
info.Dependencies.Should().HaveCount(3);
|
||||
info.Dependencies[0].Soname.Should().Be("libc.so.6");
|
||||
info.Dependencies[0].ReasonCode.Should().Be("elf-dtneeded");
|
||||
@@ -101,60 +41,14 @@ public class ElfDynamicSectionParserTests
|
||||
[Fact]
|
||||
public void ParsesElfWithRpathAndRunpath()
|
||||
{
|
||||
var buffer = new byte[2048];
|
||||
SetupElf64Header(buffer, littleEndian: true);
|
||||
// Build ELF with rpath and runpath using the builder
|
||||
var elf = ElfBuilder.LinuxX64()
|
||||
.WithRpath("/opt/lib", "/usr/local/lib")
|
||||
.WithRunpath("$ORIGIN/../lib")
|
||||
.Build();
|
||||
|
||||
// String table at offset 0x400
|
||||
var strtab = 0x400;
|
||||
var rpathOffset = 1;
|
||||
var runpathOffset = rpathOffset + WriteString(buffer, strtab + rpathOffset, "/opt/lib:/usr/local/lib") + 1;
|
||||
var strtabSize = runpathOffset + WriteString(buffer, strtab + runpathOffset, "$ORIGIN/../lib") + 1;
|
||||
var info = ParseElf(elf);
|
||||
|
||||
// Section headers
|
||||
var shoff = 0x600;
|
||||
var shentsize = 64;
|
||||
var shnum = 2;
|
||||
|
||||
BitConverter.GetBytes((ulong)shoff).CopyTo(buffer, 40);
|
||||
BitConverter.GetBytes((ushort)shentsize).CopyTo(buffer, 58);
|
||||
BitConverter.GetBytes((ushort)shnum).CopyTo(buffer, 60);
|
||||
|
||||
var sh1 = shoff + shentsize;
|
||||
BitConverter.GetBytes((uint)3).CopyTo(buffer, sh1 + 4);
|
||||
BitConverter.GetBytes((ulong)0x400).CopyTo(buffer, sh1 + 16);
|
||||
BitConverter.GetBytes((ulong)strtab).CopyTo(buffer, sh1 + 24);
|
||||
BitConverter.GetBytes((ulong)strtabSize).CopyTo(buffer, sh1 + 32);
|
||||
|
||||
// Dynamic section at offset 0x200
|
||||
var dynOffset = 0x200;
|
||||
var dynEntrySize = 16;
|
||||
var dynIndex = 0;
|
||||
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 5, 0x400); // DT_STRTAB
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 10, (ulong)strtabSize); // DT_STRSZ
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 15, (ulong)rpathOffset); // DT_RPATH
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 29, (ulong)runpathOffset); // DT_RUNPATH
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex, 0, 0); // DT_NULL
|
||||
|
||||
var dynSize = dynEntrySize * (dynIndex + 1);
|
||||
|
||||
// Program header
|
||||
var phoff = 0x40;
|
||||
var phentsize = 56;
|
||||
var phnum = 1;
|
||||
|
||||
BitConverter.GetBytes((ulong)phoff).CopyTo(buffer, 32);
|
||||
BitConverter.GetBytes((ushort)phentsize).CopyTo(buffer, 54);
|
||||
BitConverter.GetBytes((ushort)phnum).CopyTo(buffer, 56);
|
||||
|
||||
BitConverter.GetBytes((uint)2).CopyTo(buffer, phoff);
|
||||
BitConverter.GetBytes((ulong)dynOffset).CopyTo(buffer, phoff + 8);
|
||||
BitConverter.GetBytes((ulong)dynSize).CopyTo(buffer, phoff + 32);
|
||||
|
||||
using var stream = new MemoryStream(buffer);
|
||||
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
|
||||
|
||||
result.Should().BeTrue();
|
||||
info.Rpath.Should().BeEquivalentTo(["/opt/lib", "/usr/local/lib"]);
|
||||
info.Runpath.Should().BeEquivalentTo(["$ORIGIN/../lib"]);
|
||||
}
|
||||
@@ -162,49 +56,13 @@ public class ElfDynamicSectionParserTests
|
||||
[Fact]
|
||||
public void ParsesElfWithInterpreterAndBuildId()
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
SetupElf64Header(buffer, littleEndian: true);
|
||||
// Build ELF with interpreter and build ID using the builder
|
||||
var elf = ElfBuilder.LinuxX64()
|
||||
.WithBuildId("deadbeef0102030405060708090a0b0c")
|
||||
.Build();
|
||||
|
||||
// Program headers at offset 0x40
|
||||
var phoff = 0x40;
|
||||
var phentsize = 56;
|
||||
var phnum = 2;
|
||||
var info = ParseElf(elf);
|
||||
|
||||
BitConverter.GetBytes((ulong)phoff).CopyTo(buffer, 32);
|
||||
BitConverter.GetBytes((ushort)phentsize).CopyTo(buffer, 54);
|
||||
BitConverter.GetBytes((ushort)phnum).CopyTo(buffer, 56);
|
||||
|
||||
// PT_INTERP
|
||||
var ph0 = phoff;
|
||||
var interpOffset = 0x200;
|
||||
var interpData = "/lib64/ld-linux-x86-64.so.2\0"u8;
|
||||
|
||||
BitConverter.GetBytes((uint)3).CopyTo(buffer, ph0); // p_type = PT_INTERP
|
||||
BitConverter.GetBytes((ulong)interpOffset).CopyTo(buffer, ph0 + 8); // p_offset
|
||||
BitConverter.GetBytes((ulong)interpData.Length).CopyTo(buffer, ph0 + 32); // p_filesz
|
||||
interpData.CopyTo(buffer.AsSpan(interpOffset));
|
||||
|
||||
// PT_NOTE with GNU build-id
|
||||
var ph1 = phoff + phentsize;
|
||||
var noteOffset = 0x300;
|
||||
|
||||
BitConverter.GetBytes((uint)4).CopyTo(buffer, ph1); // p_type = PT_NOTE
|
||||
BitConverter.GetBytes((ulong)noteOffset).CopyTo(buffer, ph1 + 8); // p_offset
|
||||
BitConverter.GetBytes((ulong)32).CopyTo(buffer, ph1 + 32); // p_filesz
|
||||
|
||||
// Build note structure
|
||||
BitConverter.GetBytes((uint)4).CopyTo(buffer, noteOffset); // namesz
|
||||
BitConverter.GetBytes((uint)16).CopyTo(buffer, noteOffset + 4); // descsz
|
||||
BitConverter.GetBytes((uint)3).CopyTo(buffer, noteOffset + 8); // type = NT_GNU_BUILD_ID
|
||||
"GNU\0"u8.CopyTo(buffer.AsSpan(noteOffset + 12)); // name
|
||||
var buildIdBytes = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C };
|
||||
buildIdBytes.CopyTo(buffer, noteOffset + 16);
|
||||
|
||||
using var stream = new MemoryStream(buffer);
|
||||
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
|
||||
|
||||
result.Should().BeTrue();
|
||||
info.Interpreter.Should().Be("/lib64/ld-linux-x86-64.so.2");
|
||||
info.BinaryId.Should().Be("deadbeef0102030405060708090a0b0c");
|
||||
}
|
||||
@@ -212,57 +70,17 @@ public class ElfDynamicSectionParserTests
|
||||
[Fact]
|
||||
public void DeduplicatesDtNeededEntries()
|
||||
{
|
||||
var buffer = new byte[2048];
|
||||
SetupElf64Header(buffer, littleEndian: true);
|
||||
// ElfBuilder deduplicates internally, so add "duplicates" via builder
|
||||
// The builder will produce correct output, and we verify the parser handles it
|
||||
var elf = ElfBuilder.LinuxX64()
|
||||
.AddDependency("libc.so.6")
|
||||
.AddDependency("libc.so.6") // Duplicate - builder should handle this
|
||||
.AddDependency("libc.so.6") // Triple duplicate
|
||||
.Build();
|
||||
|
||||
var strtab = 0x400;
|
||||
var str1Offset = 1;
|
||||
var strtabSize = str1Offset + WriteString(buffer, strtab + str1Offset, "libc.so.6") + 1;
|
||||
var info = ParseElf(elf);
|
||||
|
||||
var shoff = 0x600;
|
||||
var shentsize = 64;
|
||||
var shnum = 2;
|
||||
|
||||
BitConverter.GetBytes((ulong)shoff).CopyTo(buffer, 40);
|
||||
BitConverter.GetBytes((ushort)shentsize).CopyTo(buffer, 58);
|
||||
BitConverter.GetBytes((ushort)shnum).CopyTo(buffer, 60);
|
||||
|
||||
var sh1 = shoff + shentsize;
|
||||
BitConverter.GetBytes((uint)3).CopyTo(buffer, sh1 + 4);
|
||||
BitConverter.GetBytes((ulong)0x400).CopyTo(buffer, sh1 + 16);
|
||||
BitConverter.GetBytes((ulong)strtab).CopyTo(buffer, sh1 + 24);
|
||||
BitConverter.GetBytes((ulong)strtabSize).CopyTo(buffer, sh1 + 32);
|
||||
|
||||
var dynOffset = 0x200;
|
||||
var dynEntrySize = 16;
|
||||
var dynIndex = 0;
|
||||
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 5, 0x400);
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 10, (ulong)strtabSize);
|
||||
// Duplicate DT_NEEDED entries for same library
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 1, (ulong)str1Offset);
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 1, (ulong)str1Offset);
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 1, (ulong)str1Offset);
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex, 0, 0);
|
||||
|
||||
var dynSize = dynEntrySize * (dynIndex + 1);
|
||||
|
||||
var phoff = 0x40;
|
||||
var phentsize = 56;
|
||||
var phnum = 1;
|
||||
|
||||
BitConverter.GetBytes((ulong)phoff).CopyTo(buffer, 32);
|
||||
BitConverter.GetBytes((ushort)phentsize).CopyTo(buffer, 54);
|
||||
BitConverter.GetBytes((ushort)phnum).CopyTo(buffer, 56);
|
||||
|
||||
BitConverter.GetBytes((uint)2).CopyTo(buffer, phoff);
|
||||
BitConverter.GetBytes((ulong)dynOffset).CopyTo(buffer, phoff + 8);
|
||||
BitConverter.GetBytes((ulong)dynSize).CopyTo(buffer, phoff + 32);
|
||||
|
||||
using var stream = new MemoryStream(buffer);
|
||||
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
|
||||
|
||||
result.Should().BeTrue();
|
||||
// Whether builder deduplicates or not, parser should return unique deps
|
||||
info.Dependencies.Should().HaveCount(1);
|
||||
info.Dependencies[0].Soname.Should().Be("libc.so.6");
|
||||
}
|
||||
@@ -291,136 +109,47 @@ public class ElfDynamicSectionParserTests
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
private static void SetupElf64Header(byte[] buffer, bool littleEndian)
|
||||
{
|
||||
// ELF magic
|
||||
buffer[0] = 0x7F;
|
||||
buffer[1] = (byte)'E';
|
||||
buffer[2] = (byte)'L';
|
||||
buffer[3] = (byte)'F';
|
||||
buffer[4] = 0x02; // 64-bit
|
||||
buffer[5] = littleEndian ? (byte)0x01 : (byte)0x02;
|
||||
buffer[6] = 0x01; // ELF version
|
||||
buffer[7] = 0x00; // System V ABI
|
||||
// e_type at offset 16 (2 bytes)
|
||||
buffer[16] = 0x02; // ET_EXEC
|
||||
// e_machine at offset 18 (2 bytes)
|
||||
buffer[18] = 0x3E; // x86_64
|
||||
}
|
||||
|
||||
private static void WriteDynEntry64(byte[] buffer, int offset, ulong tag, ulong val)
|
||||
{
|
||||
BitConverter.GetBytes(tag).CopyTo(buffer, offset);
|
||||
BitConverter.GetBytes(val).CopyTo(buffer, offset + 8);
|
||||
}
|
||||
|
||||
private static int WriteString(byte[] buffer, int offset, string str)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(str);
|
||||
bytes.CopyTo(buffer, offset);
|
||||
buffer[offset + bytes.Length] = 0; // null terminator
|
||||
return bytes.Length;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesElfWithVersionNeeds()
|
||||
{
|
||||
// Test that version needs (GLIBC_2.17, etc.) are properly extracted
|
||||
var buffer = new byte[4096];
|
||||
SetupElf64Header(buffer, littleEndian: true);
|
||||
var elf = ElfBuilder.LinuxX64()
|
||||
.AddDependency("libc.so.6")
|
||||
.AddVersionNeed("libc.so.6", "GLIBC_2.17", isWeak: false)
|
||||
.AddVersionNeed("libc.so.6", "GLIBC_2.28", isWeak: false)
|
||||
.Build();
|
||||
|
||||
// String table at offset 0x400
|
||||
var strtab = 0x400;
|
||||
var libcOffset = 1; // "libc.so.6"
|
||||
var glibc217Offset = libcOffset + WriteString(buffer, strtab + libcOffset, "libc.so.6") + 1;
|
||||
var glibc228Offset = glibc217Offset + WriteString(buffer, strtab + glibc217Offset, "GLIBC_2.17") + 1;
|
||||
var strtabSize = glibc228Offset + WriteString(buffer, strtab + glibc228Offset, "GLIBC_2.28") + 1;
|
||||
var info = ParseElf(elf);
|
||||
|
||||
// Section headers at offset 0x800
|
||||
var shoff = 0x800;
|
||||
var shentsize = 64;
|
||||
var shnum = 3; // null + .dynstr + .gnu.version_r
|
||||
|
||||
BitConverter.GetBytes((ulong)shoff).CopyTo(buffer, 40);
|
||||
BitConverter.GetBytes((ushort)shentsize).CopyTo(buffer, 58);
|
||||
BitConverter.GetBytes((ushort)shnum).CopyTo(buffer, 60);
|
||||
|
||||
// Section header 0: null
|
||||
// Section header 1: .dynstr
|
||||
var sh1 = shoff + shentsize;
|
||||
BitConverter.GetBytes((uint)3).CopyTo(buffer, sh1 + 4); // sh_type = SHT_STRTAB
|
||||
BitConverter.GetBytes((ulong)0x400).CopyTo(buffer, sh1 + 16); // sh_addr
|
||||
BitConverter.GetBytes((ulong)strtab).CopyTo(buffer, sh1 + 24); // sh_offset
|
||||
BitConverter.GetBytes((ulong)strtabSize).CopyTo(buffer, sh1 + 32); // sh_size
|
||||
|
||||
// Section header 2: .gnu.version_r (SHT_GNU_verneed = 0x6ffffffe)
|
||||
var verneedFileOffset = 0x600;
|
||||
var sh2 = shoff + shentsize * 2;
|
||||
BitConverter.GetBytes((uint)0x6ffffffe).CopyTo(buffer, sh2 + 4); // sh_type = SHT_GNU_verneed
|
||||
BitConverter.GetBytes((ulong)0x600).CopyTo(buffer, sh2 + 16); // sh_addr (vaddr)
|
||||
BitConverter.GetBytes((ulong)verneedFileOffset).CopyTo(buffer, sh2 + 24); // sh_offset
|
||||
|
||||
// Version needs section at offset 0x600
|
||||
// Verneed entry for libc.so.6 with two version requirements
|
||||
// Elf64_Verneed: vn_version(2), vn_cnt(2), vn_file(4), vn_aux(4), vn_next(4)
|
||||
var verneedOffset = verneedFileOffset;
|
||||
BitConverter.GetBytes((ushort)1).CopyTo(buffer, verneedOffset); // vn_version = 1
|
||||
BitConverter.GetBytes((ushort)2).CopyTo(buffer, verneedOffset + 2); // vn_cnt = 2 aux entries
|
||||
BitConverter.GetBytes((uint)libcOffset).CopyTo(buffer, verneedOffset + 4); // vn_file -> "libc.so.6"
|
||||
BitConverter.GetBytes((uint)16).CopyTo(buffer, verneedOffset + 8); // vn_aux = 16 (offset to first aux)
|
||||
BitConverter.GetBytes((uint)0).CopyTo(buffer, verneedOffset + 12); // vn_next = 0 (last entry)
|
||||
|
||||
// Vernaux entries
|
||||
// Elf64_Vernaux: vna_hash(4), vna_flags(2), vna_other(2), vna_name(4), vna_next(4)
|
||||
var aux1Offset = verneedOffset + 16;
|
||||
BitConverter.GetBytes((uint)0x0d696910).CopyTo(buffer, aux1Offset); // vna_hash for GLIBC_2.17
|
||||
BitConverter.GetBytes((ushort)0).CopyTo(buffer, aux1Offset + 4); // vna_flags
|
||||
BitConverter.GetBytes((ushort)2).CopyTo(buffer, aux1Offset + 6); // vna_other
|
||||
BitConverter.GetBytes((uint)glibc217Offset).CopyTo(buffer, aux1Offset + 8); // vna_name -> "GLIBC_2.17"
|
||||
BitConverter.GetBytes((uint)16).CopyTo(buffer, aux1Offset + 12); // vna_next = 16 (offset to next aux)
|
||||
|
||||
var aux2Offset = aux1Offset + 16;
|
||||
BitConverter.GetBytes((uint)0x09691974).CopyTo(buffer, aux2Offset); // vna_hash for GLIBC_2.28
|
||||
BitConverter.GetBytes((ushort)0).CopyTo(buffer, aux2Offset + 4);
|
||||
BitConverter.GetBytes((ushort)3).CopyTo(buffer, aux2Offset + 6);
|
||||
BitConverter.GetBytes((uint)glibc228Offset).CopyTo(buffer, aux2Offset + 8); // vna_name -> "GLIBC_2.28"
|
||||
BitConverter.GetBytes((uint)0).CopyTo(buffer, aux2Offset + 12); // vna_next = 0 (last aux)
|
||||
|
||||
// Dynamic section at offset 0x200
|
||||
var dynOffset = 0x200;
|
||||
var dynEntrySize = 16;
|
||||
var dynIndex = 0;
|
||||
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 5, 0x400); // DT_STRTAB
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 10, (ulong)strtabSize); // DT_STRSZ
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 1, (ulong)libcOffset); // DT_NEEDED -> libc.so.6
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 0x6ffffffe, 0x600); // DT_VERNEED (vaddr)
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex++, 0x6fffffff, 1); // DT_VERNEEDNUM = 1
|
||||
WriteDynEntry64(buffer, dynOffset + dynEntrySize * dynIndex, 0, 0); // DT_NULL
|
||||
|
||||
var dynSize = dynEntrySize * (dynIndex + 1);
|
||||
|
||||
// Program header
|
||||
var phoff = 0x40;
|
||||
var phentsize = 56;
|
||||
var phnum = 1;
|
||||
|
||||
BitConverter.GetBytes((ulong)phoff).CopyTo(buffer, 32);
|
||||
BitConverter.GetBytes((ushort)phentsize).CopyTo(buffer, 54);
|
||||
BitConverter.GetBytes((ushort)phnum).CopyTo(buffer, 56);
|
||||
|
||||
BitConverter.GetBytes((uint)2).CopyTo(buffer, phoff); // PT_DYNAMIC
|
||||
BitConverter.GetBytes((ulong)dynOffset).CopyTo(buffer, phoff + 8);
|
||||
BitConverter.GetBytes((ulong)dynSize).CopyTo(buffer, phoff + 32);
|
||||
|
||||
using var stream = new MemoryStream(buffer);
|
||||
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
|
||||
|
||||
result.Should().BeTrue();
|
||||
info.Dependencies.Should().HaveCount(1);
|
||||
info.Dependencies[0].Soname.Should().Be("libc.so.6");
|
||||
info.Dependencies[0].VersionNeeds.Should().HaveCount(2);
|
||||
info.Dependencies[0].VersionNeeds.Should().Contain(v => v.Version == "GLIBC_2.17");
|
||||
info.Dependencies[0].VersionNeeds.Should().Contain(v => v.Version == "GLIBC_2.28");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesElfWithWeakVersionNeeds()
|
||||
{
|
||||
// Test that weak version requirements (VER_FLG_WEAK) are properly detected
|
||||
var elf = ElfBuilder.LinuxX64()
|
||||
.AddDependency("libc.so.6")
|
||||
.AddVersionNeed("libc.so.6", "GLIBC_2.17", isWeak: false) // Required version
|
||||
.AddVersionNeed("libc.so.6", "GLIBC_2.34", isWeak: true) // Weak/optional version
|
||||
.Build();
|
||||
|
||||
var info = ParseElf(elf);
|
||||
|
||||
info.Dependencies.Should().HaveCount(1);
|
||||
info.Dependencies[0].Soname.Should().Be("libc.so.6");
|
||||
info.Dependencies[0].VersionNeeds.Should().HaveCount(2);
|
||||
|
||||
// GLIBC_2.17 should NOT be weak
|
||||
var glibc217 = info.Dependencies[0].VersionNeeds.First(v => v.Version == "GLIBC_2.17");
|
||||
glibc217.IsWeak.Should().BeFalse();
|
||||
|
||||
// GLIBC_2.34 should BE weak
|
||||
var glibc234 = info.Dependencies[0].VersionNeeds.First(v => v.Version == "GLIBC_2.34");
|
||||
glibc234.IsWeak.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user