-
Notifications
You must be signed in to change notification settings - Fork 792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cryptojacker payload #3411
Comments
I've prototyped the CPU utilization algorithm. Below are included two versions. The first is the basic algoritm. The second one produces some useful output and graphs for performance tuning the algorithm's parameters. import hashlib
from random import randbytes
from time import sleep
import psutil
from egg_timer import EggTimer
TARGET_CPU_UTILIZATION = .10
ACCURACY_THRESHOLD = 0.01
INITIAL_SLEEP_SECONDS = 0.001
AVERAGE_BLOCK_SIZE_BYTES = int(
3.25 * 1024 * 1024
) # 3.25 MB - Source: https://trustmachines.co/blog/bitcoin-ordinals-reignited-block-size-debate/
OPERATION_COUNT = 500
OPERATION_COUNT_MODIFIER_START = int(OPERATION_COUNT / 10)
OPERATION_COUNT_MODIFIER_FACTOR = 1.5
SIMULATION_TIME = 60
MINIMUM_SLEEP = 0.000001
def main():
timer = EggTimer()
process = psutil.Process()
sleep_seconds = INITIAL_SLEEP_SECONDS
nonce = 0
block = randbytes(AVERAGE_BLOCK_SIZE_BYTES)
operation_count_modifier = OPERATION_COUNT_MODIFIER_START
# This is a throw-away call. The first call to cpu_percent() always seems to return 0, even
# after generating some hashes. This could be due to some implementation detail in psutil, which
# performs some additional operations the first time it's called, causing utilization to drop
# back down to 0. In other words, my guess is that the first time cpu_percent() is called, the
# act of measurement has a large effect on what's being measured (i.e. the Heisenberg
# uncertainty principle is in play), where as subsequent calls have less of an effect.
#
# UPDATE: Mystery solved! See https://psutil.readthedocs.io/en/latest/#psutil.cpu_percent
process.cpu_percent()
timer.set(SIMULATION_TIME)
while not timer.is_expired():
# The operation_count_modifier decreases the number of hashes per iteration. The modifier,
# itself, decreases by a factor of 1.5 each iteration, until it reaches 1. This allows a
# higher sample rate of the CPU utilization early on to help the sleep time to converge
# quicker.
for _ in range(0, int(OPERATION_COUNT / operation_count_modifier)):
digest = hashlib.sha256()
digest.update(nonce.to_bytes(8))
digest.update(block)
nonce += 1
sleep(sleep_seconds)
operation_count_modifier = max(
operation_count_modifier / OPERATION_COUNT_MODIFIER_FACTOR, 1
)
cpu_utilization = process.cpu_percent() / 100
cpu_utilization_percent_error = calculate_percent_error(cpu_utilization)
sleep_seconds = calculate_new_sleep(sleep_seconds, cpu_utilization_percent_error)
def calculate_percent_error(measured: float) -> float:
return (measured - TARGET_CPU_UTILIZATION) / TARGET_CPU_UTILIZATION
def calculate_new_sleep(current_sleep: float, percent_error: float):
if abs(percent_error) < ACCURACY_THRESHOLD:
return current_sleep
# Since our multiplication is based on sleep_seconds, don't ever let
# sleep_seconds == 0, otherwise it will never equal anything else.
return current_sleep * max((1 + percent_error), MINIMUM_SLEEP)
if __name__ == "__main__":
main() import hashlib
import hashlib
from random import randbytes
from time import sleep
from typing import List
import matplotlib.pyplot as plt
import psutil
from egg_timer import EggTimer
TARGET_CPU_UTILIZATION = 0.05
ACCURACY_THRESHOLD = 0.02
INITIAL_SLEEP_SECONDS = 0.001
AVERAGE_BLOCK_SIZE_BYTES = int(
3.25 * 1024 * 1024
) # 3.25 MB - Source: https://trustmachines.co/blog/bitcoin-ordinals-reignited-block-size-debate/
OPERATION_COUNT = 250
OPERATION_COUNT_MODIFIER_START = int(OPERATION_COUNT / 10)
OPERATION_COUNT_MODIFIER_FACTOR = 1.5
SIMULATION_TIME = 60
def main():
timer = EggTimer()
process = psutil.Process()
sleep_seconds = INITIAL_SLEEP_SECONDS
nonce = 0
block = randbytes(AVERAGE_BLOCK_SIZE_BYTES)
operation_count_modifier = OPERATION_COUNT_MODIFIER_START
sleep_times = [sleep_seconds]
cpu_utilizations = []
percent_errors = []
# This is a throw-away call. The first call to cpu_percent() always seems to return 0, even
# after generating some hashes. This could be due to some implementation detail in psutil, which
# performs some additional operations the first time it's called, causing utilization to drop
# back down to 0. In other words, my guess is that the first time cpu_percent() is called, the
# act of measurement has a large effect on what's being measured (i.e. the Heisenberg
# uncertainty principle is in play), where as subsequent calls have less of an effect.
process.cpu_percent()
print(f"First sleep: {sleep_seconds}\n")
timer.set(SIMULATION_TIME)
while not timer.is_expired():
# The operation_count_modifier decreases the number of hashes per iteration. The modifier,
# itself, decreases by a factor of 1.5 each iteration, until it reaches 1. This allows a
# higher sample rate of the CPU utilization early on to help the sleep time to converge
# quicker.
for _ in range(0, int(OPERATION_COUNT / operation_count_modifier)):
digest = hashlib.sha256()
digest.update(nonce.to_bytes(8))
digest.update(block)
sleep(sleep_seconds)
nonce += 1
operation_count_modifier = max(
operation_count_modifier / OPERATION_COUNT_MODIFIER_FACTOR, 1
)
cpu_utilization = process.cpu_percent() / 100
cpu_utilizations.append(cpu_utilization)
cpu_utilization_percent_error = (
cpu_utilization - TARGET_CPU_UTILIZATION
) / TARGET_CPU_UTILIZATION
percent_errors.append(cpu_utilization_percent_error)
if abs(cpu_utilization_percent_error) >= ACCURACY_THRESHOLD:
# Since our multiplication is based on sleep_seconds, don't ever let
# sleep_seconds == 0, otherwise it will never equal anything else.
sleep_seconds *= max((1 + cpu_utilization_percent_error), 0.000001)
sleep_times.append(sleep_seconds)
print(f"CPU Utilization: {cpu_utilization}")
print(f"Percent error: {cpu_utilization_percent_error}")
print(f"New sleep: {sleep_seconds}")
print()
plot_sleep_times(sleep_times)
plot_cpu_utilization(cpu_utilizations)
plot_percent_error(percent_errors)
def plot_sleep_times(sleep_times: List[float]):
x = range(0, len(sleep_times))
y = sleep_times
plt.clf()
plt.plot(x, y)
plt.xlabel("Iteration")
plt.ylabel("Sleep time (seconds)")
plt.title("Sleep time per iteration")
plt.savefig("sleep_times.png")
def plot_cpu_utilization(cpu_utilizations: List[float]):
x = range(0, len(cpu_utilizations))
y = [utilization * 100 for utilization in cpu_utilizations]
plt.clf()
plt.plot(x, y)
plt.xlabel("Iteration")
plt.ylabel("CPU Utilization (percent)")
plt.title("CPU Utilization per iteration")
plt.savefig("cpu_utilization.png")
def plot_percent_error(percent_errors: List[float]):
x = range(0, len(percent_errors))
y = [utilization * 100 for utilization in percent_errors]
plt.clf()
plt.plot(x, y)
plt.xlabel("Iteration")
plt.ylabel("Percent Error (percent)")
plt.title("Percent Error per iteration")
plt.savefig("percent_error.png")
if __name__ == "__main__":
main() |
You may need https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessornumber to get the processor number on Windows. Example: def GetCurrentProcessorNumber():
_GetCurrentProcessorNumber = windll.kernel32.GetCurrentProcessorNumber
_GetCurrentProcessorNumber.argtypes = []
_GetCurrentProcessorNumber.restype = DWORD
_GetCurrentProcessorNumber.errcheck = RaiseIfZero
return _GetCurrentProcessorNumber()
# VOID WINAPI FlushProcessWriteBuffers(void); Source: https://www.programcreek.com/python/?CodeExample=get+processor |
Description
Build a payload plugin that allows Infection Monkey to simulate a cryptominer. The user should be able to configure:
getblocktemplate
requests (boolean)Notes
getblocktemplate
requests can be sent to the Island (the payload doesn't actually need a response).Tasks
getblocktemplate
request generatorgetblocktemplate
component (0d) - @shreyamalviya @mssalvatore @ilija-lazoroskiThe text was updated successfully, but these errors were encountered: