mirror of
https://github.com/Print3M/DllShimmer
synced 2026-06-06 16:34:32 +00:00
Join static and dynamic linking
This commit is contained in:
@@ -15,16 +15,8 @@ Features:
|
||||
- Forwarded functions are forwarded as original.
|
||||
- Both MSVC (`#pragma comment`) and GCC forwarding (`.def file`) are supported.
|
||||
|
||||
Caveats:
|
||||
Limitations:
|
||||
|
||||
1. Probably it doesn't work with floating-point parameters because they require different registers from va_list.
|
||||
2. Original ordinal numbers of exported and forwarded functions are not preserved
|
||||
3. There are some huge obfuscated DLLs with weird name mangling and tricks (e.g. Qt framework DLL). I don't recommend to use them as a backdoor base. Just use some normal DLL with 10-30 exported functions and it's going to work perfectly.
|
||||
|
||||
## Nowa architektura
|
||||
|
||||
Najlepiej by było, gdyby original.dll lądował w IAT backdoor.dll ze wszystkimi funkcjami. Wtedy mamy wszystko dostępne od razu, bez dodatkowego używania WinAPI.
|
||||
|
||||
Pojawia się wtedy problem, że nie można importować i eksportować tych samych symboli.
|
||||
|
||||
- Importuj oryginalne
|
||||
1. Probably it doesn't work with floating-point parameters because they require different registers to be used in ABI but I might be wrong, TODO: check
|
||||
2. There are some huge obfuscated DLLs with weird name mangling and tricks (e.g. Qt framework DLL). I don't recommend to use them as a backdoor base. Just use some normal DLL with 10-30 exported functions and it's going to work perfectly.
|
||||
3. It supports only x86-64.
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
set -ueo pipefail
|
||||
|
||||
go run main.go -i '7z.dll' -p '7z2.dll' --def 'dll.def' -m > shim.cpp
|
||||
# go run main.go -i '7z.dll' -p '7z2.dll' --def 'dll.def' -m > shim.cpp
|
||||
|
||||
x86_64-w64-mingw32-g++ -shared shim.cpp dll.def -o 7z.dll-shim -static-libstdc++ -static-libgcc -D DEBUG=1
|
||||
# x86_64-w64-mingw32-g++ -shared shim.cpp dll.def -o 7z.dll-shim -static-libstdc++ -static-libgcc -D DEBUG=1
|
||||
|
||||
mv 7z.dll-shim ~/vm/Windows\ 11/shared/7z.dll
|
||||
# mv 7z.dll-shim ~/vm/Windows\ 11/shared/7z.dll
|
||||
|
||||
|
||||
x86_64-w64-mingw32-g++ -shared 7z.dll.cpp 7z.dll.def -o test.dll -L . -l original -static-libstdc++ -static-libgcc -D DEBUG=1
|
||||
+29
-2
@@ -3,14 +3,34 @@ package cli
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CliFlags struct {
|
||||
Input string
|
||||
Output string
|
||||
Proxy string
|
||||
Mutex bool
|
||||
Output string
|
||||
Static bool
|
||||
}
|
||||
|
||||
func IsValidWindowsDllName(filename string) bool {
|
||||
invalidChars := []rune{'<', '>', ':', '"', '/', '\\', '|', '?', '*'}
|
||||
|
||||
// Check for invalid characters
|
||||
for _, char := range invalidChars {
|
||||
if strings.ContainsRune(filename, char) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(filename, ".dll") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func ParseCli() *CliFlags {
|
||||
@@ -28,6 +48,8 @@ func ParseCli() *CliFlags {
|
||||
flag.BoolVar(&flags.Mutex, "m", false, "")
|
||||
flag.BoolVar(&flags.Mutex, "mutex", false, "")
|
||||
|
||||
flag.BoolVar(&flags.Static, "static", false, "")
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: DllShimmer -i <path> -o <path> -p <path>\n")
|
||||
fmt.Println()
|
||||
@@ -35,8 +57,9 @@ func ParseCli() *CliFlags {
|
||||
fmt.Println()
|
||||
fmt.Printf(" %-24s %s\n", "-i, --input <path>", "Input DLL file (required)")
|
||||
fmt.Printf(" %-24s %s\n", "-o, --output <path>", "Output directory (required)")
|
||||
fmt.Printf(" %-24s %s\n", "-p, --proxy <path>", "Path to original DLL on target (required)")
|
||||
fmt.Printf(" %-24s %s\n", "-p, --proxy <path>", "Original DLL on target (required)")
|
||||
fmt.Printf(" %-24s %s\n", "-m, --mutex", "Multiple execution prevention (default: false)")
|
||||
fmt.Printf(" %-24s %s\n", "--static", "Original DLL loaded via static IAT (default: false)")
|
||||
fmt.Printf(" %-24s %s\n", "-h, --help", "Show this help")
|
||||
fmt.Println()
|
||||
fmt.Println("Example:")
|
||||
@@ -54,5 +77,9 @@ func ParseCli() *CliFlags {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if flags.Static && !IsValidWindowsDllName(flags.Proxy) {
|
||||
log.Fatalln("[!] In case of static linking enabled the proxy file (-p, --proxy) must be valid Windows DLL file name with no path information. E.g. kernel32.dll, user32.dll")
|
||||
}
|
||||
|
||||
return &flags
|
||||
}
|
||||
|
||||
+20
-11
@@ -1,6 +1,7 @@
|
||||
package def
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
@@ -9,6 +10,7 @@ type exportedFunction struct {
|
||||
OriginalName string
|
||||
Rename string
|
||||
Forwarder string
|
||||
Ordinal uint32
|
||||
}
|
||||
|
||||
type DefFile struct {
|
||||
@@ -16,49 +18,56 @@ type DefFile struct {
|
||||
exportedFunctions []exportedFunction
|
||||
}
|
||||
|
||||
func (d *DefFile) AddExportedFunction(name string) {
|
||||
func (d *DefFile) AddExportedFunction(name string, ordinal uint32) {
|
||||
d.exportedFunctions = append(d.exportedFunctions, exportedFunction{
|
||||
OriginalName: name,
|
||||
Ordinal: ordinal,
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DefFile) AddRenamedFunction(originalName string, rename string) {
|
||||
func (d *DefFile) AddRenamedFunction(originalName string, rename string, ordinal uint32) {
|
||||
d.exportedFunctions = append(d.exportedFunctions, exportedFunction{
|
||||
OriginalName: originalName,
|
||||
Rename: rename,
|
||||
Ordinal: ordinal,
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DefFile) AddForwardedFunction(originalName string, forwarder string) {
|
||||
func (d *DefFile) AddForwardedFunction(originalName string, forwarder string, ordinal uint32) {
|
||||
d.exportedFunctions = append(d.exportedFunctions, exportedFunction{
|
||||
OriginalName: originalName,
|
||||
Forwarder: forwarder,
|
||||
Ordinal: ordinal,
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DefFile) SaveFile(path string) {
|
||||
func (d *DefFile) SaveFile(path string, withOrdinals bool) {
|
||||
var content string
|
||||
|
||||
content += "LIBRARY \"" + d.DllName + "\"\n"
|
||||
content += "EXPORTS\n"
|
||||
|
||||
for _, function := range d.exportedFunctions {
|
||||
if function.Forwarder == "" && function.Rename == "" {
|
||||
content += "\t" + function.OriginalName
|
||||
}
|
||||
|
||||
if function.Forwarder != "" {
|
||||
// Forwarded functions
|
||||
content += "\t" + function.OriginalName + "=" + function.Forwarder + "\n"
|
||||
continue
|
||||
content += "\t" + function.OriginalName + "=" + function.Forwarder
|
||||
}
|
||||
|
||||
if function.Rename != "" {
|
||||
// Exported-renamed functions
|
||||
content += "\t" + function.Rename + "=" + function.OriginalName + "\n"
|
||||
continue
|
||||
content += "\t" + function.OriginalName + "=" + function.Rename
|
||||
}
|
||||
|
||||
content += "\t" + function.OriginalName + "\n"
|
||||
}
|
||||
if withOrdinals {
|
||||
content += " " + "@" + fmt.Sprintf("%d", function.Ordinal)
|
||||
}
|
||||
|
||||
content += "\n"
|
||||
content += "\n"
|
||||
}
|
||||
|
||||
err := os.WriteFile(path, []byte(content), 0644)
|
||||
if err != nil {
|
||||
|
||||
+8
-6
@@ -13,6 +13,7 @@ import (
|
||||
type ExportedFunction struct {
|
||||
Name string
|
||||
Forwarder string
|
||||
Ordinal uint32
|
||||
}
|
||||
|
||||
type Dll struct {
|
||||
@@ -39,21 +40,22 @@ func ParseDll(path string) *Dll {
|
||||
dll.ExportedFunctions = append(dll.ExportedFunctions, ExportedFunction{
|
||||
Name: function.Name,
|
||||
Forwarder: function.Forwarder,
|
||||
Ordinal: function.Ordinal,
|
||||
})
|
||||
}
|
||||
|
||||
return &dll
|
||||
}
|
||||
|
||||
func (d *Dll) CreateLibFile(path string) {
|
||||
func (d *Dll) CreateLibFile(path string, proxyName string) {
|
||||
var def def.DefFile
|
||||
def.DllName = d.Name
|
||||
def.DllName = proxyName
|
||||
|
||||
for _, function := range d.ExportedFunctions {
|
||||
if function.Forwarder == "" {
|
||||
def.AddExportedFunction(function.Name)
|
||||
def.AddExportedFunction(function.Name, function.Ordinal)
|
||||
} else {
|
||||
def.AddForwardedFunction(function.Name, function.Forwarder)
|
||||
def.AddForwardedFunction(function.Name, function.Forwarder, function.Ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +65,10 @@ func (d *Dll) CreateLibFile(path string) {
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
def.SaveFile(f.Name())
|
||||
def.SaveFile(f.Name(), true)
|
||||
|
||||
// Convert DLL to .lib file
|
||||
cmd := exec.Command("x86_64-w64-mingw32-dlltool", "-d", f.Name(), "-l", path)
|
||||
cmd := exec.Command("x86_64-w64-mingw32-dlltool", "-d", f.Name(), "-l", path, "-m", "i386:x86-64")
|
||||
_, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"dllshimmer/def"
|
||||
"dllshimmer/dll"
|
||||
"dllshimmer/tmpl"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
@@ -21,24 +22,51 @@ func main() {
|
||||
params.DllName = filepath.Base(flags.Input)
|
||||
params.Mutex = flags.Mutex
|
||||
|
||||
tmpl.CreateCodeFile(outputDir, params)
|
||||
if flags.Static {
|
||||
tmpl.CreateCodeFile(outputDir, params, "templates/static-shim.c.template")
|
||||
|
||||
// 1. Create temp .def based on original file
|
||||
dll.CreateLibFile(filepath.Join(outputDir, "original.lib"))
|
||||
// Create .lib based on original DLL
|
||||
dll.CreateLibFile(filepath.Join(outputDir, "original.lib"), params.ProxyDll)
|
||||
} else {
|
||||
tmpl.CreateCodeFile(outputDir, params, "templates/dynamic-shim.c.template")
|
||||
}
|
||||
|
||||
// 2. Create new .def based on generated code
|
||||
func() {
|
||||
var def def.DefFile
|
||||
def.DllName = params.DllName
|
||||
|
||||
for _, function := range dll.ExportedFunctions {
|
||||
if function.Forwarder == "" {
|
||||
def.AddRenamedFunction(function.Name, function.Name+"Fwd")
|
||||
def.AddRenamedFunction(function.Name, function.Name+"Fwd", function.Ordinal)
|
||||
} else {
|
||||
def.AddForwardedFunction(function.Name, function.Forwarder)
|
||||
def.AddForwardedFunction(function.Name, function.Forwarder, function.Ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
def.SaveFile(filepath.Join(outputDir, params.DllName+".def"))
|
||||
def.SaveFile(filepath.Join(outputDir, params.DllName+".def"), true)
|
||||
}()
|
||||
|
||||
codeFile := filepath.Join(outputDir, params.DllName+".cpp")
|
||||
defFile := filepath.Join(outputDir, params.DllName+".def")
|
||||
dllFile := filepath.Join(outputDir, params.DllName)
|
||||
|
||||
var cmd string
|
||||
if flags.Static {
|
||||
cmd = fmt.Sprintf(
|
||||
"x86_64-w64-mingw32-g++ -shared %s %s -o %s -L %s -l original -static-libstdc++ -static-libgcc -D DEBUG=1",
|
||||
codeFile,
|
||||
defFile,
|
||||
dllFile,
|
||||
outputDir,
|
||||
)
|
||||
} else {
|
||||
cmd = fmt.Sprintf(
|
||||
"x86_64-w64-mingw32-g++ -shared %s %s -o %s -static-libstdc++ -static-libgcc -D DEBUG=1",
|
||||
codeFile,
|
||||
defFile,
|
||||
dllFile,
|
||||
)
|
||||
}
|
||||
|
||||
println(cmd)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{{- $r := . }}
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
// Put your imports here...
|
||||
@@ -7,15 +9,6 @@
|
||||
// | (auto generated) |
|
||||
// #------------------------------------------------------------------#
|
||||
|
||||
{{- range .Functions}}
|
||||
#define {{.Name}} {{.Name}}Original
|
||||
{{- end}}
|
||||
#include <windows.h>
|
||||
{{- range .Functions}}
|
||||
#undef {{.Name}}
|
||||
{{- end}}
|
||||
{{ $r := . }}
|
||||
|
||||
#define MUTEX(name) \
|
||||
(CreateMutexA(NULL, TRUE, name) && GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
|
||||
@@ -64,9 +57,7 @@ typedef uint64_t (*Func12)(
|
||||
{{- range $i, $v := .Functions }}
|
||||
{{- if eq (len $v.Forwarder) 0 }}
|
||||
|
||||
__declspec(dllimport) UINT64 {{$v.Name}}(UINT64 arg1, ...);
|
||||
|
||||
extern "C" __declspec(dllexport) UINT64 {{$v.Name}}Fwd(UINT64 arg1, ...) {
|
||||
extern "C" UINT64 {{$v.Name}}Fwd(UINT64 arg1, ...) {
|
||||
#ifdef DEBUG
|
||||
printf("[+] {{$r.DllName}}: {{$v.Name}} called\n");
|
||||
#endif
|
||||
@@ -1,109 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
// Put your imports here...
|
||||
|
||||
// #------------------------------------------------------------------#
|
||||
// | "DON'T TOUCH" ZONE |
|
||||
// | (auto generated) |
|
||||
// #------------------------------------------------------------------#
|
||||
|
||||
{{- range .Functions}}
|
||||
#define {{.Name}} {{.Name}}Original
|
||||
{{- end}}
|
||||
#include <windows.h>
|
||||
{{- range .Functions}}
|
||||
#undef {{.Name}}
|
||||
{{- end}}
|
||||
{{ $r := . }}
|
||||
|
||||
#define MUTEX(name) \
|
||||
(CreateMutexA(NULL, TRUE, name) && GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
|
||||
#define ARGS_COUNT 12
|
||||
|
||||
typedef uint64_t (*Func12)(
|
||||
uint64_t, uint64_t, uint64_t, uint64_t,
|
||||
uint64_t, uint64_t, uint64_t, uint64_t,
|
||||
uint64_t, uint64_t, uint64_t, uint64_t
|
||||
);
|
||||
|
||||
#define PROXY_FUNCTION(function) \
|
||||
va_list ap; \
|
||||
va_start(ap, arg1); \
|
||||
uint64_t args[ARGS_COUNT]; \
|
||||
args[0] = arg1; \
|
||||
\
|
||||
for (int i = 1; i < ARGS_COUNT ; i++) { \
|
||||
args[i] = va_arg(ap, uint64_t); \
|
||||
} \
|
||||
\
|
||||
va_end(ap); \
|
||||
\
|
||||
HMODULE hModule = LoadLibraryA("{{.ProxyDll}}"); \
|
||||
if (hModule == NULL) { \
|
||||
printf("[!] {{.DllName}}: LoadLibraryA({{.ProxyDll}}) failed\n"); \
|
||||
printf("\tError code: %lu\n", GetLastError()); \
|
||||
} \
|
||||
\
|
||||
Func12 pFunction = (Func12) GetProcAddress(hModule, function); \
|
||||
if (pFunction == NULL) { \
|
||||
printf( \
|
||||
"[!] {{.DllName}}: GetProcAddress(%s, {{.ProxyDll}}) failed\n", \
|
||||
function ); \
|
||||
printf("\tError code: %lu\n", GetLastError()); \
|
||||
\
|
||||
} \
|
||||
\
|
||||
return pFunction(args[0], args[1], args[2], args[3], args[4], args[5], \
|
||||
args[6], args[7], args[8], args[9], args[10], args[11]); \
|
||||
|
||||
// ---- Forwarded functions ------------------------------------------
|
||||
|
||||
{{- range .Functions }}
|
||||
{{- if .Forwarder }}
|
||||
#pragma comment(linker, "/EXPORT:{{.Name}}={{.Forwarder}}")
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
// #------------------------------------------------------------------#
|
||||
// | END OF "DON'T TOUCH" ZONE |
|
||||
// #------------------------------------------------------------------#
|
||||
|
||||
{{- range $i, $v := .Functions }}
|
||||
{{- if eq (len $v.Forwarder) 0 }}
|
||||
|
||||
extern "C" __declspec(dllexport) UINT64 {{$v.Name}}(UINT64 arg1, ...) {
|
||||
#ifdef DEBUG
|
||||
printf("[+] {{$r.DllName}}: {{$v.Name}} called\n");
|
||||
#endif
|
||||
{{ if $r.Mutex }}
|
||||
if (MUTEX("Global\\{{$v.Name}}__{{$i}}")) {
|
||||
// Put your code here...
|
||||
}
|
||||
{{- else }}
|
||||
// Put your code here...
|
||||
{{- end }}
|
||||
|
||||
PROXY_FUNCTION("{{$v.Name}}");
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
||||
|
||||
switch (fdwReason) {
|
||||
case DLL_PROCESS_ATTACH: {
|
||||
#ifdef DEBUG
|
||||
printf("[+] {{.DllName}}: DLL_PROCESS_ATTACH event\n");
|
||||
#endif
|
||||
}
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
// Put your imports here...
|
||||
|
||||
// #------------------------------------------------------------------#
|
||||
// | "DON'T TOUCH" ZONE |
|
||||
// | (auto generated) |
|
||||
// #------------------------------------------------------------------#
|
||||
|
||||
{{- range .Functions}}
|
||||
#define {{.Name}} {{.Name}}Original
|
||||
{{- end}}
|
||||
#include <windows.h>
|
||||
{{- range .Functions}}
|
||||
#undef {{.Name}}
|
||||
{{- end}}
|
||||
{{ $r := . }}
|
||||
|
||||
#define MUTEX(name) \
|
||||
(CreateMutexA(NULL, TRUE, name) && GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
|
||||
#define T UINT64
|
||||
#define PARAMS T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10, T a11, T a12
|
||||
#define ARGS a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12
|
||||
|
||||
// #------------------------------------------------------------------#
|
||||
// | END OF "DON'T TOUCH" ZONE |
|
||||
// #------------------------------------------------------------------#
|
||||
|
||||
{{- range $i, $v := .Functions }}
|
||||
{{- if eq (len $v.Forwarder) 0 }}
|
||||
|
||||
extern "C" __declspec(dllimport) UINT64 {{$v.Name}}(PARAMS);
|
||||
|
||||
extern "C" UINT64 {{$v.Name}}Fwd(PARAMS) {
|
||||
#ifdef DEBUG
|
||||
printf("[+] {{$r.DllName}}: {{$v.Name}} called\n");
|
||||
#endif
|
||||
{{ if $r.Mutex }}
|
||||
if (MUTEX("Global\\{{$v.Name}}__{{$i}}")) {
|
||||
// Put your code here...
|
||||
}
|
||||
{{- else }}
|
||||
// Put your code here...
|
||||
{{- end }}
|
||||
|
||||
MessageBoxA(NULL, "{{$v.Name}}", "My Message Box", MB_OK);
|
||||
|
||||
return {{$v.Name}}(ARGS);
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
||||
|
||||
switch (fdwReason) {
|
||||
case DLL_PROCESS_ATTACH: {
|
||||
#ifdef DEBUG
|
||||
printf("[+] {{.DllName}}: DLL_PROCESS_ATTACH event\n");
|
||||
#endif
|
||||
}
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
+2
-2
@@ -15,8 +15,8 @@ type TemplateParams struct {
|
||||
Mutex bool
|
||||
}
|
||||
|
||||
func CreateCodeFile(outputDir string, params TemplateParams) {
|
||||
tmpl := template.Must(template.ParseFiles("templates/shim.c.template"))
|
||||
func CreateCodeFile(outputDir string, params TemplateParams, path string) {
|
||||
tmpl := template.Must(template.ParseFiles(path))
|
||||
|
||||
f, err := os.Create(filepath.Join(outputDir, params.DllName+".cpp"))
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user