Offensive Rust For Hacker’s (Part — 1) | Why Rust?, Setup, Basic Concepts, Project & More . . .
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
- Step #1 — Open your web browser and visit website: https://rustup.rs/
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.
- Step #4 — Now it’s time for setting up our IDE and for this i am using rust rover. You can simply download community version using url: https://www.jetbrains.com/rust/buy/?section=personal&billing=yearly&beginnerForm=open
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 functionmain
. 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 thatprintln
is a macro, which is a special kind of function in Rust that does more complex things than regular functions. Inside theprintln!()
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 namedvalue
and assigns it the value"Hacker's!"
.println!("Hello, {}", value);
: Prints the string"Hello, "
followed by the value stored in thevalue
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 thefn
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, callsgreet()
to execute thegreet
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
andnum2
) 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 amatch
statement to handle theResult
returned by thedivide
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.