Offensive Rust For Hacker’s (Part — 1) | Why Rust?, Setup, Basic Concepts, Project & More . . .

D3athCod3
17 min readJul 1, 2024

--

Welcome to the “Offensive Rust for Hackers” series! I began learning Rust during my ongoing CyberQuest1095 challenge, and after one month, I was inspired to create this free series for hackers. We’ll start with basic Rust concepts and progress to advanced penetration testing projects, including writing custom tools. With clear, easy-to-understand explanations, you’ll gain the skills to use Rust effectively in your offensive security endeavours. In this article, we will explore fundamental Rust concepts through a straightforward project. Additionally, I will provide a project description that you can use to write and submit your code as well!

Happy Coding Hacker’s :)

Why Rust?

Rust is great for hackers because it helps avoid common programming errors like crashes and security bugs. It’s fast, like C and C++, but also safe to use, making it ideal for creating reliable hacking tools. Even though Rust code can take time to write, you won’t spend hours fixing bugs like you do in other programming language. The Rust compiler might give you a hard time sometimes, but it provides easy-to-understand errors and quick fixes, making your coding experience smoother in the long run.

Setting Up the Environment

If you are in windows click on “rustup-init.exe” and download this executable binary.

  • Step #2 — After successfully downloading run the installer

If you don’t have Microsoft linker or build tools then you going to get above screen after running installer. Press 1 and hit enter & it’ll install visual studio code.

  • Step #3 — After completing above step rust’ll go to the next step i.e.

Press 1 and hit enter.

After Step 3 successfully completed verify the installation using

$ rustc --version

This will show you rust version.

After rust rover setup you’re can move to the next section of this blog :)

Basic Syntax

1. Writing Your First Rust Program

As we start writing our first program “Hello, World!” in Rust, it’s just like what we do in other programming languages. In Rust, printing “Hello, World!” is straightforward — we use `println!` to show our message on the screen. Let’s see how simple it is to begin coding in Rust!

Code Explanation:

  • fn main() — This line starts with fn, which stands for "function". main() is a special function in Rust. It's the entry point of any Rust program; it's where the program starts running. Everything inside {} belongs to the function main. In this case, there’s only one line of code inside the function.
  • println!(“Hello, Hacker’s!”);println! is a macro in Rust used for printing text to the console. The ! indicates that println is a macro, which is a special kind of function in Rust that does more complex things than regular functions. Inside the println!() function, there's a string of text enclosed in " ". This text is what will be printed to the console when the program runs.

When you run this Rust program, it will start at the main() function. Inside main(), it will execute println!("Hello, Hacker's!");. As a result, the program will print Hello, Hacker's! to the console.

Output:

2. Variables

A variable in programming is like a labeled box where you can store different kinds of information, such as numbers or words. It lets you save data temporarily and change it as needed while your program runs.

In Rust, a variable is a named piece of memory that holds a value. In Rust, you declare a variable using the let keyword.

Explanation:

  • let value = "Hacker's!";: Declares a variable named value and assigns it the value "Hacker's!".
  • println!("Hello, {}", value);: Prints the string "Hello, " followed by the value stored in the value variable. The {} is a placeholder for the variable’s value.

Output:

Unlike some other languages, variables in Rust are immutable by default, meaning once you assign a value to a variable, you can’t change that value.

For example:

let x = 5;
x = 9; // We can't change value of x
println!("value of x: {x}");

Here, x is a variable that holds the value 5. By default, x is immutable; you cannot reassign x to another value. If we assign x = 9 it gonna give us error. But fear not we have mutable variable for this :)

Mutable Variable:

To create a mutable variable in Rust, you use the mut keyword.

For example:

let mut x = 5;
x = 9; // Value of x is changed to 9
println!("value of x: {x}");

In this example, x is declared as mutable with mut, allowing its value to be changed after it's initially set. If we run this code we get result: value of x: 9

3. Reading User Input

In Rust, reading user input from the console typically involves using the std::io module. The io module provides various utilities for input and output, and the stdin function is used to capture input from the user.

For Example:

use std::io;

fn main() {
// Step 1: Create a mutable String to store user input
let mut name = String::new();

// Step 2: Print a prompt to the user
println!("Please enter your name:");

// Step 3: Read the user input and store it in the `name` variable
io::stdin().read_line(&mut name).expect("Failed to read line");

// Step 4: Trim the input to remove any trailing newlines or spaces
let name = name.trim();

// Step 5: Print a greeting message
println!("Hello, {}!", name);
}

4. Data Types in Rust

Rust has several basic data types, categorized into scalar and compound types.

a. Scalar Types

A scalar type in Rust represents a single, indivisible value. There are four main scalar types in Rust, similar to those in other languages. Below, we’ll explore these types and how they function in Rust. Let’s dive in!

  • Integer Data Type

In Rust, the integer data type represents whole numbers without fractional components. Integers can be either signed (able to represent both positive and negative numbers) or unsigned (only positive numbers, including zero). Rust provides several integer types based on their size and whether they are signed or unsigned.

For Example :

fn main() {
let a: i32 = -10; // a is a 32-bit signed integer with a value of -10
let b: u32 = 42; // b is a 32-bit unsigned integer with a value of 42

println!("Signed integer a: {}", a);
println!("Unsigned integer b: {}", b);
}

Explanation:

let a: i32 = -10;: Declares a variable a of type i32 and assigns it the value -10. This means a is a 32-bit signed integer and can hold both positive and negative numbers.

let b: u32 = 42;: Declares a variable b of type u32 and assigns it the value 42. This means b is a 32-bit unsigned integer and can only hold non-negative numbers.

println!("Signed integer a: {}", a);: Prints the value of a, which is -10.

println!("Unsigned integer b: {}", b);: Prints the value of b, which is 42.

  • Floating Point Numbers

Rust has two types for floating-point numbers, which are numbers with decimals: `f32` and `f64`. The `f32` type is 32 bits in size, and `f64` is 64 bits. By default, Rust uses `f64` because it is almost as fast as `f32` on modern computers but provides greater precision. Both `f32` and `f64` can represent positive and negative numbers.

For Example:

fn main() {
let y: f64 = 3.14; // y is a floating-point number with a value of 3.14
println!("The value of y is: {}", y);
}

The variable y is declared with the type f64 (64-bit floating-point) and assigned the value 3.14. The println! macro prints "The value of y is: 3.14".

  • Boolean Data Type

A boolean data type in Rust, denoted as bool, is used to represent a value that can be either true or false. This is useful for conditional statements, logical operations, and controlling the flow of a program.

Declaration and Usage

fn main() {
let is_rust_awesome: bool = true; // Declaring a boolean variable
if is_rust_awesome {
println!("Rust is awesome!");
} else {
println!("Rust is not awesome.");
}
}

The variable is_rust_awesome is declared with the type bool and assigned the value true.

The if statement checks the value of is_rust_awesome. If it's true, the program prints "Rust is awesome!". Otherwise, it prints "Rust is not awesome.".

Boolean Operations

Rust supports common boolean operations such as AND (&&), OR (||), and NOT (!):

fn main() {
let a: bool = true;
let b: bool = false;

// AND operation
let and_result = a && b; // false

// OR operation
let or_result = a || b; // true

// NOT operation
let not_result = !a; // false

println!("AND result: {}", and_result);
println!("OR result: {}", or_result);
println!("NOT result: {}", not_result);
}

AND Operation (&&): Returns true only if both operands are true. In this example, and_result is false because a is true and b is false.

OR Operation (||): Returns true if at least one operand is true. Here, or_result is true because a is true.

NOT Operation (!): Reverses the value of a boolean. not_result is false because !a reverses true to false.

  • Characters

In Rust, the char data type is used to represent a single character. Rust's char type is a bit different from characters in some other languages because it supports Unicode, which means it can represent more than just ASCII characters. Each char in Rust is four bytes in size, allowing it to represent a wide range of characters from different languages and symbol sets.

For Example:

fn main() {
let letter: char = 'A'; // A simple ASCII character
let emoji: char = '😊'; // A Unicode emoji

println!("Letter: {}", letter);
println!("Emoji: {}", emoji);
}

b. Compound Types

  • String Type (String, &str)

In Rust, Strings are more complex than primitive types and are built on a more complex data structure. There are two main types for handling textual data:

String: This type is mutable, meaning you can change its contents, like adding or removing characters.

&str: This is a reference to a String type. It allows you to read the string’s content but doesn’t allow modifications (immutable). It’s like a view into the String without the ability to change it.

For Example :

fn main() {
// String type
let mut s = String::from("hello");
s.push_str(", world!"); // Appends a literal to the end of the String
println!("String: {}", s); // Prints: String: hello, world!

// &str type
let greeting: &str = "hello, world!"; // Immutable reference to a string literal
println!("&str: {}", greeting); // Prints: &str: hello, world!
}

Explanation:

String::from("hello") creates a mutable string s. Using push_str, we append ", world!" to s, modifying its content. This demonstrates the mutable nature of String.

let greeting: &str = "hello, world!"; creates an immutable reference greeting to a string literal. You can only read from greeting; any attempt to modify it will result in a compile-time error. This showcases the immutability of &str.

  • Tuples

In Rust, a tuple is a compound data type that can store a fixed number of values of potentially different types. Tuples are created by enclosing their elements in parentheses ( ). Here’s how tuples work in Rust:

fn main() {
let person: (&str, i32) = ("Alice", 30); // person is a tuple with a string and an integer
println!("Name: {}, Age: {}", person.0, person.1);
}

Explanation:

The variable person is a tuple containing a string and an integer. person.0 accesses the first element ("Alice"), and person.1 accesses the second element (30). The println! macro prints "Name: Alice, Age: 30".

  • Arrays

Arrays in Rust have a fixed size, determined at compile-time, and store elements of the same data type.

For Example :

fn main() {
let numbers: [i32; 3] = [1, 2, 3]; // numbers is an array of three integers
println!("First number: {}", numbers[0]);
}

Explanation:

The variable numbers is an array of three integers. numbers[0] accesses the first element of the array (1). The println! macro prints "First number: 1".

5. Functions

In Rust, a function is a block of code that performs a specific task. You’ve already seen one of the most important functions in the language: the main function, which is the entry point of many programs. You’ve also seen the fn keyword, which allows you to declare new functions. They allow you to encapsulate reusable pieces of code.

For Example :

fn main() {
// Call the greet function
greet();
}

// Define a function named greet
fn greet() {
println!("Hello, world!");
}

Explanation:

  • fn greet() - Declares a function named greet using the fn keyword.
  • println!(“Hello, world!”); - Inside greet, this line prints "Hello, world!" to the console.
  • fn main() - The main function, the entry point of the program, calls greet() to execute the greet function.

6. Conditional Statements

A conditional statement in programming is like making a decision based on a situation. It allows your program to check if something is true or false, and then decide what action to take next. In Rust, you can use `if`, `else`, and `match` to write these conditions clearly and effectively.

  • if/else :

An if/else statement in programming lets you make decisions based on conditions. In Rust, if/else statements evaluate a condition. If the condition is true, the code inside the if block runs. Otherwise, if the condition is false, the code inside the else block executes.

For Example:

fn main() {
let number = 10;

if number % 2 == 0 {
println!("{} is even.", number);
} else {
println!("{} is odd.", number);
}
}

In this example, since 10 % 2 == 0 is true, the output will be: 10 is even.

Explanation :

let number = 10;: Declares a variable number and assigns it the value 10.

if number % 2 == 0 { ... }: Checks if number divided by 2 gives a remainder of 0. If true (number is even), it prints 10 is even..

else { ... }: If the condition (number % 2 == 0) is false (number is odd), it prints 10 is odd..

Note: When you have more than two possible outcomes based on different conditions, you can use else if statements in addition to if and else. Here’s how it works:

fn main() {
let number = 10;

if number > 10 {
println!("{} is greater than 10.", number);
} else if number < 10 {
println!("{} is less than 10.", number);
} else {
println!("{} is equal to 10.", number);
}
}

In this example, since number is equal to 10, the output will be: 10 is equal to 10.

Explanation:

let number = 10;: Declares a variable number and assigns it the value 10.

if number > 10 { ... }: Checks if number is greater than 10. If true, it prints 10 is greater than 10. (which won't happen in this case).

else if number < 10 { ... }: If the first condition (number > 10) is false, it checks if number is less than 10. If true, it prints 10 is less than 10. (which also won't happen here).

else { ... }: If both conditions (number > 10 and number < 10) are false, it prints 10 is equal to 10..

  • match :

In Rust, the match statement is used to compare a value against a series of patterns and then execute code based on which pattern matches. It’s like a more powerful version of switch statements found in other languages.

For Example :

fn main() {
let num = 3;

match num {
1 => println!("One"),
2 => println!("Two"),
3 => println!("Three"),
_ => println!("Other"), // Default case if none of the above match
}
}

Explanation :

In this example, num is set to 3.

The match statement checks the value of num against several patterns (1, 2, 3).

If num matches 1, it prints "One".

If num matches 2, it prints "Two".

If num matches 3, it prints "Three".

The _ => println!("Other") is a catch-all case that matches any value not covered by the specific patterns (1, 2, 3).

7. Loops

In Rust, loops are used to repeat a block of code multiple times until a certain condition is met. There are mainly three types of loops:

  • “loop” Loop:

This loop repeats indefinitely until explicitly told to stop. It’s useful when you need to continuously execute a block of code until a specific condition is met.

For Example :

fn main() {
let mut counter = 0;

loop {
println!("Counter: {}", counter);
counter += 1;

if counter >= 5 {
break; // Exit the loop when counter reaches 5
}
}

println!("Loop ended!");
}

Explanation :

let mut counter = 0;: Initializes a mutable variable counter and sets its initial value to 0.

loop { ... }: Starts an infinite loop that will continue indefinitely until it is explicitly terminated with a break statement.

println!("Counter: {}", counter);: Prints the current value of counter.

counter += 1;: Increments the counter by 1 in each iteration.

if counter >= 5 { break; }: Checks if counter is greater than or equal to 5. If true, it exits the loop using the break statement.

After exiting the loop, println!("Loop ended!"); prints "Loop ended!" to indicate that the loop has finished.

  • “while” Loop:

This loop repeats a block of code as long as a condition is true. It checks the condition before each iteration.

For Example:

fn main() {
let mut count = 0; // Initialize a mutable variable `count` to 0

while count < 5 { // While `count` is less than 5, execute the loop body
println!("Count is: {}", count); // Print the current value of `count`
count += 1; // Increment `count` by 1 after each iteration
}

println!("Loop finished!"); // After the loop, print a message indicating the loop has finished
}

Explanation :

let mut count = 0; initializes a mutable variable count with an initial value of 0.

while count < 5 { ... } specifies that the loop will continue executing as long as the condition count < 5 is true.

println!("Count is: {}", count); prints the current value of count to the console.

count += 1; increments the value of count by 1 after each iteration. This is crucial to eventually meet the exit condition (count < 5).

Once count reaches 5, the condition count < 5 becomes false, and the loop exits.

println!("Loop finished!"); prints a message indicating that the loop has finished executing.

If you want to support this series, you can do so using the button below

  • “for” Loop:

This loop iterates over a sequence of values. It’s commonly used to loop over collections like arrays, vectors, or ranges.

For Example:

fn main() {
// Iterating over a range of numbers
for num in 1..=5 {
println!("Number is: {}", num);
}
}

Explanation :

The for loop iterates over a range of values (1..=5). During each iteration, num takes on the value of the current item in the range. The loop prints out each number along with a formatted message using println! .

Project #1 - Simple Calculator

Project Description

Create a Rust calculator that performs basic arithmetic operations (addition, subtraction, multiplication, division) on two integers. Users input numbers and select operations from a menu. The program uses functions for each operation, runs continuously with a loop for consecutive calculations, and handles division by zero errors. Operation execution is determined by simple if-else statements based on user input.

Solution

#![allow(unused)]

use std::io;

// Function definitions for arithmetic operations
fn add(a: i32, b: i32) -> i32 {
a + b
}

fn subtract(a: i32, b: i32) -> i32 {
a - b
}

fn multiply(a: i32, b: i32) -> i32 {
a * b
}

fn divide(a: i32, b: i32) -> Result<f64, &'static str> {
if b == 0 {
return Err("Division by zero error!");
}
Ok(a as f64 / b as f64)
}

fn main() {
loop {
println!("Welcome to the Rust Calculator!");

// Input first number
let mut num1: String = String::new();
println!("Enter the first number: ");

io::stdin().read_line(&mut num1).expect("Failed to read line");
// Parse num1 choice
let num1: i32 = num1.trim().parse().expect("Please type a valid number!");
let num1_float:f64 = num1 as f64;

// Input second number
let mut num2: String = String::new();
println!("Enter the second number: ");

io::stdin().read_line(&mut num2).expect("Failed to read line");
// Parse num2 choice
let num2: i32 = num2.trim().parse().expect("Please type a valid number!");
let num2_float:f64 = num2 as f64;

let mut operation = String::new();
// Choose operation
println!("Choose an operation:");
println!("1. Addition (+)");
println!("2. Subtraction (-)");
println!("3. Multiplication (*)");
println!("4. Division (/)");

io::stdin().read_line(&mut operation).expect("Failed to read line");
// Parse operation choice
let operation: u32 = operation.trim().parse().expect("Please enter a valid number!");

if operation == 1 {
println!("Addition is: {}", add(num1, num2));
} else if operation == 2 {
println!("Subtraction is: {}", subtract(num1, num2));
} else if operation == 3 {
println!("Multiplication is: {}", multiply(num1, num2));
} else if operation == 4 {
match divide(num1, num2) {
Ok(result) => println!("Division is: {}", result),
Err(err) => println!("{}", err),
}
} else {
println!("Invalid operation choice.");
}

// Ask if the user wants to perform another calculation
println!("Do you want to perform another calculation? (y/n)");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line");

if input.trim().to_lowercase() != "y" {
break;
}
}

println!("Goodbye!");
}

Output:

Explanation:

  • #![allow(unused)]: This directive tells the compiler to suppress warnings about unused code or variables, allowing us to focus on other aspects of the code without distractions.
  • Input Handling and Function Definitions:

Imports: The program uses std::io for input/output operations.

Functions: Four functions (add, subtract, multiply, divide) are defined to perform basic arithmetic operations on integers (i32).

add, subtract, multiply return results as i32.

divide returns a Result<f64, &'static str>, where Ok(result) contains the division result and Err("Division by zero error!") handles the case where the second operand (b) is zero.

  • Main Function (main()):

Loop (loop {}): Initiates an infinite loop to continuously prompt the user for calculations until they choose to exit.

Input Handling:

  • Prompts the user to enter two numbers (num1 and num2) and reads them from standard input (io::stdin()).
  • Parses the input strings into integers (i32).

Operation Selection:

  • Displays a menu of operations (+, -, *, /) and reads the user's choice (operation).

Conditional Statements (if else, match):

  • Based on the user’s choice (operation), executes the corresponding arithmetic function (add, subtract, multiply, divide).
  • For division (operation == 4), uses a match statement to handle the Result returned by the divide function:
  • Ok(result) => println!("Division is: {}", result): Prints the division result.
  • Err(err) => println!("{}", err): Prints the error message if division by zero occurs.

Continuation Prompt:

  • After each calculation, asks the user if they want to perform another calculation (y for yes, any other input to exit).
  • If the user chooses to exit (input.trim().to_lowercase() != "y"), breaks out of the loop.

Exit Message:

  • Prints “Goodbye!” when the user decides to exit the program.

Project Work For You:

Develop a Rust program for calculating areas of various geometric shapes such as rectangles, circles, and triangles. Users interact with the program through a command-line interface, where they can select the shape they want to calculate the area for. The program prompts users to input necessary dimensions (e.g., length and width for rectangles, radius for circles, base and height for triangles) and computes the area based on the chosen shape. Error handling is implemented to manage invalid inputs (e.g., negative dimensions) and ensure accurate calculations. The application maintains continuous operation, allowing users to calculate areas for multiple shapes consecutively until they choose to exit.

You can share your code of this project in the comments or on social media, and feel free to tag us for feedback or to showcase your work! :)

Conclusion

In this first part of the “Offensive Rust for Hackers” series, we’ve seen why Rust is great for hacking, set up our tools, and learned the basics of Rust programming. We’ve covered how to work with variables, data types, functions, loops and user input/output. Also we covered our first project i.e. Simple Calculator in rust. You’ve now got a solid foundation to build on. In the next part, we’ll explore more advanced topics like managing memory and handling errors, which are key for creating powerful and safe hacking tools.

If you enjoyed this part of the series, feel free to follow us and share your thoughts in the comments below.

You can also support me through the “Buy me a Coffee” option.

--

--

No responses yet