From fc58a645a6a7473c6625b2874972611317998797 Mon Sep 17 00:00:00 2001 From: aeverj <37864882+aeverj@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:55:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AC=A1=E6=8F=90=E4=BA=A4=20?= =?UTF-8?q?=F0=9F=90=B1=E2=80=8D=F0=9F=8F=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 136 ++++++++++++++++++++++++++++++++++++++++++ README_EN.md | 135 +++++++++++++++++++++++++++++++++++++++++ cmd/cmd.go | 66 ++++++++++++++++++++ core/general.go | 96 +++++++++++++++++++++++++++++ core/staticParser.go | 54 +++++++++++++++++ go.mod | 21 +++++++ go.sum | 57 ++++++++++++++++++ models/dll.go | 8 +++ models/input.go | 9 +++ runner/input.go | 35 +++++++++++ runner/output.go | 29 +++++++++ runner/run.go | 103 ++++++++++++++++++++++++++++++++ utils/mlogger.go | 80 +++++++++++++++++++++++++ utils/mlogger_test.go | 15 +++++ utils/util.go | 36 +++++++++++ 16 files changed, 881 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 README_EN.md create mode 100644 cmd/cmd.go create mode 100644 core/general.go create mode 100644 core/staticParser.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 models/dll.go create mode 100644 models/input.go create mode 100644 runner/input.go create mode 100644 runner/output.go create mode 100644 runner/run.go create mode 100644 utils/mlogger.go create mode 100644 utils/mlogger_test.go create mode 100644 utils/util.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..aed47e3 --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +# PE File Parser + +PE File Parser 是一个解析 PE 文件导入表并生成可劫持 DLL 源代码的工具。 + +## 特性 + +- 自动发现给定目录中的可劫持 DLL +- 根据发现的 DLL 生成对应的源代码 +- 支持选择编译器(MinGW 或 MSVC) +- 可排除特定文件或目录 +- 提供详细输出以便调试 + +## 安装 + +```sh +git clone https://github.com/aeverj/AGHD.git +cd AGHD +go mod tidy +go build -o AGHD.exe cmd/cmd.go +``` + +## Usage +```sh +.\AGHD.exe -h +NAME: + PE File Parser - Parses PE file import tables and generates hijackable DLL source code + +USAGE: + PE File Parser [global options] command [command options] + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --compiler value, -c value Compiler to use (mingw or msvc) (default: "msvc") + --input value, -i value Input file or directory path + --output value, -o value Output directory path + --exclude value, -e value Exclude file or directory name pattern + --verbose, -v Enable verbose output (default: false) + --help, -h show help + +``` +## 获取所有C盘可执行文件dll劫持 +```sh +AGHD.exe -i C:\ +``` +## 生成支持MingW编译器的源文件 +```sh +AGHD.exe -i C:\ -c mingw +``` +## 排除特定的文件或目录 +```sh +AGHD.exe -i C:\ -e admin +``` +## 结果 +```sh +├─NoSigned +│ ├─C__Program Files_Common Files_microsoft shared_ink_InputPersonalization.exe +│ │ elscore.dll.cpp +│ │ elscore.dll.def +│ │ InputPersonalization.exe +│ │ ntdll.dll.cpp +│ │ ntdll.dll.def +│ │ XmlLite.dll.cpp +│ │ XmlLite.dll.def +│ │ +│ ├─C__Program Files_Common Files_microsoft shared_ink_mip.exe +│ │ COMCTL32.dll.cpp +│ │ COMCTL32.dll.def +│ │ dwmapi.dll.cpp +│ │ dwmapi.dll.def +│ │ mip.exe +│ │ MSIMG32.dll.cpp +│ │ MSIMG32.dll.def +│ │ ntdll.dll.cpp +│ │ ntdll.dll.def +│ │ OLEACC.dll.cpp +│ │ OLEACC.dll.def +│ │ UxTheme.dll.cpp +│ │ UxTheme.dll.def +│ │ VERSION.dll.cpp +│ │ VERSION.dll.def +│ │ +│ ├─C__Program Files_Common Files_microsoft shared_ink_ShapeCollector.exe +│ │ COMCTL32.dll.cpp +│ │ COMCTL32.dll.def +│ │ DUI70.dll.cpp +│ │ DUI70.dll.def +│ │ ntdll.dll.cpp +│ │ ntdll.dll.def +│ │ ShapeCollector.exe +│ │ +│ └─C__Program Files_Common Files_microsoft shared_MSInfo_msinfo32.exe +│ ATL.DLL.cpp +│ ATL.DLL.def +│ COMCTL32.dll.cpp +│ COMCTL32.dll.def +│ MFC42u.dll.cpp +│ MFC42u.dll.def +│ msinfo32.exe +│ ntdll.dll.cpp +│ ntdll.dll.def +│ POWRPROF.dll.cpp +│ POWRPROF.dll.def +│ SLC.dll.cpp +│ SLC.dll.def +│ +└─Signed + ├─C__Program Files_Common Files_microsoft shared_ClickToRun_appvcleaner.exe + │ appvcleaner.exe + │ APPVMANIFEST.dll.cpp + │ APPVMANIFEST.dll.def + │ APPVPOLICY.dll.cpp + │ APPVPOLICY.dll.def + │ msi.dll.cpp + │ msi.dll.def + │ ntdll.dll.cpp + │ ntdll.dll.def + │ USERENV.dll.cpp + │ USERENV.dll.def + │ + ├─C__Program Files_Common Files_microsoft shared_ClickToRun_AppVShNotify.exe + │ AppVShNotify.exe + │ USERENV.dll.cpp + │ USERENV.dll.def + │ + ├─C__Program Files_Common Files_microsoft shared_ClickToRun_InspectorOfficeGadget.exe + │ InspectorOfficeGadget.exe + │ mscoree.dll.cpp + │ mscoree.dll.def + │ + └─C__Program Files_Common Files_microsoft shared_ClickToRun_IntegratedOffice.exe + IntegratedOffice.exe + IPHLPAPI.DLL.cpp + IPHLPAPI.DLL.def +``` diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..113f814 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,135 @@ +# PE File Parser + +PE File Parser is a tool that parses PE file import tables and generates hijackable DLL source code. + +## Features + +- Automatically discover hijackable DLLs in a given directory +- Generate corresponding source code for discovered DLLs +- Support for choosing compiler (MinGW or MSVC) +- Exclude specific files or directories +- Provide verbose output for debugging + +## Installation + +```sh +git clone https://github.com/aeverj/AGHD.git +cd AGHD +go mod tidy +go build -o AGHD.exe cmd/cmd.go +``` +## Usage +```sh +.\AGHD.exe -h +NAME: + PE File Parser - Parses PE file import tables and generates hijackable DLL source code + +USAGE: + PE File Parser [global options] command [command options] + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --compiler value, -c value Compiler to use (mingw or msvc) (default: "msvc") + --input value, -i value Input file or directory path + --output value, -o value Output directory path + --exclude value, -e value Exclude file or directory name pattern + --verbose, -v Enable verbose output (default: false) + --help, -h show help + +``` +## Parsing all C drive files +```sh +AGHD.exe -i C:\ +``` +## Generate source files that support the MingW compiler +```cgo +AGHD.exe -i C:\ -c mingw +``` +## Exclude specific files or directories +```cgo +AGHD.exe -i C:\ -e admin +``` +## Result +```sh +├─NoSigned +│ ├─C__Program Files_Common Files_microsoft shared_ink_InputPersonalization.exe +│ │ elscore.dll.cpp +│ │ elscore.dll.def +│ │ InputPersonalization.exe +│ │ ntdll.dll.cpp +│ │ ntdll.dll.def +│ │ XmlLite.dll.cpp +│ │ XmlLite.dll.def +│ │ +│ ├─C__Program Files_Common Files_microsoft shared_ink_mip.exe +│ │ COMCTL32.dll.cpp +│ │ COMCTL32.dll.def +│ │ dwmapi.dll.cpp +│ │ dwmapi.dll.def +│ │ mip.exe +│ │ MSIMG32.dll.cpp +│ │ MSIMG32.dll.def +│ │ ntdll.dll.cpp +│ │ ntdll.dll.def +│ │ OLEACC.dll.cpp +│ │ OLEACC.dll.def +│ │ UxTheme.dll.cpp +│ │ UxTheme.dll.def +│ │ VERSION.dll.cpp +│ │ VERSION.dll.def +│ │ +│ ├─C__Program Files_Common Files_microsoft shared_ink_ShapeCollector.exe +│ │ COMCTL32.dll.cpp +│ │ COMCTL32.dll.def +│ │ DUI70.dll.cpp +│ │ DUI70.dll.def +│ │ ntdll.dll.cpp +│ │ ntdll.dll.def +│ │ ShapeCollector.exe +│ │ +│ └─C__Program Files_Common Files_microsoft shared_MSInfo_msinfo32.exe +│ ATL.DLL.cpp +│ ATL.DLL.def +│ COMCTL32.dll.cpp +│ COMCTL32.dll.def +│ MFC42u.dll.cpp +│ MFC42u.dll.def +│ msinfo32.exe +│ ntdll.dll.cpp +│ ntdll.dll.def +│ POWRPROF.dll.cpp +│ POWRPROF.dll.def +│ SLC.dll.cpp +│ SLC.dll.def +│ +└─Signed + ├─C__Program Files_Common Files_microsoft shared_ClickToRun_appvcleaner.exe + │ appvcleaner.exe + │ APPVMANIFEST.dll.cpp + │ APPVMANIFEST.dll.def + │ APPVPOLICY.dll.cpp + │ APPVPOLICY.dll.def + │ msi.dll.cpp + │ msi.dll.def + │ ntdll.dll.cpp + │ ntdll.dll.def + │ USERENV.dll.cpp + │ USERENV.dll.def + │ + ├─C__Program Files_Common Files_microsoft shared_ClickToRun_AppVShNotify.exe + │ AppVShNotify.exe + │ USERENV.dll.cpp + │ USERENV.dll.def + │ + ├─C__Program Files_Common Files_microsoft shared_ClickToRun_InspectorOfficeGadget.exe + │ InspectorOfficeGadget.exe + │ mscoree.dll.cpp + │ mscoree.dll.def + │ + └─C__Program Files_Common Files_microsoft shared_ClickToRun_IntegratedOffice.exe + IntegratedOffice.exe + IPHLPAPI.DLL.cpp + IPHLPAPI.DLL.def +``` diff --git a/cmd/cmd.go b/cmd/cmd.go new file mode 100644 index 0000000..f53c3e0 --- /dev/null +++ b/cmd/cmd.go @@ -0,0 +1,66 @@ +package main + +import ( + "AGHD/models" + "AGHD/runner" + "fmt" + "github.com/urfave/cli/v2" + "os" +) + +func main() { + var params models.Parameters + app := &cli.App{ + Name: "PE File Parser", + Usage: "Parses PE file import tables and generates hijackable DLL source code", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "compiler", + Aliases: []string{"c"}, + Usage: "Compiler to use (mingw or msvc)", + Destination: ¶ms.Compiler, + Value: "msvc", + }, + &cli.StringFlag{ + Name: "input", + Aliases: []string{"i"}, + Usage: "Input file or directory path", + Destination: ¶ms.InputPath, + Required: true, + }, + &cli.StringFlag{ + Name: "output", + Aliases: []string{"o"}, + Usage: "Output directory path", + Destination: ¶ms.OutputPath, + Value: "", + }, + &cli.StringFlag{ + Name: "exclude", + Aliases: []string{"e"}, + Usage: "Exclude file or directory name pattern", + Destination: ¶ms.ExcludePattern, + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Enable verbose output", + Destination: ¶ms.Verbose, + Value: false, + }, + }, + Action: func(c *cli.Context) error { + r, err := runner.New(¶ms) + if err != nil { + return err + } + r.Run() + return nil + }, + } + + err := app.Run(os.Args) + if err != nil { + fmt.Println("Error:\n", err) + } +} diff --git a/core/general.go b/core/general.go new file mode 100644 index 0000000..df9c8ce --- /dev/null +++ b/core/general.go @@ -0,0 +1,96 @@ +package core + +import ( + "AGHD/models" + "fmt" + "strings" +) + +func GeneCppCodeForMsvc(dllInfo models.DLLInfo) string { + var builder strings.Builder + + // 添加头文件和注释 + builder.WriteString(fmt.Sprintf(`#include + +// %s implementation %s + +`, dllInfo.DllName, dllInfo.Arch)) + + // 添加函数导出 + for _, funcName := range dllInfo.FunName { + builder.WriteString(fmt.Sprintf(`#pragma comment(linker, "/export:%s=run")`, funcName)) + builder.WriteString("\n") + + } + for _, funcOrdinal := range dllInfo.FunOrdinal { + builder.WriteString(fmt.Sprintf(`#pragma comment(linker, "/export:%d=run,@%d,NONAME")`, funcOrdinal, funcOrdinal)) + builder.WriteString("\n") + + } + + // 添加函数实现和 DllMain 函数 + builder.WriteString(` +extern "C" void run() { + // Function implementation +} + +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; // Successful DLL_PROCESS_ATTACH. +} +`) + + return builder.String() +} + +func GeneCppCodeForMinGW(dllInfo models.DLLInfo) string { + var builder strings.Builder + builder.WriteString(`LIBRARY def +EXPORTS +`) + // 添加函数导出 + for _, funcName := range dllInfo.FunName { + builder.WriteString(fmt.Sprintf(` %s=run`, funcName)) + builder.WriteString("\n") + + } + for _, funcOrdinal := range dllInfo.FunOrdinal { + builder.WriteString(fmt.Sprintf(` run%d=run @%d NONAME PRIVATE`, funcOrdinal, funcOrdinal)) + builder.WriteString("\n") + + } + + builder.WriteString(strings.Repeat("=", 50)) + builder.WriteString("\n") + // 添加头文件和注释 + builder.WriteString(fmt.Sprintf(`#include + +// %s implementation %s +// g++ -shared -o %s -O3 -static -s -Wl,--strip-all %s.cpp %s.def + +`, dllInfo.DllName, dllInfo.Arch, dllInfo.DllName, dllInfo.DllName, dllInfo.DllName)) + builder.WriteString(` +extern "C" void run() { + // Function implementation +} + + +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; // Successful DLL_PROCESS_ATTACH. +}`) + + return builder.String() +} diff --git a/core/staticParser.go b/core/staticParser.go new file mode 100644 index 0000000..e02c17e --- /dev/null +++ b/core/staticParser.go @@ -0,0 +1,54 @@ +package core + +import ( + "AGHD/models" + "AGHD/utils" + "github.com/saferwall/pe" + "strings" +) + +var known_dlls_list = []string{"kernel32.dll", "wow64.dll", "shell32.dll", "imagehlp.dll", "advapi32.dll", "wow64win.dll", "psapi.dll", "msctf.dll", "imm32.dll", "gdiplus.dll", "shcore.dll", "wowarmhw.dll", "setupapi.dll", "msvcrt.dll", "gdi32.dll", "xtajit.dll", "wldap32.dll", "combase.dll", "nsi.dll", "normaliz.dll", "oleaut32.dll", "user32.dll", "clbcatq.dll", "ole32.dll", "wow64cpu.dll", "sechost.dll", "difxapi.dll", "comdlg32.dll", "coml2.dll", "shlwapi.dll", "ws2_32.dll", "rpcrt4.dll", "ntdll.dll','mscoree.dll", "msvcp_win"} + +func StaticParsePE(filePath string) (dllInfoList []models.DLLInfo, SignatureValid bool) { + file, err := pe.New(filePath, &pe.Options{}) + if err != nil { + utils.Mlogger.Error("Error opening PE file: %v", err) + return nil, false + } + + err = file.Parse() + if err != nil { + utils.Mlogger.Error("Error parsing PE file: %v", err) + return nil, false + } + SignatureValid = false + if file.IsSigned { + SignatureValid = file.Certificates.Certificates[0].SignatureValid + } + if file.HasImport { + var currDllInfo models.DLLInfo + currDllInfo.Arch = file.PrettyOptionalHeaderMagic() + for _, imp := range file.Imports { + if utils.Contains(known_dlls_list, imp.Name) || strings.HasPrefix(imp.Name, "api-ms-win") { + continue + } + currDllInfo.DllName = imp.Name + currDllInfo.FunName = nil + currDllInfo.FunOrdinal = nil + for _, funcName := range imp.Functions { + if !funcName.ByOrdinal { + currDllInfo.FunName = append(currDllInfo.FunName, funcName.Name) + } else { + currDllInfo.FunOrdinal = append(currDllInfo.FunOrdinal, funcName.Ordinal) + } + } + if len(currDllInfo.FunOrdinal) > 0 || len(currDllInfo.FunName) > 0 { + dllInfoList = append(dllInfoList, currDllInfo) + } + } + } else { + utils.Mlogger.Error("No imports found.") + return nil, false + } + return +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3572f27 --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module AGHD + +go 1.21.1 + +require ( + github.com/saferwall/pe v1.5.3 + github.com/urfave/cli/v2 v2.27.2 +) + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect + golang.org/x/text v0.7.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..361ddd7 --- /dev/null +++ b/go.sum @@ -0,0 +1,57 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/saferwall/pe v1.5.3 h1:tZ2KJWeJK6N2jjJx+B9hm+wq6qe0o56pql2PXUaQ9eI= +github.com/saferwall/pe v1.5.3/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ= +github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d h1:RQqyEogx5J6wPdoxqL132b100j8KjcVHO1c0KLRoIhc= +github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d/go.mod h1:PegD7EVqlN88z7TpCqH92hHP+GBpfomGCCnw1PFtNOA= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= +github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/models/dll.go b/models/dll.go new file mode 100644 index 0000000..240382c --- /dev/null +++ b/models/dll.go @@ -0,0 +1,8 @@ +package models + +type DLLInfo struct { + DllName string + Arch string + FunName []string + FunOrdinal []uint32 +} diff --git a/models/input.go b/models/input.go new file mode 100644 index 0000000..40e6701 --- /dev/null +++ b/models/input.go @@ -0,0 +1,9 @@ +package models + +type Parameters struct { + Compiler string + InputPath string + OutputPath string + ExcludePattern string + Verbose bool +} diff --git a/runner/input.go b/runner/input.go new file mode 100644 index 0000000..b872227 --- /dev/null +++ b/runner/input.go @@ -0,0 +1,35 @@ +package runner + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +func ProcessInput(inputPath, excludePattern string, peFilePath chan string) { + info, _ := os.Stat(inputPath) + if info.IsDir() { + err := filepath.Walk(inputPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + _ = fmt.Errorf("unable to walk the input directory: %v", err) + return nil + } + if strings.HasSuffix(info.Name(), ".exe") { + if excludePattern != "" && strings.Contains(strings.ToLower(path), strings.ToLower(excludePattern)) { + return nil + } + peFilePath <- path + } + return nil + }) + if err != nil { + _ = fmt.Errorf("unable to walk the input directory: %v", err) + } + } else { + if strings.HasSuffix(info.Name(), ".exe") { + peFilePath <- inputPath + } + } + close(peFilePath) +} diff --git a/runner/output.go b/runner/output.go new file mode 100644 index 0000000..b79d2fd --- /dev/null +++ b/runner/output.go @@ -0,0 +1,29 @@ +package runner + +import ( + "os" + "path/filepath" + "strings" +) + +func WriteMsvcDLLSource(content, dllName, outputPath string) error { + err := os.WriteFile(filepath.Join(outputPath, dllName+".cpp"), []byte(content), 0666) + if err != nil { + return err + } + return nil +} + +func WriteMinGWDLLSource(content, dllName, outputPath string) error { + defContent := strings.Split(content, strings.Repeat("=", 50))[0] + source := strings.Split(content, strings.Repeat("=", 50))[1] + err := os.WriteFile(filepath.Join(outputPath, dllName+".cpp"), []byte(source), 0666) + if err != nil { + return err + } + err = os.WriteFile(filepath.Join(outputPath, dllName+".def"), []byte(defContent), 0666) + if err != nil { + return err + } + return nil +} diff --git a/runner/run.go b/runner/run.go new file mode 100644 index 0000000..935e904 --- /dev/null +++ b/runner/run.go @@ -0,0 +1,103 @@ +package runner + +import ( + "AGHD/core" + "AGHD/models" + "AGHD/utils" + "fmt" + "os" + "path/filepath" + "strings" + "time" +) + +type RunnerOpt struct { + Input string + Output string + Exclude string + Arch string + Compile string + Verbose bool +} + +func New(p *models.Parameters) (*RunnerOpt, error) { + opt := &RunnerOpt{} + _, err := os.Stat(p.InputPath) + if err != nil { + return nil, fmt.Errorf("cannot access input path: %v", err) + } + opt.Input = p.InputPath + if p.OutputPath == "" { + currentTime := time.Now() + formattedTime := currentTime.Format("20060102_150405") + folderName := formattedTime + "_out" + err := os.Mkdir(folderName, 0755) + if err != nil { + utils.Mlogger.Error("Error creating directory: %s\n", err.Error()) + return nil, err + } + opt.Output = folderName + } else { + opt.Output = p.OutputPath + } + if p.Verbose { + utils.Mlogger = utils.NewLogger(utils.LogLevelInfo) + } else { + utils.Mlogger = utils.NewLogger(utils.LogLevelSuccess) + } + opt.Compile = p.Compiler + opt.Exclude = p.ExcludePattern + opt.Verbose = true + return opt, nil +} + +func (r *RunnerOpt) Run() { + pePathChan := make(chan string) + go ProcessInput(r.Input, r.Exclude, pePathChan) + for filePath := range pePathChan { + contentList, signValid := core.StaticParsePE(filePath) + resultPath := r.Output + if len(contentList) > 0 { + if signValid { + resultPath = filepath.Join(resultPath, "Signed", strings.Replace(strings.Replace(filePath, string(os.PathSeparator), "_", -1), ":", "_", -1)) + } else { + resultPath = filepath.Join(resultPath, "NoSigned", strings.Replace(strings.Replace(filePath, string(os.PathSeparator), "_", -1), ":", "_", -1)) + } + _, err := utils.CopyFile(filepath.Join(resultPath, filepath.Base(filePath)), filePath) + if err != nil { + continue + } + } + for _, content := range contentList { + switch r.Compile { + case "msvc": + sourceMsvc := core.GeneCppCodeForMsvc(content) + err := WriteMsvcDLLSource(sourceMsvc, content.DllName, resultPath) + if err != nil { + utils.Mlogger.Error("Write cpp file failed, %s", err.Error()) + break + } + utils.Mlogger.Success("Success general %s", content.DllName) + break + case "mingw": + sourceMinGW := core.GeneCppCodeForMinGW(content) + err := WriteMinGWDLLSource(sourceMinGW, content.DllName, resultPath) + if err != nil { + utils.Mlogger.Error("Write cpp file failed, %s", err.Error()) + break + } + utils.Mlogger.Success("Success general %s", content.DllName) + break + default: + break + } + } + } + entries, _ := os.ReadDir(r.Output) + if len(entries) == 0 { + utils.Mlogger.Info("No matching files found in the %s folder.", r.Input) + _ = os.RemoveAll(r.Output) + } else { + utils.Mlogger.Success("Operation completed successfully! Results saved to the %s folder.", r.Output) + } +} diff --git a/utils/mlogger.go b/utils/mlogger.go new file mode 100644 index 0000000..816ff25 --- /dev/null +++ b/utils/mlogger.go @@ -0,0 +1,80 @@ +package utils + +import ( + go_colorable "github.com/mattn/go-colorable" + "log" + "os" + "runtime" +) + +// 定义日志级别 +const ( + LogLevelError = iota + LogLevelSuccess + LogLevelInfo +) + +// 定义颜色代码 +const ( + ColorRed = "\033[31m" + ColorGreen = "\033[32m" + ColorYellow = "\033[33m" + ColorReset = "\033[0m" +) + +// 定义日志结构体 +type Logger struct { + logger *log.Logger + level int +} + +var Mlogger *Logger + +// 创建新的日志实例 +func NewLogger(level int) *Logger { + var out *os.File + if runtime.GOOS == "windows" { + out = os.Stdout + return &Logger{ + logger: log.New(go_colorable.NewColorable(out), "", log.LstdFlags), + level: level, + } + } else { + out = os.Stdout + return &Logger{ + logger: log.New(out, "", log.LstdFlags), + level: level, + } + } +} + +// 输出错误日志 +func (l *Logger) Error(format string, v ...interface{}) { + if l.level >= LogLevelError { + l.logger.Printf(ColorRed+"[ERROR] "+format+ColorReset, v...) + } +} + +// 输出成功日志 +func (l *Logger) Success(format string, v ...interface{}) { + if l.level >= LogLevelSuccess { + l.logger.Printf(ColorGreen+"[SUCCESS] "+format+ColorReset, v...) + } +} + +// 输出信息日志 +func (l *Logger) Info(format string, v ...interface{}) { + if l.level >= LogLevelInfo { + l.logger.Printf(ColorYellow+"[INFO] "+format+ColorReset, v...) + } +} + +func main() { + // 创建一个日志级别为 INFO 的日志实例 + logger := NewLogger(LogLevelInfo) + + // 输出不同级别的日志 + logger.Error("This is an error message: %v", "something went wrong") + logger.Success("Operation completed successfully!") + logger.Info("This is an informational message.") +} diff --git a/utils/mlogger_test.go b/utils/mlogger_test.go new file mode 100644 index 0000000..378371b --- /dev/null +++ b/utils/mlogger_test.go @@ -0,0 +1,15 @@ +package utils + +import ( + "testing" +) + +func Test_main(t *testing.T) { + // 创建一个日志级别为 INFO 的日志实例 + logger := NewLogger(LogLevelInfo) + logger.level = LogLevelError + // 输出不同级别的日志 + logger.Error("This is an error message: %v", "something went wrong") + logger.Success("Operation completed successfully!") + logger.Info("This is an informational message.") +} diff --git a/utils/util.go b/utils/util.go new file mode 100644 index 0000000..e7f2f27 --- /dev/null +++ b/utils/util.go @@ -0,0 +1,36 @@ +package utils + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +func CopyFile(dstName, srcName string) (writeen int64, err error) { + src, err := os.Open(srcName) + if err != nil { + return 0, err + } + defer src.Close() + err = os.MkdirAll(filepath.Dir(dstName), os.ModePerm) + if err != nil { + return 0, err + } + dst, err := os.OpenFile(dstName, os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + Mlogger.Error(err.Error()) + return 0, err + } + defer dst.Close() + return io.Copy(dst, src) +} + +func Contains(arr []string, str string) bool { + for _, v := range arr { + if strings.Contains(strings.ToLower(str), v) { + return true + } + } + return false +}