Skip to content

Instantly share code, notes, and snippets.

@richinseattle
Last active September 24, 2021 02:24
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save richinseattle/613105953003ec5e1f24ca17b2d8541f to your computer and use it in GitHub Desktop.
Save richinseattle/613105953003ec5e1f24ca17b2d8541f to your computer and use it in GitHub Desktop.
// Launch WinAFL with current function as hook location
//@author richinseattle
//@category _NEW_
//@keybinding
//@menupath
//@toolbar
// Usage:
// Install DynamoRIO and WinAFL
// Add LaunchWinAFL to Ghidra scripts
// Set one-time config in the LaunchWinAFL class below the imports
// Load target exe & dlls into Ghidra
// Go to target func in disasm
// Run script to start fuzzing!
import ghidra.app.script.GhidraScript;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.Project;
import ghidra.framework.model.ProjectData;
import ghidra.framework.model.ProjectDataUtils;
import ghidra.framework.model.ProjectManager;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.util.*;
import ghidra.program.model.reloc.*;
import ghidra.program.model.data.*;
import ghidra.program.model.block.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.scalar.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.address.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.stream.Stream;
import docking.widgets.dialogs.InputDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
import java.util.function.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
public class LaunchWinAFL extends GhidraScript {
/*
* ======================================================
* Configuration section, modify these until I make a gui
* ======================================================
*/
// working directory for this script
public String base_dir = System.getenv("HOMEPATH") + "\\ghidraflow";
// base install directories for both tools
// do not include arch subdirs, they are autodetected based on target exe
String winafl_dir = "c:\\winafl";
//String winafl_dir = System.getenv("WINAFL_DIR");
String dynamorio_dir = "c:\\dynamorio";
//String dynamorio_dir = System.getenv("DYNAMORIO_DIR");
// target_exe_path will be auto populated by currently opened executable
String target_exe_path;
// place target program arguments here, use @@ for where the fuzzed file path should go
// by default we just pass the fuzzed file to the target exe
String target_exe_args = "@@";
// auto populated by properties of current function
// in disassembly listing unless specified
int target_func_argc = 0;
long target_func_offset = 0;
String target_func_callconv;
// coverage will include all binaries in the current ghidra project unless specified here
String[] coverage_modules;
// a set of inputs for your fuzz target
// if not specified, a dialog will ask you to specify the path
// by default it will be showing the winafl testcases directory
public String input_dir;
// the top level output / working directory for WinAFL
// subdirectories will be created for each fuzzing run
public String output_dir = base_dir + "\\winafl";
// target arch is auto-detected unless target_exe_path was specified above
static enum CpuArch {
x86,
x64,
Unknown
}
public CpuArch target_arch = CpuArch.Unknown;
/*
* ======================================================
* Main program code follows
* ======================================================
*/
public void run() throws Exception {
/*
* validate environment / config
*/
if(!System.getProperty("os.name").startsWith("Windows"))
{
popup("Sorry, this plugin only runs on Windows!");
return;
}
// make working dir if not present
File dir = new File(base_dir);
if(!dir.exists())
{
if(!dir.mkdir())
{
popup("Error: couldn't create or access working directory\nCheck base_dir variable!");
return;
}
}
if(target_exe_path == null)
target_exe_path = currentProgram.getExecutablePath();
// combine exe with args
String target_cmdline = String.format("%s %s", target_exe_path, target_exe_args);
// currently only getting pointer size in bytes
if (target_arch == CpuArch.Unknown)
{
switch(currentProgram.getDefaultPointerSize())
{
case 4:
target_arch = CpuArch.x86;
break;
case 8:
target_arch = CpuArch.x64;
break;
default:
popup("Error: couldn't detect target arch, please specify in config!");
return;
}
}
if (dynamorio_dir == null)
{
popup("Error: DynamoRIO not found, please set dynamorio_dir!");
return;
}
if (winafl_dir == null)
{
popup("Error: WinAFL not found, please set winafl_dir!");
return;
}
String arch_path = "";
if(target_arch == CpuArch.x86)
{
arch_path = "bin32";
} else if(target_arch == CpuArch.x64)
{
arch_path = "bin64";
}
else
{
popup("Error: WinAFL doesn't support this architecture!");
return;
}
/*
* Gather needed info
*/
Function currentFunction = getCurrentFunction();
if(target_exe_path == null)
target_exe_path = currentProgram.getExecutablePath();
if(target_func_offset == 0)
{
target_func_argc = currentFunction.getParameterCount();
target_func_offset = (currentFunction.getEntryPoint().subtract(currentProgram.getImageBase()));
}
String target_module = new File(target_exe_path).getName();
String target_offset = String.format("0x%x", target_func_offset);
int nargs = target_func_argc;
if(target_func_callconv == null)
{
// FIXME: assuming this works always, unverified
String cc = currentFunction.getCallingConventionName();
target_func_callconv = cc.substring(2);
}
// add all project modules to the coverage modules list by default
// including modules not present in process here doesn't hurt anything
if(coverage_modules == null)
coverage_modules = getProgramNames();
String afl_fuzz_exe_path = String.format("%s\\%s\\%s", winafl_dir, arch_path, "afl-fuzz.exe");
String dynamorio_binpath = String.format("%s\\%s", dynamorio_dir, arch_path);
output_dir = output_dir + "." + target_module + "." + System.currentTimeMillis();
if(input_dir == null)
{
GhidraFileChooser gfc = new GhidraFileChooser(getState().getTool().getActiveWindow());
gfc.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY);
gfc.setCurrentDirectory(new File(winafl_dir + "\\testcases"));
gfc.setTitle("Input Files: Select a directory containing an input set for fuzzing");
gfc.setStatusJustification(2); // left
String padding = " "; // to align with the input text dialogs
gfc.setStatusText(padding + "Please select a directory containing an input set for fuzzing");
File f = gfc.getSelectedFile(true);
if(f == null || !f.exists())
{
println("Error: Input file path not valid");
return;
}
input_dir = f.getPath();
}
String afl_fuzz_opts =
" -t 2000+ "
+ " -i " + input_dir
+ " -o " + output_dir
+ " -D " + dynamorio_binpath;
//afl_opts += " -x " + dictionary;
String winafl_opts =
" -target_module " + target_module
+ " -target_offset " + target_offset
+ " -nargs " + nargs
+ " -call_convention " + target_func_callconv;
for(String mod : coverage_modules)
winafl_opts += " -coverage_module " + mod + " ";
winafl_opts += " -covtype edge";
winafl_opts += " -fuzz_iterations 5000";
//winafl_opts += " -thread_coverage";
// create a new timestamped output dir
dir = new File(output_dir);
if(!dir.exists())
{
if(!dir.mkdir())
{
popup("Error: couldn't create or access output directory!");
return;
}
}
// we add "cmd /c start" here to get winafl running outside the ghidra process tree
String cmdline = String.format("cmd /c start %s %s -- %s -- %s",
afl_fuzz_exe_path,
afl_fuzz_opts,
winafl_opts,
target_cmdline);
// build child command line argv
println("Running WinAFL cmdline: \n" + cmdline);
String[] argv = cmdline2argv(cmdline);
// execute
String exec_dir = winafl_dir + "\\" + arch_path;
exec_argv_in_dir(argv, exec_dir);
return;
}
/*
* ======================================================
* Utility functions
* ======================================================
*/
public Boolean exec_argv_in_dir(String[] argv, String exec_dir)
{
Boolean ret = false;
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(exec_dir));
builder.command(argv);
try {
Process process = builder.start();
final InputStream is = process.getInputStream();
byte[] msg = is.readAllBytes();
println(new String(msg));
ret = true;
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
public Function addr2func(Address addr)
{
return currentProgram.getListing().getFunctionContaining(addr);
}
public Function getCurrentFunction()
{
Address addr = currentAddress;
return addr2func(addr);
}
public String[] cmdline2argv(String cmdline)
{
List<String> argvList = new ArrayList<String>();
Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(cmdline);
while (m.find())
{
argvList.add(m.group(1));
}
return argvList.toArray(new String[0]);
}
private ArrayList<String> getFolderProgramNames ( DomainFolder domainFolder )
{
ArrayList<String> projectFiles = new ArrayList<String>();
DomainFile[] files = domainFolder.getFiles();
for ( DomainFile domainFile : files )
projectFiles.add( domainFile.getName());
DomainFolder[] folders = domainFolder.getFolders();
for ( DomainFolder folder : folders )
projectFiles.addAll(getFolderProgramNames(folder));
return projectFiles;
}
public String[] MatchStringArray(String[] array, String filter)
{
ArrayList<String> matches = new ArrayList<String>();
for (String str: array)
if (str.contains(filter)) matches.add(str);
return matches.toArray(new String[0]);
}
public String[] getProgramNames()
{
PluginTool tool = state.getTool();
Project project = tool.getProject();
ProjectData projectData = project.getProjectData();
DomainFolder rootFolder = projectData.getRootFolder();
return getFolderProgramNames(rootFolder).toArray(new String[0]);
}
public String[] getExeNames()
{
return MatchStringArray(getProgramNames(), ".exe");
}
public String[] getDllNames()
{
return MatchStringArray(getProgramNames(), ".dll");
}
public String[] getEnvp()
{
Map<String,String> envs = System.getenv();
String[] envp = new String[envs.size()];
int i = 0;
for (Map.Entry<String,String> e : envs.entrySet())
envp[i++] = e.getKey()+'+'+e.getValue();
return envp;
}
}
@Tejas163
Copy link

Tejas163 commented Jul 4, 2020

public String base_dir = System.getenv("HOMEPATH")

Hi where do i set the HOMEPATH and to which directory it should be pointing to?

@richinseattle
Copy link
Author

richinseattle commented Jul 4, 2020 via email

@Tejas163
Copy link

Tejas163 commented Jul 5, 2020

Hi richinseattle,

                I changed the base_dir to where my script was and executed on 7zip program. But after initial fuzzing it takes the input directory and then crashes without error. I tried the same command through command prompt. I got timeout error saying all cases timeout.

image
Capture-error

@richinseattle
Copy link
Author

richinseattle commented Jul 5, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment