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
14 changes: 7 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,21 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release delete 1.0.5 --cleanup-tag --yes || echo "No release or tag found for 1.0.5"
gh release delete 1.1.0 --cleanup-tag --yes || echo "No release or tag found for 1.1.0"

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: 1.0.5
release_name: DllShimmer 1.0.5
tag_name: 1.1.0
release_name: DllShimmer 1.1.0
body: |
DllShimmer Weaponize DLL hijacking easily. Backdoor any function in any DLL.

- [x] GitHub Actions workflow improvements
- [x] Windows build improvements
- [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.
draft: false
prerelease: false

Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"files.associations": {
"*.cpp.template": "cpp",
"*.sh.template": "shellscript",
"functional": "cpp"
"functional": "cpp",
"*.template": "cpp",
"cstdarg": "cpp"
}
}
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ Example:

Parameters:

**`-i / --input <file>`** [required]
**`-i / --input <path>`** [required]

The original DLL that you want to backdoor.

**`-o / --output <dir>`** [required]
**`-o / --output <path>`** [required]

The path to the directory where DllShimmer will save all generated files.

**`-x / --original-path <path | file>`** [required]
**`-x / --original <path>`** [required]

In case of dynamic linking (default) provide the path where the proxy DLL will find the original DLL on the target system.

Expand All @@ -67,7 +67,17 @@ This technique has some serious limitations compared to dynamic linking:

However, static linking may be more stealthy and natural in some scenarios.

By default, DllShimmer always uses dynamic linking with the `LoadLibraryA()` and `GetProcAddress()` functions.
Default: DllShimmer always uses dynamic linking with the `LoadLibraryA()` and `GetProcAddress()` functions.

**`--debug-file <path>`** [optional]

Save debug logs to a file. Logs are written to a file on an ongoing basis while the program is running. If selected, logs are not printed to STDOUT.

Default: DllShimmer always writes debug logs to STDOUT.

Example debug output:

![Example debug output](_img/img-2.png)

## Limitations

Expand All @@ -78,6 +88,12 @@ By default, DllShimmer always uses dynamic linking with the `LoadLibraryA()` and

## Troubleshooting

Before you start troubleshooting:

1. Read "Limitations".
2. Make sure you don't use static linking (`--static`). It's easier to debug with dynamic linking (default).
3. Save debug output to file (`--debug-file`).

### _In the generated `.cpp` file, I don't see all the exported functions from the original DLL._

Functions defined in the original DLL as “forwarded” are not included in the `.cpp` file. However, they are visible in the `.def` file. They will also be exported after compilation, exactly as in the original DLL.
Expand All @@ -102,5 +118,3 @@ 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).
- Improve the shim template code (leave as little code in the macro as possible. Is the macro actually required now when we use args/params trick?)
- Maybe move boilerplate code into header file?
Binary file added _img/img-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 17 additions & 12 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
)

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

func IsValidDllName(filename string) bool {
Expand Down Expand Up @@ -41,8 +42,11 @@ func ParseCli() *CliFlags {
flag.StringVar(&flags.Output, "o", "", "")
flag.StringVar(&flags.Output, "output", "", "")

flag.StringVar(&flags.OriginalPath, "x", "", "")
flag.StringVar(&flags.OriginalPath, "original-path", "", "")
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, "")
Expand All @@ -54,10 +58,11 @@ func ParseCli() *CliFlags {
fmt.Println()
fmt.Println("Usage:")
fmt.Println()
fmt.Printf(" %-26s %s\n", "-i, --input <file>", "Input DLL file (required)")
fmt.Printf(" %-26s %s\n", "-o, --output <dir>", "Output directory (required)")
fmt.Printf(" %-26s %s\n", "-x, --original-path <path>", "Path to original DLL on target (required)")
fmt.Printf(" %-26s %s\n", "-i, --input <path>", "Input DLL file (required)")
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", "-h, --help", "Show this help")
fmt.Println()
Expand All @@ -71,12 +76,12 @@ func ParseCli() *CliFlags {

flag.Parse()

if flags.Input == "" || flags.Output == "" || flags.OriginalPath == "" {
if flags.Input == "" || flags.Output == "" || flags.Original == "" {
flag.Usage()
os.Exit(1)
}

if flags.Static && !IsValidDllName(flags.OriginalPath) {
if flags.Static && !IsValidDllName(flags.Original) {
fmt.Fprintf(os.Stderr, "[!] Invalid '-x' parameter value:\n")
fmt.Fprintf(os.Stderr, "In case of static linking enabled '-x' parameter must be valid Windows DLL file name with no path information. E.g. kernel32.dll, user32.dll.")
os.Exit(1)
Expand Down
6 changes: 3 additions & 3 deletions dll/dll.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ type ExportedFunction struct {

type Dll struct {
Name string
OriginalPath string
Original string
ExportedFunctions []ExportedFunction
}

func ParseDll(path string, originalPath string) *Dll {
func ParseDll(path string, original string) *Dll {
var dll Dll

dll.Name = filepath.Base(path)
dll.OriginalPath = originalPath
dll.Original = original

pe, err := peparser.New(path, &peparser.Options{})
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ func main() {
cli.PrintBanner()

out := output.Output{
Dll: dll.ParseDll(flags.Input, flags.OriginalPath),
Dll: dll.ParseDll(flags.Input, flags.Original),
OutputDir: filepath.Clean(flags.Output),
TemplatesFS: &templatesFS,
}

out.CreateCodeFile(flags.Mutex, flags.Static)
out.CreateCodeFiles(flags.Mutex, flags.DebugFile, flags.Static)
out.CreateDefFile()
out.CreateCompileScript(flags.Static)

Expand All @@ -35,7 +35,7 @@ func main() {
fmt.Println("Success! What to do next?")
fmt.Println()
fmt.Printf(" 1. Jump into the '%s/' directory.\n", out.OutputDir)
fmt.Printf(" 2. Add your backdoor to the '%s' file.\n", out.GetCodeFileName())
fmt.Printf(" 2. Add your backdoor to the '%s' file.\n", out.GetCppCodeFileName())
fmt.Printf(" 3. Compile project using the '%s' script.\n", out.GetCompileScriptName())
fmt.Println()
}
65 changes: 35 additions & 30 deletions output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ func (o *Output) GetDefFileName() string {
return o.Dll.Name + ".def"
}

func (o *Output) GetCodeFileName() string {
func (o *Output) GetCppCodeFileName() string {
return o.Dll.Name + ".cpp"
}

func (o *Output) GetHdrCodeFileName() string {
return "dllshimmer.h"
}

func (o *Output) GetCompileScriptName() string {
return "compile.sh"
}
Expand All @@ -40,10 +44,12 @@ func (o *Output) GetLibFileName() string {
}

type CodeFileParams struct {
Functions []dll.ExportedFunction
OriginalPath string
Mutex bool
DllName string
Functions []dll.ExportedFunction
Original string
Mutex bool
DllName string
DebugFile string
IsStaticLinked bool
}

func (o *Output) GetTemplate(filename string) *template.Template {
Expand All @@ -56,34 +62,33 @@ func (o *Output) GetTemplate(filename string) *template.Template {
return template.Must(template.New("new").Parse(string(content)))
}

func (o *Output) CreateCodeFile(mutex bool, isStaticLinked bool) {
templateFile := "dynamic-shim.cpp.template"
if isStaticLinked {
templateFile = "static-shim.cpp.template"
func (o *Output) CreateCodeFiles(mutex bool, debugFile string, isStaticLinked bool) {
params := CodeFileParams{
Functions: o.Dll.ExportedFunctions,
Original: sanitizePathForInjection(o.Dll.Original),
Mutex: mutex,
DllName: o.Dll.Name,
DebugFile: sanitizePathForInjection(debugFile),
IsStaticLinked: isStaticLinked,
}

tmpl := o.GetTemplate(templateFile)
outputPath := filepath.Join(o.OutputDir, o.GetCodeFileName())

f, err := os.Create(outputPath)
if err != nil {
log.Fatalf("[!] Error while creating '%s' file: %v", outputPath, err)
}
defer f.Close()
o.createCppCodeFile(params)
o.createHdrCodeFile(params)
}

params := CodeFileParams{
Functions: o.Dll.ExportedFunctions,
OriginalPath: sanitizePathForInjection(o.Dll.OriginalPath),
Mutex: mutex,
DllName: o.Dll.Name,
func (o *Output) createCppCodeFile(params CodeFileParams) {
templateFile := "dynamic-shim.cpp.template"
if params.IsStaticLinked {
templateFile = "static-shim.cpp.template"
}

err = tmpl.Execute(f, params)
if err != nil {
log.Fatalf("[!] Error of template engine: %v", err)
}
outputPath := filepath.Join(o.OutputDir, o.GetCppCodeFileName())
createFileFromTemplate(o, templateFile, outputPath, params)
}

fmt.Printf("[+] '%s' file created\n", outputPath)
func (o *Output) createHdrCodeFile(params CodeFileParams) {
outputPath := filepath.Join(o.OutputDir, o.GetHdrCodeFileName())
createFileFromTemplate(o, "dllshimmer.h.template", outputPath, params)
}

func (o *Output) CreateDefFile() {
Expand Down Expand Up @@ -112,8 +117,8 @@ func (o *Output) CreateDefFile() {
func (o *Output) CreateLibFile() {
var def def.DefFile

// In case of static linking OriginalPath is DLL name itself
def.DllName = o.Dll.OriginalPath
// In case of static linking Original is DLL name itself
def.DllName = o.Dll.Original

for _, function := range o.Dll.ExportedFunctions {
if function.Forwarder == "" {
Expand Down Expand Up @@ -166,7 +171,7 @@ func (o *Output) CreateCompileScript(isStaticLinked bool) {
defer f.Close()

params := CompileScriptParams{
Code: o.GetCodeFileName(),
Code: o.GetCppCodeFileName(),
Def: o.GetDefFileName(),
Output: o.GetOutputDllName(),
IsStaticLinked: isStaticLinked,
Expand Down
24 changes: 23 additions & 1 deletion output/utils.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
package output

import "strings"
import (
"fmt"
"log"
"os"
"strings"
)

func sanitizePathForInjection(path string) string {
return strings.ReplaceAll(path, "\\", "\\\\")
}

func createFileFromTemplate[K interface{}](o *Output, template string, outputPath string, params K) {
tmpl := o.GetTemplate(template)

f, err := os.Create(outputPath)
if err != nil {
log.Fatalf("[!] Error while creating '%s' file: %v", outputPath, err)
}
defer f.Close()

err = tmpl.Execute(f, params)
if err != nil {
log.Fatalf("[!] Error of template engine: %v", err)
}

fmt.Printf("[+] '%s' file created\n", outputPath)
}
Loading
Loading