mirror of
https://github.com/taviso/loadlibrary
synced 2026-06-08 17:42:38 +00:00
270 lines
8.3 KiB
C
270 lines
8.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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/unistd.h>
|
|
#include <asm/unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <mcheck.h>
|
|
#include <err.h>
|
|
|
|
#include "winnt_types.h"
|
|
#include "pe_linker.h"
|
|
#include "ntoskernel.h"
|
|
#include "util.h"
|
|
#include "hook.h"
|
|
#include "log.h"
|
|
#include "rsignal.h"
|
|
#include "engineboot.h"
|
|
#include "scanreply.h"
|
|
#include "streambuffer.h"
|
|
#include "openscan.h"
|
|
|
|
// Any usage limits to prevent bugs disrupting system.
|
|
const struct rlimit kUsageLimits[] = {
|
|
[RLIMIT_FSIZE] = { .rlim_cur = 0x20000000, .rlim_max = 0x20000000 },
|
|
[RLIMIT_CPU] = { .rlim_cur = 3600, .rlim_max = RLIM_INFINITY },
|
|
[RLIMIT_CORE] = { .rlim_cur = 0, .rlim_max = 0 },
|
|
[RLIMIT_NOFILE] = { .rlim_cur = 32, .rlim_max = 32 },
|
|
};
|
|
|
|
DWORD (* __rsignal)(PHANDLE KernelHandle, DWORD Code, PVOID Params, DWORD Size);
|
|
|
|
static DWORD EngineScanCallback(PSCANSTRUCT Scan)
|
|
{
|
|
if (Scan->Flags & SCAN_MEMBERNAME) {
|
|
LogMessage("Scanning archive member %s", Scan->VirusName);
|
|
}
|
|
if (Scan->Flags & SCAN_FILENAME) {
|
|
LogMessage("Scanning %s", Scan->FileName);
|
|
}
|
|
if (Scan->Flags & SCAN_PACKERSTART) {
|
|
LogMessage("Packer %s identified.", Scan->VirusName);
|
|
}
|
|
if (Scan->Flags & SCAN_ENCRYPTED) {
|
|
LogMessage("File is encrypted.");
|
|
}
|
|
if (Scan->Flags & SCAN_CORRUPT) {
|
|
LogMessage("File may be corrupt.");
|
|
}
|
|
if (Scan->Flags & SCAN_FILETYPE) {
|
|
LogMessage("File %s is identified as %s", Scan->FileName, Scan->VirusName);
|
|
}
|
|
if (Scan->Flags & 0x08000022) {
|
|
LogMessage("Threat %s identified.", Scan->VirusName);
|
|
}
|
|
// This may indicate PUA.
|
|
if ((Scan->Flags & 0x40010000) == 0x40010000) {
|
|
LogMessage("Threat %s identified.", Scan->VirusName);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static DWORD ReadStream(PVOID this, ULONGLONG Offset, PVOID Buffer, DWORD Size, PDWORD SizeRead)
|
|
{
|
|
fseek(this, Offset, SEEK_SET);
|
|
*SizeRead = fread(Buffer, 1, Size, this);
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD GetStreamSize(PVOID this, PULONGLONG FileSize)
|
|
{
|
|
fseek(this, 0, SEEK_END);
|
|
*FileSize = ftell(this);
|
|
return TRUE;
|
|
}
|
|
|
|
static PWCHAR GetStreamName(PVOID this)
|
|
{
|
|
return L"input";
|
|
}
|
|
|
|
// These are available for pintool.
|
|
BOOL __noinline InstrumentationCallback(PVOID ImageStart, SIZE_T ImageSize)
|
|
{
|
|
// Prevent the call from being optimized away.
|
|
asm volatile ("");
|
|
return TRUE;
|
|
}
|
|
|
|
int main(int argc, char **argv, char **envp)
|
|
{
|
|
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) {
|
|
LogMessage("You must add the dll and vdm files to the engine directory");
|
|
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")) {
|
|
#ifndef NDEBUG
|
|
LogMessage("The map file wasn't found, symbols wont be available");
|
|
#endif
|
|
} else {
|
|
// 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) {
|
|
errx(EXIT_FAILURE, "Failed to resolve mpengine entrypoint");
|
|
}
|
|
|
|
EXCEPTION_DISPOSITION ExceptionHandler(struct _EXCEPTION_RECORD *ExceptionRecord,
|
|
struct _EXCEPTION_FRAME *EstablisherFrame,
|
|
struct _CONTEXT *ContextRecord,
|
|
struct _EXCEPTION_FRAME **DispatcherContext)
|
|
{
|
|
LogMessage("Toplevel Exception Handler Caught Exception");
|
|
abort();
|
|
}
|
|
|
|
VOID ResourceExhaustedHandler(int Signal)
|
|
{
|
|
errx(EXIT_FAILURE, "Resource Limits Exhausted, Signal %s", strsignal(Signal));
|
|
}
|
|
|
|
setup_nt_threadinfo(ExceptionHandler);
|
|
|
|
// Call DllMain()
|
|
image.entry((PVOID) 'MPEN', DLL_PROCESS_ATTACH, NULL);
|
|
|
|
// Install usage limits to prevent system crash.
|
|
setrlimit(RLIMIT_CORE, &kUsageLimits[RLIMIT_CORE]);
|
|
setrlimit(RLIMIT_CPU, &kUsageLimits[RLIMIT_CPU]);
|
|
setrlimit(RLIMIT_FSIZE, &kUsageLimits[RLIMIT_FSIZE]);
|
|
setrlimit(RLIMIT_NOFILE, &kUsageLimits[RLIMIT_NOFILE]);
|
|
|
|
signal(SIGXCPU, ResourceExhaustedHandler);
|
|
signal(SIGXFSZ, ResourceExhaustedHandler);
|
|
|
|
# ifndef NDEBUG
|
|
// Enable Maximum heap checking.
|
|
mcheck_pedantic(NULL);
|
|
# endif
|
|
|
|
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;
|
|
|
|
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;
|
|
ScanDescriptor.GetName = GetStreamName;
|
|
|
|
if (argc < 2) {
|
|
LogMessage("usage: %s [filenames...]", *argv);
|
|
return 1;
|
|
}
|
|
|
|
// Enable Instrumentation.
|
|
InstrumentationCallback(image.image, image.size);
|
|
|
|
for (char *filename = *++argv; *argv; ++argv) {
|
|
ScanDescriptor.UserPtr = fopen(*argv, "r");
|
|
|
|
if (ScanDescriptor.UserPtr == NULL) {
|
|
LogMessage("failed to open file %s", *argv);
|
|
return 1;
|
|
}
|
|
|
|
LogMessage("Scanning %s...", *argv);
|
|
|
|
if (__rsignal(&KernelHandle, RSIG_SCAN_STREAMBUFFER, &ScanParams, sizeof ScanParams) != 0) {
|
|
LogMessage("__rsignal(RSIG_SCAN_STREAMBUFFER) returned failure, file unreadable?");
|
|
return 1;
|
|
}
|
|
|
|
fclose(ScanDescriptor.UserPtr);
|
|
}
|
|
|
|
return 0;
|
|
}
|