Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,18 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release delete 1.1.0 --cleanup-tag --yes || echo "No release or tag found for 1.1.0"
gh release delete 1.1.1 --cleanup-tag --yes || echo "No release or tag found for 1.1.1"

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: 1.1.0
release_name: DllShimmer 1.1.0
tag_name: 1.1.1
release_name: DllShimmer 1.1.1
body: |
- [x] Dynamic linking is now cached (both LoadLibraryA and GetProcAddress). Performance improved.
- [x] Better debug log format: timestamp added.
- [x] New parameter: `--debug-file`. Save debug logs to file.
- [x] README updated.
- [x] "Debug cannot be disabled" bug fixed.
draft: false
prerelease: false

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,4 @@ In case of static linking, we really only have one option:

## TODO

- Cache LoadLibraryA() and GetProcAddress() pointers not to call WinAPI every time (better performance and more stealthy).
- Support C++ mangled function names
33 changes: 22 additions & 11 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import (
"strings"
)

const VERSION = "1.1.1"

type CliFlags struct {
Input string
Output string
Original string
Mutex bool
Static bool
DebugFile string
Input string
Output string
Original string
Mutex bool
Static bool
DebugFile string
ShowVersion bool
}

func IsValidDllName(filename string) bool {
Expand Down Expand Up @@ -45,14 +48,16 @@ func ParseCli() *CliFlags {
flag.StringVar(&flags.Original, "x", "", "")
flag.StringVar(&flags.Original, "original", "", "")

flag.StringVar(&flags.DebugFile, "d", "", "")
flag.StringVar(&flags.DebugFile, "debug-file", "", "")

flag.BoolVar(&flags.Mutex, "m", false, "")
flag.BoolVar(&flags.Mutex, "mutex", false, "")

flag.BoolVar(&flags.Static, "static", false, "")

flag.BoolVar(&flags.ShowVersion, "v", false, "")
flag.BoolVar(&flags.ShowVersion, "version", false, "")

flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: DllShimmer -i <path> -o <path> -p <path>\n")
fmt.Println()
Expand All @@ -62,8 +67,9 @@ func ParseCli() *CliFlags {
fmt.Printf(" %-26s %s\n", "-o, --output <path>", "Output directory (required)")
fmt.Printf(" %-26s %s\n", "-x, --original <path>", "Path to original DLL on target (required)")
fmt.Printf(" %-26s %s\n", "-m, --mutex", "Multiple execution prevention (default: false)")
fmt.Printf(" %-26s %s\n", " --debug-file <path>", "Save debug logs to a file (default: stdout)")
fmt.Printf(" %-26s %s\n", " --static", "Static linking to original DLL via IAT (default: false)")
fmt.Printf(" %-26s %s\n", " --debug-file <path>", "Save debug logs to a file (default: stdout)")
fmt.Printf(" %-26s %s\n", "-v, --version", "Show version of DllShimmer")
fmt.Printf(" %-26s %s\n", "-h, --help", "Show this help")
fmt.Println()
fmt.Println("Example:")
Expand All @@ -76,6 +82,11 @@ func ParseCli() *CliFlags {

flag.Parse()

if flags.ShowVersion {
fmt.Printf("DllShimmer %s\n", VERSION)
os.Exit(0)
}

if flags.Input == "" || flags.Output == "" || flags.Original == "" {
flag.Usage()
os.Exit(1)
Expand All @@ -91,15 +102,15 @@ func ParseCli() *CliFlags {
}

func PrintBanner() {
banner := `
banner := fmt.Sprintf(`
▓█████▄ ██▓ ██▓
▒██▀ ██▌▓██▒ ▓██▒ By @Print3M
░██ █▌▒██░ ▒██░ (print3m.github.io)
░▓█▄ ▌▒██░ ▒██░
░▒████▓ ░██████▒░██████▒ Documentation:
▒▒▓ ▒ ░ ▒░▓ ░░ ▒░▓ ░ github.com/Print3M/DllShimmer
░ ▒ ▒ ░ ░ ▒ ░░ ░ ▒ ░
░ ░ ░ ░ ░ ░ ░ 2025
░ ░ ░ ░ ░ ░ ░ %s
░ ░ ░ ░ ░
██████ ██░ ██ ██▓ ███▄ ▄███▓ ███▄ ▄███▓▓█████ ██▀███
Expand All @@ -111,7 +122,7 @@ func PrintBanner() {
░ ░▒ ░ ░ ▒ ░▒░ ░ ▒ ░░ ░ ░░ ░ ░ ░ ░ ░ ░▒ ░ ▒░
░ ░ ░ ░ ░░ ░ ▒ ░░ ░ ░ ░ ░ ░░ ░
░ ░ ░ ░ ░ ░ ░ ░ ░ ░
`
`, VERSION)

fmt.Print(banner)
fmt.Println()
Expand Down
45 changes: 26 additions & 19 deletions templates/dllshimmer.h.template
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ void initDbg() {
{{- end }}
}

char gTimeBuf[9]; // "HH:MM:SS" + null
char gTimeBuf[9]; // "HH:MM:SS" + \0

char *getCurrentTime() {
time_t t = time(NULL);
Expand All @@ -61,26 +61,29 @@ char *getCurrentTime() {
}

void dbgf(const char *fmt, ...) {
if (gCtx.dbgOut == NULL) {
initDbg();
}

va_list ap;
va_start(ap, fmt);

fprintf(gCtx.dbgOut, "[DBG] {{.DllName}} | %s | ", getCurrentTime());
vfprintf(gCtx.dbgOut, fmt, ap);
fprintf(gCtx.dbgOut, "\n");
fflush(gCtx.dbgOut);

va_end(ap);
#ifdef DEBUG
if (gCtx.dbgOut == NULL) {
initDbg();
}

va_list ap;
va_start(ap, fmt);

fprintf(gCtx.dbgOut, "[DBG] {{.DllName}} | %s | ", getCurrentTime());
vfprintf(gCtx.dbgOut, fmt, ap);
fprintf(gCtx.dbgOut, "\n");
fflush(gCtx.dbgOut);

va_end(ap);
#endif
}

void dbgCurrentDirectory() {
char buf[MAX_PATH];
DWORD len = GetCurrentDirectoryA(MAX_PATH, buf);
if (len == 0 || len >= MAX_PATH) {
dbgf("GetCurrentDirectoryA failed");

return;
}

Expand All @@ -92,9 +95,11 @@ FuncPtr getProxyFunc(const char *funcName) {
if (gCtx.module == NULL) {
gCtx.module = LoadLibraryA("{{.Original}}");
if (gCtx.module == NULL) {
dbgf("LoadLibraryA({{.Original}}) failed");
dbgf("\tError code: %lu", GetLastError());
dbgCurrentDirectory();
#ifdef DEBUG
dbgf("LoadLibraryA({{.Original}}) failed");
dbgf("\tError code: %lu", GetLastError());
dbgCurrentDirectory();
#endif

return NULL;
}
Expand All @@ -109,8 +114,10 @@ FuncPtr getProxyFunc(const char *funcName) {

FuncPtr pFunc = (FuncPtr)GetProcAddress(gCtx.module, funcName);
if (pFunc == NULL) {
dbgf("GetProcAddress(%s, {{.Original}}) failed", funcName);
dbgf("\tError code: %lu", GetLastError());
#ifdef DEBUG
dbgf("GetProcAddress(%s, {{.Original}}) failed", funcName);
dbgf("\tError code: %lu", GetLastError());
#endif
}

gCtx.functions[strFuncName] = pFunc;
Expand Down
1 change: 1 addition & 0 deletions templates/dynamic-shim.cpp.template
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

{{- if eq (len $v.Forwarder) 0 }}

// {{$v.Name}}
extern "C" UINT64 {{$v.Name}}Fwd(PARAMS) {
#ifdef DEBUG
dbgf("{{$v.Name}} called");
Expand Down
1 change: 1 addition & 0 deletions templates/static-shim.cpp.template
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

extern "C" __declspec(dllimport) UINT64 {{$v.Name}}(PARAMS);

// {{$v.Name}}
extern "C" UINT64 {{$v.Name}}Fwd(PARAMS) {
#ifdef DEBUG
dbgf("{{$v.Name}} called");
Expand Down
Loading