Put artifacts in separate output folder

This commit is contained in:
Duncan Ogilvie
2026-05-02 23:56:59 +02:00
parent c58f357c0f
commit 0c71152f69
20 changed files with 154 additions and 55 deletions
+1
View File
@@ -18,6 +18,7 @@
x64/
[Bb]in/
[Oo]bj/
output/
# build folder is nowadays used for build scripts and should not be ignored
#build/
+3 -2
View File
@@ -1,5 +1,6 @@
using Dna.BinaryTranslator.Lifting;
using Dna.Extensions;
using Dna.Utilities;
using Dna.LLVMInterop.API.Remill.Arch;
using Dna.LLVMInterop.API.Remill.BC;
using LLVMSharp.Interop;
@@ -15,7 +16,7 @@ namespace Dna.BinaryTranslator
{
public static unsafe LLVMValueRef IsolateFunctionIntoNewModule(RemillArch arch, LLVMValueRef function)
{
File.WriteAllText("translatedFunction.ll", function.PrintToString());
File.WriteAllText(ArtifactPaths.Resolve("translatedFunction.ll"), function.PrintToString());
// Console.WriteLine("");
//Console.WriteLine(function);
// Optimize the lifted function(mainly to inline intrinsics).
@@ -67,7 +68,7 @@ namespace Dna.BinaryTranslator
builder.CreateMsvcPersonalityFunction();
}
isolatedFunction.GlobalParent.PrintToFile("translatedFunction.ll");
isolatedFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
return (isolatedFunction, newFilters);
}
}
@@ -1,6 +1,7 @@
using Dna.Binary;
using Dna.ControlFlow.Extensions;
using Dna.Extensions;
using Dna.Utilities;
using Dna.LLVMInterop.API.LLVMBindings.Analysis;
using Dna.LLVMInterop.API.Optimization;
using Dna.LLVMInterop.Souper.Candidate;
@@ -86,7 +87,7 @@ namespace Dna.BinaryTranslator.JmpTables.Precise
indJmp.SetOperand(1, intraBlockPtr);
//jmpPtr = intraBlockPtr;
function.GlobalParent.PrintToFile("translatedFunction.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Build the set of path constraints necessary to reach the remill_jump.
var exprBuilder = new SouperExprBuilder(instCtx);
@@ -44,7 +44,7 @@ namespace Dna.BinaryTranslator.Safe
//OptimizationApi.OptimizeModule(llvmFunction.GlobalParent, llvmFunction);
llvmFunction.GlobalParent.PrintToFile("translatedFunction.ll");
llvmFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
var path = ClangCompiler.CompileToWindowsDll(llvmFunction, "translatedFunction.ll", false);
IDALoader.Load(path, true);
@@ -229,7 +229,7 @@ namespace Dna.BinaryTranslator.Safe
return new SafelyTranslatedFunction(binaryFunction, executableRuntime, updatedFilterFunctions);
/*
executableRuntime.OutputFunction.GlobalParent.PrintToFile("translatedFunction.ll");
executableRuntime.OutputFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
string path = null;
bool toDll = true;
if (toDll)
@@ -1,6 +1,7 @@
using Dna.BinaryTranslator.Lifting;
using Dna.Extensions;
using Dna.LLVMInterop.API.LLVMBindings.IR;
using Dna.Utilities;
using LLVMSharp.Interop;
using System;
using System.Collections.Generic;
@@ -36,7 +37,7 @@ namespace Dna.BinaryTranslator.Safe
private void Implement()
{
liftedFunction.GlobalParent.PrintToFile("translatedFunction.ll");
liftedFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// If the function has no SEH filters then do nothing.
if (!liftedFilterFunctions.Any())
return;
@@ -92,7 +93,7 @@ namespace Dna.BinaryTranslator.Safe
{
foreach(var filter in liftedFilterFunctions)
{
filter.LlvmFunction.GlobalParent.PrintToFile("translatedFunction.ll");
filter.LlvmFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Emit @llvm.eh.recoverfp. Note that argument 1 of any filter function is the parent functions frame pointer.
builder.PositionBefore(filter.LlvmFunction.EntryBasicBlock.FirstInstruction);
var recoverFp = intrinsicBuilder.EmitSehRecoverFp(liftedFunction, filter.LlvmFunction.GetParam(1));
@@ -53,11 +53,11 @@ namespace Dna.BinaryTranslator.Unsafe
liftedFunction = FunctionIsolator.IsolateFunctionIntoNewModuleWithSehSupport(arch, liftedFunction, filterFunctions.Select(x => x.LiftedFilterFunction).ToList().AsReadOnly()).function;
liftedFunction.GlobalParent.PrintToFile("translatedFunction.ll");
liftedFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
liftedFunction = StripRuntime(liftedFunction);
liftedFunction.GlobalParent.PrintToFile("translatedFunction.ll");
liftedFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
@@ -142,7 +142,7 @@ namespace Dna.BinaryTranslator.Unsafe
OptimizationApi.OptimizeModule(function.GlobalParent, function, false, false, 0, false, 0, false);
PassPipeline.Run(dna.Binary, function, false, false);
function.GlobalParent.PrintToFile("translatedFunction.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
MbaDeobfuscationPass.Run(function);
@@ -102,7 +102,7 @@ namespace Dna.BinaryTranslator.Unsafe
(var liftedFunction, var blockMapping, var filterFunctions) = CfgTranslator.Translate(dna.Binary.BaseAddress, arch, "C:\\Users\\colton\\Downloads\\remill-17-semantics", ctx, new BinaryFunction(encodedCfg, scopeTableTree, solvedTables.AsReadOnly()), fallthroughFromIps, CallHandlingKind.Normal);
liftedFunction = FunctionIsolator.IsolateFunctionIntoNewModuleWithSehSupport(arch, liftedFunction, filterFunctions.Select(x => x.LiftedFilterFunction).ToList().AsReadOnly()).function;
liftedFunction.GlobalParent.PrintToFile("translatedFunction.ll");
liftedFunction.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Strip away as much of the remill runtime as possible.
@@ -97,7 +97,7 @@ namespace Dna.BinaryTranslator.VMProtect
// Clear the to-lift worklists.
handlersRipsToLift.Clear();
outModule.PrintToFile("translatedFunction.ll");
outModule.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Create a control flow graph out of the current partial CFG.
// Create a "true" CFG by inlining direct jumps to targets with only one predecessor.
@@ -141,7 +141,7 @@ namespace Dna.BinaryTranslator.VMProtect
// Isolate the lifted function into it's own module.
liftedFunction = FunctionIsolator.IsolateFunctionInto(outModule, liftedFunction);
outModule.PrintToFile("translatedFunction.ll");
outModule.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
var callTargets = outModule.GetFunctions().Where(x => x.Name.Contains("TranslatedFrom"));
foreach(var caller in callTargets)
@@ -206,8 +206,8 @@ namespace Dna.BinaryTranslator.VMProtect
output = liftedFunction;
outModule.WriteBitcodeToFile("translatedFunction.bc");
outModule.PrintToFile("translatedFunction.ll");
outModule.WriteBitcodeToFile(ArtifactPaths.Resolve("translatedFunction.bc"));
outModule.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
outModule.Verify(LLVMVerifierFailureAction.LLVMPrintMessageAction);
Debugger.Break();
break;
@@ -228,7 +228,7 @@ namespace Dna.BinaryTranslator.VMProtect
// Convert the cfg to binja IR
var viewer = new BinjaVmCfgViewer(newCfg);
var compiled = viewer.Run();
File.WriteAllText("vmcfg.py", compiled);
File.WriteAllText(ArtifactPaths.Resolve("vmcfg.py"), compiled);
// Now we need to figure out which basic blocks we can cache.
// IF a partial block remains unchanged, we ca
@@ -5,6 +5,7 @@ using Dna.BinaryTranslator.Unsafe;
using Dna.Extensions;
using Dna.LLVMInterop.API.Remill.Arch;
using Dna.LLVMInterop.API.Remill.BC;
using Dna.Utilities;
using Iced.Intel;
using LLVMSharp.Interop;
using System;
@@ -63,7 +64,7 @@ namespace Dna.BinaryTranslator.VMProtect
// Lift the trace into an LLVM IR function.
var function = TraceLifter.Lift(module, arch, traceInsts);
function.GlobalParent.PrintToFile("translatedFunction.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Apply concrete implementations to all intrinsics.
// E.g. __remill_memory_read() and __remill_memory_write().
@@ -74,7 +75,7 @@ namespace Dna.BinaryTranslator.VMProtect
var parameterizedStateStruct = VmpParameterizedStateStructure.CreateFromFunction(arch, function);
function = parameterizedStateStruct.OutputFunction;
function.GlobalParent.PrintToFile("translatedFunction.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Replace the remill return and error intrinsics with
// functions that are more amenable to optimization.
ErrorAndReturnImplementer.Implement(function);
@@ -20,7 +20,7 @@ namespace Dna.BinaryTranslator.VMProtect
{
if(dbg)
{
function.GlobalParent.PrintToFile("translatedFunction.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
Console.WriteLine("dbg");
}
@@ -4,6 +4,7 @@ using Dna.ControlFlow;
using Dna.Extensions;
using Dna.LLVMInterop.API.LLVMBindings.Transforms.Utils;
using Dna.LLVMInterop.API.Remill.Arch;
using Dna.Utilities;
using LLVMSharp;
using LLVMSharp.Interop;
using System;
@@ -85,13 +86,13 @@ namespace Dna.BinaryTranslator.VMProtect
LiftBlocks(blockMapping, registerAllocaMapping);
module.PrintToFile("translatedFunction.ll");
module.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Inline all calls to each basic block. TODO: Delete.
foreach (var func in blockToFunctionMapping.Values)
LLVMCloning.InlineFunction(func.func);
module.PrintToFile("translatedFunction.ll");
module.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
//module.Verify(LLVMVerifierFailureAction.LLVMPrintMessageAction);
Console.WriteLine("lifted all blocks");
@@ -192,7 +193,7 @@ namespace Dna.BinaryTranslator.VMProtect
// Console.WriteLine(defaultBlock);
// Console.WriteLine(indirectPc);
module.PrintToFile("translatedFunction.ll");
module.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
// Console.ReadLine();
builder.PositionAtEnd(llvmBlock);
var swtch = builder.BuildSwitch(indirectPc, defaultBlock, (uint)outgoingAddresses.Count);
@@ -5,6 +5,7 @@ using Dna.Extensions;
using Dna.LLVMInterop.API.LLVMBindings.Transforms.Utils;
using Dna.LLVMInterop.API.Remill.Arch;
using Dna.LLVMInterop.API.Remill.BC;
using Dna.Utilities;
using LLVMSharp.Interop;
using System;
using System.Collections.Generic;
@@ -69,7 +70,7 @@ namespace Dna.BinaryTranslator.VMProtect
func.DeleteFunction();
}
module.PrintToFile("translatedFunction.ll");
module.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
return blockToLlvmFunc;
}
@@ -83,7 +84,7 @@ namespace Dna.BinaryTranslator.VMProtect
memPtr.Initializer = memoryPtrNull;
}
module.PrintToFile("translatedFunction.ll");
module.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
module.Verify(LLVMVerifierFailureAction.LLVMAbortProcessAction);
// Create the function
@@ -33,7 +33,7 @@ namespace Dna.BinaryTranslator.VMProtect
var targetFunc = function.GlobalParent.GetFunctions().SingleOrDefault(x => x.Name.Contains("vmp_maybe_unsolved_jump"));
if(targetFunc == null)
{
function.GlobalParent.PrintToFile("translatedFunction.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
throw new InvalidOperationException($"No jump tables to solve!");
}
@@ -45,7 +45,7 @@ namespace Dna.BinaryTranslator.VMProtect
{
if (jmpCall.GetOperand(2).Kind != LLVMValueKind.LLVMConstantIntValueKind)
{
function.GlobalParent.PrintToFile("translatedFunction.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
throw new InvalidOperationException($"Could not identify jump from address for call {jmpCall}");
}
@@ -83,7 +83,7 @@ namespace Dna.BinaryTranslator.VMProtect
else
{
// Otherwise this is probably an unsolved jump table. Error out.
jmpCall.InstructionParent.Parent.GlobalParent.PrintToFile("translatedFunction.ll");
jmpCall.InstructionParent.Parent.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
throw new InvalidOperationException($"Failed to solve indirect jump! {jmpCall}");
}
}
@@ -116,8 +116,8 @@ namespace Dna.BinaryTranslator.VMProtect
memPtr.Initializer = memoryPtrNull;
}
jmpCall.InstructionParent.Parent.GlobalParent.PrintToFile("translatedFunction.ll");
jmpCall.InstructionParent.Parent.GlobalParent.WriteBitcodeToFile("translatedFunction.bc");
jmpCall.InstructionParent.Parent.GlobalParent.PrintToFile(ArtifactPaths.Resolve("translatedFunction.ll"));
jmpCall.InstructionParent.Parent.GlobalParent.WriteBitcodeToFile(ArtifactPaths.Resolve("translatedFunction.bc"));
throw new InvalidOperationException($"Failed to solve indirect jump! {jmpCall}");
}
+1 -1
View File
@@ -236,7 +236,7 @@ if(prototypeBounds)
var l = LowerLshr.LowerLshrToLlvm(toSlice, bld);
toSlice.ReplaceAllUsesWith(l);
fromFile.PrintToFile("nolshr.ll");
fromFile.PrintToFile(ArtifactPaths.Resolve("nolshr.ll"));
var loopInfo = new LoopInfo();
+2 -1
View File
@@ -1,6 +1,7 @@
using Dna.LLVMInterop.API.LLVMBindings.IR;
using Dna.LLVMInterop.API.LLVMBindings.Transforms.Utils;
using Dna.LLVMInterop.API.Remill.BC;
using Dna.Utilities;
using LLVMSharp.Interop;
using System;
using System.Collections.Generic;
@@ -15,7 +16,7 @@ namespace Dna.Extensions
{
public static void WriteToLlFile(this LLVMModuleRef module, string path)
{
File.WriteAllText(path, module.GetModuleText());
File.WriteAllText(ArtifactPaths.Resolve(path), module.GetModuleText());
}
public static string GetModuleText(this LLVMModuleRef module)
+3 -2
View File
@@ -1,4 +1,5 @@
using Dna.Extensions;
using Dna.Utilities;
using Dna.LLVMInterop.API.LLVMBindings.Transforms.Utils;
using LLVMSharp.Interop;
using System;
@@ -31,7 +32,7 @@ namespace Dna.Passes
if (function.GetInstructions().Any(x => x.InstructionOpcode == LLVMOpcode.LLVMPHI))
LLVMCloning.PrepareForCloning(function, false);
function.GlobalParent.PrintToFile("beforecloning.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("beforecloning.ll"));
var directJmps = function.GetBlocks().Where(x => x.LastInstruction.InstructionOpcode == LLVMOpcode.LLVMBr && x.LastInstruction.OperandCount == 1).Select(x => x.LastInstruction).ToList();
if (!directJmps.Any())
@@ -59,7 +60,7 @@ namespace Dna.Passes
// throw new InvalidOperationException("Failed to merge basic block!");
}
function.GlobalParent.PrintToFile("cloned.ll");
function.GlobalParent.PrintToFile(ArtifactPaths.Resolve("cloned.ll"));
return true;
// Clone the basic block.
+97
View File
@@ -0,0 +1,97 @@
using System;
using System.IO;
namespace Dna.Utilities
{
public static class ArtifactPaths
{
private const string OutputDirectoryEnvVar = "DNA_OUTPUT_DIR";
private static readonly Lazy<string> outputDirectory = new(() =>
{
var configured = Environment.GetEnvironmentVariable(OutputDirectoryEnvVar);
var directory = !string.IsNullOrWhiteSpace(configured)
? configured
: Path.Combine(FindRepositoryRoot(), "output");
directory = Path.GetFullPath(directory);
Directory.CreateDirectory(directory);
return directory;
});
public static string OutputDirectory => outputDirectory.Value;
public static string Resolve(string path)
{
if (string.IsNullOrWhiteSpace(path) || Path.IsPathRooted(path))
return path;
var resolved = Path.Combine(OutputDirectory, path);
var parent = Path.GetDirectoryName(resolved);
if (!string.IsNullOrWhiteSpace(parent))
Directory.CreateDirectory(parent);
return resolved;
}
public static string ResolveInputFile(string path)
{
if (string.IsNullOrWhiteSpace(path) || Path.IsPathRooted(path))
return path;
var resolved = Resolve(path);
if (!File.Exists(resolved) && File.Exists(path))
File.Copy(path, resolved, overwrite: true);
return resolved;
}
public static string CopyInputToOutput(string path)
{
if (string.IsNullOrWhiteSpace(path))
return path;
var fullPath = Path.GetFullPath(path);
if (IsInOutputDirectory(fullPath))
return fullPath;
var targetPath = GetAvailablePath(Path.Combine(OutputDirectory, Path.GetFileName(path)), overwrite: false);
File.Copy(fullPath, targetPath, overwrite: false);
return targetPath;
}
public static string GetAvailablePath(string path, bool overwrite)
{
if (overwrite || !File.Exists(path))
return path;
var directory = Path.GetDirectoryName(path) ?? OutputDirectory;
var fileName = Path.GetFileNameWithoutExtension(path);
var extension = Path.GetExtension(path);
return Path.Combine(directory, $"{fileName}-{Guid.NewGuid():N}{extension}");
}
private static bool IsInOutputDirectory(string path)
{
var outputPath = OutputDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar;
return path.StartsWith(outputPath, StringComparison.OrdinalIgnoreCase);
}
private static string FindRepositoryRoot()
{
foreach (var startPath in new[] { Directory.GetCurrentDirectory(), AppContext.BaseDirectory })
{
var directory = new DirectoryInfo(startPath);
while (directory != null)
{
if (File.Exists(Path.Combine(directory.FullName, "Dna.sln")))
return directory.FullName;
directory = directory.Parent;
}
}
return Directory.GetCurrentDirectory();
}
}
}
+7 -14
View File
@@ -23,6 +23,7 @@ namespace Dna.Utilities
public static unsafe string CompileToWindowsDll(LLVMValueRef targetFunction, string llPath, bool overwrite = false)
{
llPath = ArtifactPaths.Resolve(llPath);
targetFunction.GlobalParent.WriteToLlFile(llPath);
@@ -66,12 +67,7 @@ define i32 @main(i32 %argc, i8** %argv)
// Compile the .ll to an exe.
var objPath = Path.Combine(dir, Path.ChangeExtension(fileName, ".exe"));
if (File.Exists(objPath) && overwrite == false)
{
var randName = Guid.NewGuid().ToString();
objPath = Path.Combine(dir, randName);
}
var objPath = ArtifactPaths.GetAvailablePath(Path.Combine(dir, Path.ChangeExtension(fileName, ".exe")), overwrite);
RunClang(clangPath, @$"""{asmPath}"" -target x86_64-pc-windows-msvc -O3 -mno-avx -mno-avx512f -fno-vectorize -fno-slp-vectorize -O3 -fasync-exceptions -fseh-exceptions -fexceptions -fcxx-exceptions -mno-sse -shared -o ""{objPath}""");
@@ -84,6 +80,7 @@ define i32 @main(i32 %argc, i8** %argv)
public static unsafe string Compile(string llPath, bool overwrite = false)
{
llPath = ArtifactPaths.ResolveInputFile(llPath);
/*
foreach(var function in module.GetFunctions())
{
@@ -101,12 +98,7 @@ define i32 @main(i32 %argc, i8** %argv)
// Compile the .ll to an exe.
var objPath = Path.Combine(dir, Path.ChangeExtension(fileName, ".exe"));
if (File.Exists(objPath) && overwrite == false)
{
var randName = Guid.NewGuid().ToString();
objPath = Path.Combine(dir, randName);
}
var objPath = ArtifactPaths.GetAvailablePath(Path.Combine(dir, Path.ChangeExtension(fileName, ".exe")), overwrite);
RunClang(clangPath, @$"""{asmPath}"" -target x86_64-pc-windows-msvc -O3 -fasync-exceptions -fseh-exceptions -fexceptions -fcxx-exceptions -fno-vectorize -fno-slp-vectorize -c -mno-sse -o ""{objPath}""");
@@ -139,6 +131,7 @@ define i32 @main(i32 %argc, i8** %argv)
public static LLVMModuleRef Optimize(LLVMModuleRef module, string llPath, bool overwrite = false)
{
llPath = ArtifactPaths.Resolve(llPath);
module.WriteToLlFile(llPath);
// If the file already exists, we don't want to overwrite it.
@@ -147,9 +140,9 @@ define i32 @main(i32 %argc, i8** %argv)
var fileName = Path.GetFileName(llPath);
var newPath = Path.Combine(dir, Path.ChangeExtension(fileName, ".opt.ll"));
RunClang(optPath, @$"-O3 -S ""{llPath}"" -o {newPath}");
RunClang(optPath, @$"-O3 -S ""{llPath}"" -o ""{newPath}""");
return RemillUtils.LoadModuleFromFile(module.Context, Path.Combine(Directory.GetCurrentDirectory(), newPath)).Value;
return RemillUtils.LoadModuleFromFile(module.Context, newPath).Value;
}
}
}
+8 -8
View File
@@ -14,17 +14,17 @@ namespace Dna.Utilities
public static string Load(string exePath, bool overwrite = false)
{
// If the file already exists, we don't want to overwrite it.
var dir = Path.GetDirectoryName(exePath);
// Compile the .ll file to assembly with vectorization disabled.
var fileName = Path.GetFileName(exePath);
exePath = ArtifactPaths.CopyInputToOutput(ArtifactPaths.ResolveInputFile(exePath));
// Compile the .ll to an exe.
// If the database already exists, use a unique copy so IDA does not
// reuse/overwrite an old .i64 next to the input.
if (File.Exists(exePath) && overwrite == false)
{
var randName = Guid.NewGuid().ToString();
var newPath = Path.Combine(dir, randName);
File.Move(exePath, newPath);
var dir = Path.GetDirectoryName(exePath) ?? ArtifactPaths.OutputDirectory;
var fileName = Path.GetFileNameWithoutExtension(exePath);
var extension = Path.GetExtension(exePath);
var newPath = Path.Combine(dir, $"{fileName}-{Guid.NewGuid():N}{extension}");
File.Copy(exePath, newPath, overwrite: false);
exePath = newPath;
}