mirror of
https://github.com/taviso/loadlibrary
synced 2026-06-08 17:42:38 +00:00
234 lines
7.3 KiB
C
234 lines
7.3 KiB
C
//
|
|
// Copyright (C) 2017 Tavis Ormandy
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
|
|
#ifndef _GNU_SOURCE
|
|
# define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/unistd.h>
|
|
#include <asm/unistd.h>
|
|
#include <fcntl.h>
|
|
#include <iconv.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <err.h>
|
|
#include <readline/readline.h>
|
|
#include <readline/history.h>
|
|
|
|
#include "log.h"
|
|
#include "winnt_types.h"
|
|
#include "pe_linker.h"
|
|
#include "ntoskernel.h"
|
|
#include "util.h"
|
|
#include "hook.h"
|
|
#include "rsignal.h"
|
|
#include "engineboot.h"
|
|
#include "scanreply.h"
|
|
#include "streambuffer.h"
|
|
#include "openscan.h"
|
|
|
|
const char header[] =
|
|
"function log(msg) { parseFloat('__log: ' + msg); }\n"
|
|
"function dump(obj) { for (i in obj) { log(i); log('\\t' + obj[i]); }; }\n";
|
|
const char footer[] = {
|
|
#include "script.h" // Generated by Makefile
|
|
0, // String terminator
|
|
};
|
|
|
|
DWORD (* __rsignal)(PHANDLE KernelHandle, DWORD Code, PVOID Params, DWORD Size);
|
|
|
|
double JavaScriptLog(const char *nptr, char **endptr)
|
|
{
|
|
if (strncmp(nptr, "__log: ", 7) == 0) {
|
|
LogMessage("%s", nptr + 7);
|
|
return 0;
|
|
}
|
|
return strtod(nptr, endptr);
|
|
}
|
|
|
|
static DWORD EngineScanCallback(PSCANSTRUCT Scan)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static DWORD ReadStream(PVOID this, QWORD Offset, PVOID Buffer, DWORD Size, PDWORD SizeRead)
|
|
{
|
|
memcpy(Buffer, this + Offset, *SizeRead = MIN(strlen(this+Offset), Size));
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD GetStreamSize(PVOID this, PQWORD FileSize)
|
|
{
|
|
*FileSize = strlen(this);
|
|
return TRUE;
|
|
}
|
|
|
|
int main(int argc, char **argv, char **envp)
|
|
{
|
|
PVOID StrtodPtr;
|
|
PIMAGE_DOS_HEADER DosHeader;
|
|
PIMAGE_NT_HEADERS PeHeader;
|
|
HANDLE KernelHandle;
|
|
SCAN_REPLY ScanReply;
|
|
BOOTENGINE_PARAMS BootParams;
|
|
SCANSTREAM_PARAMS ScanParams;
|
|
STREAMBUFFER_DESCRIPTOR ScanDescriptor;
|
|
ENGINE_INFO EngineInfo;
|
|
ENGINE_CONFIG EngineConfig;
|
|
struct pe_image image = {
|
|
.entry = NULL,
|
|
.name = "engine/mpengine.dll",
|
|
};
|
|
|
|
// Load the mpengine module.
|
|
if (pe_load_library(image.name, &image.image, &image.size) == false) {
|
|
return 1;
|
|
}
|
|
|
|
// Handle relocations, imports, etc.
|
|
link_pe_images(&image, 1);
|
|
|
|
// Fetch the headers to get base offsets.
|
|
DosHeader = (PIMAGE_DOS_HEADER) image.image;
|
|
PeHeader = (PIMAGE_NT_HEADERS)(image.image + DosHeader->e_lfanew);
|
|
|
|
// Load any additional exports.
|
|
if (!process_extra_exports(image.image, PeHeader->OptionalHeader.BaseOfCode, "engine/mpengine.map")) {
|
|
LogMessage("A map file is required to intercept interpreter output. See documentation.");
|
|
}
|
|
|
|
// Calculate the commands needed to get export and map symbols visible in gdb.
|
|
if (IsGdbPresent()) {
|
|
LogMessage("GDB: add-symbol-file %s %#x+%#x",
|
|
image.name,
|
|
image.image,
|
|
PeHeader->OptionalHeader.BaseOfCode);
|
|
LogMessage("GDB: shell bash genmapsym.sh %#x+%#x symbols_%d.o < %s",
|
|
image.image,
|
|
PeHeader->OptionalHeader.BaseOfCode,
|
|
getpid(),
|
|
"engine/mpengine.map");
|
|
LogMessage("GDB: add-symbol-file symbols_%d.o 0", getpid());
|
|
__debugbreak();
|
|
}
|
|
|
|
if (get_export("__rsignal", &__rsignal) == -1) {
|
|
LogMessage("Cannot resolve mpengine!__rsignal, cannot continue");
|
|
return 1;
|
|
}
|
|
|
|
if (get_export("_strtod", &StrtodPtr) != -1) {
|
|
insert_function_redirect(StrtodPtr, JavaScriptLog, HOOK_REPLACE_FUNCTION);
|
|
} else {
|
|
LogMessage("Unable to hook output (missing or incomplete map?), but input might still work. See documentation.");
|
|
}
|
|
|
|
// Call DllMain()
|
|
image.entry((PVOID) 'MPEN', DLL_PROCESS_ATTACH, NULL);
|
|
|
|
ZeroMemory(&BootParams, sizeof BootParams);
|
|
ZeroMemory(&EngineInfo, sizeof EngineInfo);
|
|
ZeroMemory(&EngineConfig, sizeof EngineConfig);
|
|
|
|
BootParams.ClientVersion = BOOTENGINE_PARAMS_VERSION;
|
|
BootParams.Attributes = BOOT_ATTR_NORMAL;
|
|
BootParams.SignatureLocation = L"engine";
|
|
BootParams.ProductName = L"Legitimate Antivirus";
|
|
EngineConfig.QuarantineLocation = L"quarantine";
|
|
EngineConfig.Inclusions = L"*.*";
|
|
EngineConfig.EngineFlags = 1 << 1;
|
|
BootParams.EngineInfo = &EngineInfo;
|
|
BootParams.EngineConfig = &EngineConfig;
|
|
KernelHandle = NULL;
|
|
|
|
LogMessage("Please wait, initializing engine...");
|
|
|
|
if (__rsignal(&KernelHandle, RSIG_BOOTENGINE, &BootParams, sizeof BootParams) != 0) {
|
|
LogMessage("__rsignal(RSIG_BOOTENGINE) returned failure, missing definitions?");
|
|
LogMessage("Make sure the VDM files and mpengine.dll are in the engine directory");
|
|
return 1;
|
|
}
|
|
|
|
ZeroMemory(&ScanParams, sizeof ScanParams);
|
|
ZeroMemory(&ScanDescriptor, sizeof ScanDescriptor);
|
|
ZeroMemory(&ScanReply, sizeof ScanReply);
|
|
|
|
ScanParams.Descriptor = &ScanDescriptor;
|
|
ScanParams.ScanReply = &ScanReply;
|
|
ScanReply.EngineScanCallback = EngineScanCallback;
|
|
ScanReply.field_C = 0x7fffffff;
|
|
ScanDescriptor.Read = ReadStream;
|
|
ScanDescriptor.GetSize = GetStreamSize;
|
|
|
|
LogMessage("Ready, type javascript (history available, use arrow keys)");
|
|
LogMessage("Try log(msg) or dump(obj), mp.getAttribute() can query scan state booleans.");
|
|
while (true) {
|
|
CHAR *InputBuf = readline("> ");
|
|
|
|
if (InputBuf) {
|
|
CHAR *EscapeBuf = calloc(strlen(InputBuf) + 1, 3);
|
|
CHAR *p = EscapeBuf;
|
|
|
|
if (!EscapeBuf)
|
|
break;
|
|
|
|
// This is probably not correct.
|
|
for (size_t i = 0; InputBuf[i]; i++) {
|
|
if (InputBuf[i] == '%') {
|
|
*p++ = '%'; *p++ = '2'; *p++ = '5';
|
|
} else if (InputBuf[i] == '"') {
|
|
*p++ = '%'; *p++ = '2'; *p++ = '2';
|
|
} else if (InputBuf[i] == '\\') {
|
|
*p++ = '%'; *p++ = '5'; *p++ = 'c';
|
|
} else {
|
|
*p++ = InputBuf[i];
|
|
}
|
|
}
|
|
|
|
if (asprintf((PVOID) &ScanDescriptor.UserPtr,
|
|
"%s\ntry{log(eval(unescape(\"%s\")))} catch(e) { log(e); }\n%s",
|
|
header,
|
|
EscapeBuf,
|
|
footer) == -1) {
|
|
err(EXIT_FAILURE, "memory allocation failure");
|
|
}
|
|
free(EscapeBuf);
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if (__rsignal(&KernelHandle, RSIG_SCAN_STREAMBUFFER, &ScanParams, sizeof ScanParams) != 0) {
|
|
LogMessage("__rsignal(RSIG_SCAN_STREAMBUFFER) returned failure, file unreadable?");
|
|
return 1;
|
|
}
|
|
|
|
add_history(InputBuf);
|
|
free(ScanDescriptor.UserPtr);
|
|
free(InputBuf);
|
|
}
|
|
|
|
return 0;
|
|
}
|