Skip to content

Commit

Permalink
Add --jit option to use JIT debugger. Bug fixes. Minor updates and cl…
Browse files Browse the repository at this point in the history
…eanup
  • Loading branch information
idiom committed Feb 29, 2020
1 parent 85c1480 commit 0e629a7
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 126 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017
Copyright (c) 2017-20220 Sean Wilson OpenAnalysis

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
37 changes: 30 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ To use BlobRunner, you can download the compiled executable from the [releases](
## Building
Building the executable is straight forward and relatively painless.

### Windows
__Requirements__
- Download and install Microsoft Visual C++ Build Tools or Visual Studio

Expand All @@ -22,18 +23,19 @@ __Build Steps__
```
cl blobrunner.c
```

### Building BlobRunner x64


#### Building BlobRunner x64

Building the x64 version is virtually the same as above, but simply uses the x64 tooling.
- Open x64 Visual Studio Command Prompt
- Navigate to the directory where BlobRunner is checked out
- Build the executable by running:

```
cl /Feblobrunner64.exe /Foblobrunner64.out blobrunner.c
```


## Usage

To debug:
Expand All @@ -59,15 +61,36 @@ Debug into file and don't pause before the jump. __Warning:__ Ensure you have a
BlobRunner.exe shellcode.bin --nopause
```

##### Debugging x64 Shellcode
#### Just-In-Time (JIT) Debugger

The optional parameter `--jit` can be used to debug the shell code with the configured Just-In-Time (JIT) debugger.
To trigger the debugger - BlobRunner removes execute access from the newly allocated memory region. This causes the program to raise
an AccessViolation (0xC0000005) exception when attempting to execute the loaded shellcode.

Instructions for configuring the JIT debugger on windows can be found [here](https://docs.microsoft.com/en-us/windows/win32/debug/configuring-automatic-debugging).
For x64Dbg see [setjit](https://help.x64dbg.com/en/latest/commands/misc/setjit.html) or [setjitauto](https://help.x64dbg.com/en/latest/commands/misc/setjitauto.html).

Example:
```
BlobRunner.exe shellcode.bin --jit
```

Once the debugger is loaded grant executable access to the region. Using [x64Dbg](https://x64dbg.com/#start) you can execute the command [setpagerights](https://help.x64dbg.com/en/latest/commands/memory-operations/setpagerights.html)
using the address returned by BlobRunner.

```
setpagerights <region>, ExecuteReadWrite
```

#### Debugging x64 Shellcode

Inline assembly [isn't supported](https://msdn.microsoft.com/en-us/library/wbk4z78b.aspx) by the x64 compiler, so to support debugging into x64 shellcode the loader
Inline assembly [isn't supported](https://msdn.microsoft.com/en-us/library/wbk4z78b.aspx) by the x64 compiler, so to support debugging x64 shellcode the loader
creates a suspended thread which allows you to place a breakpoint at the thread entry, before the thread is resumed.

##### Remote Debugging Shell Blobs (IDAPro)
#### Remote Debugging Shell Blobs (IDAPro)

The process is virtually identical to debugging shellcode locally - with the exception that the you need to copy the shellcode file
to the remote system. If the file is copied to the same path you are running _win32_remote.exe_ from, you just need to use
to the remote system. If the file is copied to the same path you are running _win32_remote.exe_ from, you need to use
the file name for the parameter. Otherwise, you will need to specify the path to the shellcode file on the remote system.

## Shellcode Samples
Expand Down
241 changes: 123 additions & 118 deletions blobrunner.c
Original file line number Diff line number Diff line change
@@ -1,193 +1,198 @@
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <stdlib.h>

#ifdef _WIN64
#include <WinBase.h>
#include <WinBase.h>
#endif

// Define bool
typedef int bool;
#define true 1
#define false 0

const char *_version = "0.0.3";
const char* _version = "0.0.4";

const char *_banner = " __________.__ ___. __________\n"
" \\______ \\ | ____\\_ |__\\______ \\__ __ ____ ____ ___________ \n"
" | | _/ | / _ \\| __ \\| _/ | \\/ \\ / \\_/ __ \\_ __ \\ \n"
" | | \\ |_( <_> ) \\_\\ \\ | \\ | / | \\ | \\ ___/| | \\/ \n"
" |______ /____/\\____/|___ /____|_ /____/|___| /___| /\\___ >__| \n"
" \\/ \\/ \\/ \\/ \\/ \\/ \n\n"
" \033[92m %s\033[0m \n\n";
const char* _banner = " __________.__ ___. __________\n"
" \\______ \\ | ____\\_ |__\\______ \\__ __ ____ ____ ___________ \n"
" | | _/ | / _ \\| __ \\| _/ | \\/ \\ / \\_/ __ \\_ __ \\ \n"
" | | \\ |_( <_> ) \\_\\ \\ | \\ | / | \\ | \\ ___/| | \\/ \n"
" |______ /____/\\____/|___ /____|_ /____/|___| /___| /\\___ >__| \n"
" \\/ \\/ \\/ \\/ \\/ \\/ \n\n"
" %s \n\n";

void banner(){
system("cls");
printf(_banner, _version);
return;

void banner() {
system("cls");
printf(_banner, _version);
return;
}

LPVOID process_file(char* inputfile_name, bool autobreak, int offset, bool debug){
LPVOID process_file(char* inputfile_name, bool jit, int offset, bool debug) {
LPVOID lpvBase;
FILE *file;
FILE* file;
unsigned long fileLen;
char *buffer;
char* buffer;
DWORD dummy;

file = fopen(inputfile_name, "rb");

if (!file){
if (!file) {
printf(" [!] Error: Unable to open %s\n", inputfile_name);

return (LPVOID)NULL;
}

printf (" [*] Reading file...\n");
printf(" [*] Reading file...\n");
fseek(file, 0, SEEK_END);
fileLen=ftell(file); //Get Length
fileLen = ftell(file); //Get Length

printf (" [*] File Size: 0x%04x\n", fileLen);
printf(" [*] File Size: 0x%04x\n", fileLen);
fseek(file, 0, SEEK_SET); //Reset

if(autobreak)
fileLen+=2;
else
fileLen+=1;

buffer=(char *)malloc(fileLen); //Create Buffer

if(autobreak){
if(offset == 0){
buffer[0] = 0xCC;
fread(buffer+1, fileLen, 1, file);
}
else{
buffer[offset] = 0xCC;
fread(buffer, offset, 1, file);
fread(buffer+offset+1, fileLen, 1, file);
}
}
else{
fread(buffer, fileLen, 1, file);
}

fclose(file);

printf (" [*] Allocating Memory...");
fileLen += 1;

buffer = (char*)malloc(fileLen); //Create Buffer
fread(buffer, fileLen, 1, file);
fclose(file);

printf(" [*] Allocating Memory...");

lpvBase = VirtualAlloc(NULL,fileLen,0x3000,0x40);
lpvBase = VirtualAlloc(NULL, fileLen, 0x3000, 0x40);

printf(".Allocated!\n");
printf(" [*] |-Base: 0x%08x\n",lpvBase);
printf(" [*] Copying input data...\n");
printf(" [*] |-Base: 0x%08x\n", (int)(size_t)lpvBase);
printf(" [*] Copying input data...\n");

CopyMemory(lpvBase, buffer, fileLen);
printf(" [*] Entry: Setting executable\n");
VirtualProtect(lpvBase, fileLen, PAGE_EXECUTE_READ, &dummy);
return lpvBase;
}

void execute(LPVOID base, int offset, bool nopause, bool autobreak, bool debug)
void execute(LPVOID base, int offset, bool nopause, bool jit, bool debug)
{
LPVOID entry;
#ifdef _WIN64
DWORD thread_id;
HANDLE thread_handle;
#endif


entry = (LPVOID)((int)base + offset);


#ifdef _WIN64

printf(" [*] Creating Suspended Thread...\n");
thread_handle = CreateThread(
NULL, // Attributes
0, // Stack size (Default)
entry, // Thread EP
NULL, // Arguments
0x4, // Create Suspended
&thread_id); // Thread identifier

if(thread_handle == NULL){
printf(" [!] Error Creating thread...");
return;
}
printf(" [*] Created Thread: [%d]\n", thread_id);
printf(" [*] Thread Entry: 0x%016x\n",entry);
printf(" [*] Navigate to the Thread Entry and set a breakpoint. Then press any key to resume the thread.\n",entry);
getchar();
ResumeThread(thread_handle);
#else
printf(" [*] Entry: 0x%08x\n",entry);
if(nopause == false){
printf(" [*] Navigate to the EP and set a breakpoint. Then press any key to jump to the shellcode.\n");
getchar();
}
else{
printf(" [*] Jumping to shellcode\n");
}
__asm jmp entry;
#endif
LPVOID shell_entry;

#ifdef _WIN64
DWORD thread_id;
HANDLE thread_handle;
const char msg[] = " [*] Navigate to the Thread Entry and set a breakpoint. Then press any key to resume the thread.\n";
#else
const char msg[] = " [*] Navigate to the EP and set a breakpoint. Then press any key to jump to the shellcode.\n";
#endif

shell_entry = (LPVOID)((UINT_PTR)base + offset);

#ifdef _WIN64

printf(" [*] Creating Suspended Thread...\n");
thread_handle = CreateThread(
NULL, // Attributes
0, // Stack size (Default)
shell_entry, // Thread EP
NULL, // Arguments
0x4, // Create Suspended
&thread_id); // Thread identifier

if (thread_handle == NULL) {
printf(" [!] Error Creating thread...");
return;
}
printf(" [*] Created Thread: [%d]\n", thread_id);
printf(" [*] Thread Entry: 0x%016x\n", (int)(size_t)shell_entry);

#endif

if (nopause == false) {
printf("%s", msg);
getchar();
}
else
{
if (jit == true) {
// Force an exception by making the first byte not executable.
// This will cause
DWORD oldp;

printf(" [*] Removing EXECUTE access to trigger exception...\n");

VirtualProtect(shell_entry, 1 , PAGE_READWRITE, &oldp);
}
}

#ifdef _WIN64
printf(" [*] Resuming Thread..\n");
ResumeThread(thread_handle);
#else
printf(" [*] Entry: 0x%08x\n", (int)(size_t)shell_entry);
printf(" [*] Jumping to shellcode\n");
__asm jmp shell_entry;
#endif
}

void print_help() {
printf(" [!] Error: No file!\n\n");
printf(" Required args: <inputfile>\n\n");
printf(" Optional Args:\n");
printf(" --offset <offset> The offset to jump into.\n");
printf(" --nopause Don't pause before jumping to shellcode. Danger!!! \n");
printf(" --jit Forces an exception by removing the EXECUTE permission from the alloacted memory.\n");
printf(" --debug Verbose logging.\n");
printf(" --version Print version and exit.\n\n");
}

int main(int argc, char* argv[])
{
LPVOID base;
LPVOID base;
int i;
int offset = 0;
bool nopause = false;
bool debug = false;
bool autobreak = false;
char *nptr;
bool jit = false;
char* nptr;

banner();

if(argc < 2){
printf(" \033[31m [!] Error: \033[0m No file!\n\n");
printf(" Required args: <inputfile>\n\n");
printf(" Optional Args:\n");
printf(" --offset <offset> The offset to jump into.\n");
printf(" --nopause Don't pause before jumping to shellcode. Danger!!! \n");
printf(" --autobreak Insert a breakpoint at the offset. (Default: 0)\n");
printf(" --debug Verbose logging.\n");
printf(" --version Print version and exit.\n\n");
if (argc < 2) {
print_help();
return -1;
}

printf(" [*] Using file: %s \n", argv[1]);

for(i=2; i < argc; i++){
if(strcmp(argv[i], "--offset") == 0){
for (i = 2; i < argc; i++) {
if (strcmp(argv[i], "--offset") == 0) {
printf(" [*] Parsing offset...\n");
i=i+1;
i = i + 1;
offset = strtol(argv[i], &nptr, 16);
}
else if(strcmp(argv[i], "--nopause") == 0){
else if (strcmp(argv[i], "--nopause") == 0) {
nopause = true;
}
else if(strcmp(argv[i], "--autobreak") == 0){
autobreak = true;
nopause = true;
else if (strcmp(argv[i], "--jit") == 0) {
jit = true;
nopause = true;
}
else if(strcmp(argv[i],"--debug") == 0){
else if (strcmp(argv[i], "--debug") == 0) {
debug = true;
}
else if(strcmp(argv[i],"--version") == 0){
else if (strcmp(argv[i], "--version") == 0) {
printf("Version: %s", _version);
}
else{
printf("\033[93m [!] Warning: \033[0mUnknown arg: %s\n",argv[i]);
else {
printf("[!] Warning: Unknown arg: %s\n", argv[i]);
}
}

base = process_file(argv[1], autobreak, offset, debug);
if (base == NULL){
base = process_file(argv[1], jit, offset, debug);
if (base == NULL) {
printf(" [!] Exiting...");
return -1;
}
printf(" [*] Using offset: 0x%08x\n", offset);
execute(base, offset, nopause, autobreak, debug);
printf ("Pausing - Press any key to quit.\n");
execute(base, offset, nopause, jit, debug);
printf("Pausing - Press any key to quit.\n");
getchar();
return 0;
}
Loading

0 comments on commit 0e629a7

Please sign in to comment.