1.Malware ∀-SRWD
1/25/2024
What is ∀-SRWD? As hinted in article 2, "Modular-learning. Stacking it like
Legos", It is a malware made with the capabilities to search for, read from, write to, and delete files system-wide for as long as it has proper
permissions. In this project, we will be writing the modular snippets of code for each of these 4 functions, and then integrate all of these
functions into a single, monolithic, program. As this allows for greater focus on extending the
functionality of each feature rather than worrying about bridging all of them together cohesively which there is always a way and time to do.
Click on the Github links below to gain access to the codes used:
https://github.com/CyberTHX1138/CFuncsTemplateshttps://github.com/CyberTHX1138/Malware--SRWD-
search
The functionality we'll start with is the search functionality. As the first iteration of this function, it is meant to work, not exactly be perfect and polished, but good enough to do something with. As it stands, the function recursively looks, system-wide, for the provided (hardcoded) file's name. If it has permissions to view the contents of a folder (with/out root permissions, such as /root/) it will keep looking even after it finds the needed file.
below is the snippet of code concerned with the search function. Below it will be a boring detailing of what the code does and why:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
const char *targetFile = "co.txt";
void searchFile(const char *dirPath) {
DIR *dir;
struct dirent *entry;
struct stat statBuf;
if (!(dir = opendir(dirPath))) {
perror("opendir");
return;
}
while ((entry = readdir(dir)) != NULL) {
char fullPath[1024];
snprintf(fullPath, sizeof(fullPath), "%s/%s", dirPath, entry->d_name);
if (lstat(fullPath, &statBuf) == -1) {
perror("lstat");
continue;
}
if (S_ISDIR(statBuf.st_mode)) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
searchFile(fullPath);
} else if (S_ISREG(statBuf.st_mode)) {
if (strcmp(entry->d_name, targetFile) == 0) {
printf("Found: %s\n", fullPath);
}
}
}
closedir(dir);
}
int main() {
searchFile("/");
return 0;
}
The following is a detailed explanation of the code:
- LINES 1-5: #include <stdio.h> for input/output, <stdlib.h> for memory allocation, <string.h> for string manipulation, <dirent.h> for directory-related functions, and <sys/stat.h> for file status functions.
- LINES 7: A constant string targetFile is declared and initialized with the value "SearchFiles.c". This is the hardcoded target file name the program is searching for.
- LINES 9-17: This is the beginning of the searchFile function. It takes a directory path as a parameter. It uses the DIR and struct dirent types to represent a directory stream and an entry in that stream, respectively. It also uses struct stat to get information about a file. The function attempts to open the directory using opendir(). If it fails, it prints an error message using perror() and returns from the function.
- LINES 19-26: This is a loop that iterates through each entry in the directory. For each entry, it constructs the full path by concatenating the directory path and the entry name. It then uses lstat() to get information about the file. If lstat() fails, it prints an error message using perror() and continues to the next iteration of the loop.
- LINES 28-41: Inside the loop, it checks if the entry is a directory (S_ISDIR(statBuf.st_mode)). If it is, it skips the "." and ".." entries and recursively calls the searchFile function with the new directory path. If it's a regular file (S_ISREG(statBuf.st_mode)), it checks if its name matches the target file name. If it does, it prints a message indicating that the file is found.
- LINES 43-47: The main function is simple. It calls the searchFile function, starting the search from the root directory ("/"). The program returns 0, indicating successful execution.
To dive into it in a more organic style:
-
1. Header Files:
These are the standard C header files included for input/output, memory allocation, string manipulation, directory operations, and file information.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #include <sys/stat.h>
-
2. Constant Declaration:
This line declares a constant string targetFile with the value "co.txt", representing the file to be searched.
const char *targetFile = "co.txt";
-
3. Function Declaration:
This is a function prototype for searchFile, which will be defined later in the code. It takes a directory path as an argument.
void searchFile(const char *dirPath);
-
4. Function Definition - searchFile:
This function is responsible for recursively searching for the target file in the specified directory and its subdirectories.
void searchFile(const char *dirPath) { // Function body }
-
5. Directory and File Information Variables:
These variables are used to store information about directories and files during the search process.
DIR *dir; struct dirent *entry; struct stat statBuf;
-
6. Directory Opening and Error Handling:
Attempts to open the specified directory (dirPath). If unsuccessful, it prints an error message using perror and exits the function.
if (!(dir = opendir(dirPath))) { perror("opendir"); return; }
-
7. Directory Entry Loop:
Iterates through each entry (file or subdirectory) in the currently opened directory.
while ((entry = readdir(dir)) != NULL) { // Loop body }
-
8. Building Full Path:
Constructs the full path by combining the current directory path (dirPath) with the name of the entry.
char fullPath[1024]; snprintf(fullPath, sizeof(fullPath), "%s/%s", dirPath, entry->d_name);
-
9. Checking File Information:
Checks whether the entry is a directory using S_ISDIR and skips "." and ".." entries.
if (S_ISDIR(statBuf.st_mode)) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } searchFile(fullPath); }
-
10. Checking Regular File:
Checks if the entry is a regular file and if its name matches the target file (targetFile).
else if (S_ISREG(statBuf.st_mode)) { if (strcmp(entry->d_name, targetFile) == 0) { printf("Found: %s\n", fullPath); } }
-
11. Closing File:
Closes the currently opened file.
fclose(filePointer);
-
12. Returning from Function:
Returns from the searchFile function.
return;
-
13. Main Function:
Calls searchFile starting from the root directory ("/").
int main() { searchFile("/"); return 0; }
-
14. Directory Closing:
Closes the currently opened directory.
closedir(dir);
-
15. Main Function:
Calls searchFile starting from the root directory ("/").
int main() { searchFile("/"); return 0; }
As this is the first iteration of the code, it isn't perfect. For example, it spams the terminal with "opendir: permission denied". As for the next iteration of this code, I'll remove this printed spam to only output if it found the specified file.
Read
Our second functionality, reading the specified file. Simply, as long as the compiled program has proper permissions to read the contents
of the meant file, and nothing goes wrong with initializing the pointer, storing read data to the pointer and displaying that, it should
print out the data on the terminal!
below is the snippet of code concerned with the read function. Below it will be a detailing of what the code does and why:
#include <stdio.h>
int main() {
FILE *filePointer;
filePointer = fopen("FILE_PATH", "r");
if (filePointer == NULL) {
printf("Reading Failed!");
return 1;
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), filePointer) != NULL) {
printf("%s", buffer);
}
fclose(filePointer);
return 0;
}
- LINE 4: "FILE" initializes the pointer with a data type. It could be "int", "char", etc. The "*filePointer" initializes the pointer's name.
- LINE 6: "fopen" is used to open a file named "FILE_PATH" in a different directory path with the reading mode indicated by "r".
- LINES 8-11: If our "filePointer" address is assigned NULL, meaning the "fopen" function failed, print out the error message.
- LINES 15-17: "fgets" function gets strings from the meant file. "buffer" is the space array where what is read will be stored. "sizeof(buffer)" is the maximum amount of characters, and "filePointer" is simply the name of the file we assigned to be read. All of this is being done in a while loop to ensure that everything in the file gets printed out even if it's larger than our assigned buffer size.
In this function we were introduced for a second time to the sizeof() operator in C. We've used it in the Search functionality but I felt that the section was getting too crowded for a comfy read so I decided to explain it later to not overwhelm the reader. The sizeof() operator is used to determine the minimum required space to store the specified data or expressions in an integer, float, pointer, structure, union, etc.. We mainly use the sizeof() operator when we want to keep constant, the assigned storage size for each data type when working with the compiled code can run correctly across multiple OS & CPU architectures and machines. Be it 32-bit, 64-bit, ARM, PowerPC, ESP32, etc...This is because different CPU architectures assign byte-storage differently and when dealing with pointers, arrays, or structures, knowing the size of the data type ensures proper memory management.
We mainly use the sizeof() operator when we want to keep constant, the assigned storage size for each data type when working with the compiled code can run correctly across multiple OS & CPU architectures and machines. Be it 32-bit, 64-bit, ARM, PowerPC, ESP32, etc...This is because different CPU architectures assign byte-storage differently and when dealing with pointers, arrays, or structures, knowing the size of the data type ensures proper memory management.
I do apologize if the above isn't as detailed as what you were hoping for although I'd like to dive deeper into the topic with a better base example and project for learning!
Write
Our third functionality: writing (not appending) files. Please be considerate of what you name the file that you want to be created by the function.
If you name it after a file that already exists in the folder/directory you're in, the original file will be replaced (if the file doesn't require higher privileges).
Regarding any experimentation or development regarding this function and the last one to come, I strongly suggest setting up a Linux/Unix VM and compiling and running these functions
there. To avoid any file replacements/deletions.
As I believe this code to be even simpler than the "Read" function above, I'll spend as much time on it.
below is the snippet of code concerned with the write function. BeSlow it will be a detailing of what the code does and why:
#include <stdio.h>
int main() {
FILE *filePointer;
filePointer = fopen("WritableFile.txt", "w");
if (filePointer == NULL) {
printf("Error opening the new file.\n");
return 1;
}
fprintf(filePointer, "FILE CONTENT WRITTEN IN C!!!");
fclose(filePointer);
return 0;
}
The following is a basic explanation of the code:
- LINE 1: "Header provides input/output functionality, as well as printf and perror."
- LINE 5: Declare a constant string, assign it to pointer "file_path", and make its value the file's path.
-
LINES 8-13:
We delete the file and check if successful in a single statement.
DELETE
As a reminder, please test and develop this segment of the code in a VM or a folder/directory that doesn't hold any important file(s).
The 4th and last function, the deletion of files (this current iteration of code only works on a single files)
#include <stdio.h>
int main() {
// Hardcoded file name
const char *file_path = "/path/to/your/file.txt";
// Remove file & check if successful
if (remove(file_path) == 0) {
printf("File %s deleted successfully.\n", file_path);
} else {
perror("Error deleting file");
return 1;
}
return 0;
}
- LINE 1: Header provides input/output functionality, as well as printf and perror.
- LINE 5: Declare a constant string, assign it to pointer "file_path", and make its value the file's path.
- LINES 8-13: We delete the file and check if successful in a single statement
Now you might ask "Hey Cyber, how are we checking if the remove operation was successful without calling it first? can't we just do remove(file_path); and then check for it?"
Well, if you do that you get the perror message. Why? because you'd be calling the same file-operation twice. But how are you calling it twice? Well, in C, when you call an expression
in a condition statement, your function, operation, or comparison is called and executed and the output is checked. So by simply calling our expression into the condition statement,
we are executing it!
That's C for you! and it actually makes sense once you think about it more. If I ask you "Hey does 1+1 = 2?" how would you know whatever to answer yes or no? you have to run it in your head, add 1 to 1. I didn't ask you to add 1+1, I asked you if 1+1 = 2, you've done the operation of adding on your own.
and so whenever we call an expression, not a variable or a constant, into a comparison or a boolean or any statement, we are actually running the expression to then evaluate it's result.
STACKING IT UP ALL TOGETHER
All what is left now is stringing it all together! What I'm going to do is turn each function we've made into a void function that is called into the main() function we'll implement now. void is a function prototype that doesn't send data back to the main() function. It (void function) can take arguments and other inputs from the main() function but it cannot send back any data streams to be used by the main() function. We'll implement this simply as follows below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void createAndWriteToFile(const char *filePath, const char *content) {
FILE *filePointer;
filePointer = fopen(filePath, "w");
if (filePointer == NULL) {
printf("Error opening/creating the file.\n");
return;
}
fprintf(filePointer, "%s", content);
fclose(filePointer);
}
void readAndPrintFile(const char *filePath) {
FILE *filePointer;
filePointer = fopen(filePath, "r");
if (filePointer == NULL) {
printf("Reading Failed!\n");
return;
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), filePointer) != NULL) {
printf("%s", buffer);
}
fclose(filePointer);
}
void deleteFile(const char *filePath) {
if (remove(filePath) == 0) {
printf("File %s deleted successfully.\n", filePath);
} else {
perror("Error deleting file");
}
}
void searchFile(const char *dirPath, const char *targetFile) {
DIR *dir;
struct dirent *entry;
struct stat statBuf;
if (!(dir = opendir(dirPath))) {
perror("opendir");
return;
}
while ((entry = readdir(dir)) != NULL) {
char fullPath[1024];
snprintf(fullPath, sizeof(fullPath), "%s/%s", dirPath, entry->d_name);
if (lstat(fullPath, &statBuf) == -1) {
perror("lstat");
continue;
}
if (S_ISDIR(statBuf.st_mode)) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
searchFile(fullPath, targetFile);
} else if (S_ISREG(statBuf.st_mode)) {
if (strcmp(entry->d_name, targetFile) == 0) {
printf("Found: %s\n", fullPath);
}
}
}
closedir(dir);
}
Now that we've literally just copy-pasted the functions we wrote before into a single file, which is the goal of this whole learning/working method of modular learning, making reusable functions,
and we've just changed every function name from main() to what the function does and int to void, our very final step is to make logic flow of the program, enable our user to pick what file operation they want,
what file name & path, and what they want to write into the file (in the case of the CreateAndWriteToFile)
We can simply start by declaring our integer variable that will hold the number corresponding to our choice of file operation, and declaring the variables that will hold our filename (and path) and declaring our content variable that will hold the content we want to be written to a file, assigning a buffer of 256 bytes to each.
int main() {
int choice;
char fileName[256];
char content[256];
And now we can decide what kind of conditional to use to check what the user chose as their file operation. The most obvious choice is to use if/else statements, although I would like for y'all to learn about our lord and savior, SWITCH STATEMENTS AND CASES!
What are those? Well, they're conditionals just like if/else statements but, instead of checking the given value against conditions-to-be-met until it reaches a case that it agrees with, switches don't need to run against every statement in order to check if it's true or false or whatever, it directly goes in an instant (compared to the time if/else takes) to the statement that returns true and executes the corresponding code. Why am I showing this to you guys? I don't want you to make the same mistake Yandere Dev. made with his use of if/else statements (go search it on Youtube). Later in your career when you're working on larger projects with larger sets of conditionals, I would like for you to know what's the most optimal technique to use.
The implementation is below:
while (1) {
printf("Choose a function:\n");
printf("1. Create and Write to File\n");
printf("2. Read and Print File\n");
printf("3. Delete File\n");
printf("4. Search for File\n");
printf("0. Exit\n");
printf("Enter your choice (0-4): ");
scanf("%d", &choice);
if (choice == 0) {
break; // Exit the program
}
if (choice != 4) {
printf("Enter the file name (full path included): ");
scanf("%s", fileName);
}
switch (choice) {
case 1:
printf("Enter the content to write to the file: ");
scanf(" %[^\n]", content);
createAndWriteToFile(fileName, content);
break;
case 2:
readAndPrintFile(fileName);
break;
case 3:
deleteFile(fileName);
break;
case 4:
searchFile("/", fileName);
break;
default:
printf("Invalid choice.\n");
break;
}
}
return 0;
}
- LINES 88-90: We initialize the int variable that will later hold our operation's # choice. The char fileName[256] will hold the file's name as well as the full path to the file. char content[256] will hold the content of the made file when applicable.
- LINE 92: While(1) will ensure that the loop keeps running forever unless we use ^C or we pick the exit option. Even after we execute our file operation, It will keep going and return to the first menu of options.
- LINES 112-129: The switch statement takes in the inputted int that's assigned to choice, and based off of what it was, it executes the code of block (calling function) right before the break statement.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void createAndWriteToFile(const char *filePath, const char *content) {
FILE *filePointer;
filePointer = fopen(filePath, "w");
if (filePointer == NULL) {
printf("Error opening/creating the file.\n");
return;
}
fprintf(filePointer, "%s", content);
fclose(filePointer);
}
void readAndPrintFile(const char *filePath) {
FILE *filePointer;
filePointer = fopen(filePath, "r");
if (filePointer == NULL) {
printf("Reading Failed!\n");
return;
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), filePointer) != NULL) {
printf("%s", buffer);
}
fclose(filePointer);
}
void deleteFile(const char *filePath) {
if (remove(filePath) == 0) {
printf("File %s deleted successfully.\n", filePath);
} else {
perror("Error deleting file");
}
}
void searchFile(const char *dirPath, const char *targetFile) {
DIR *dir;
struct dirent *entry;
struct stat statBuf;
if (!(dir = opendir(dirPath))) {
perror("opendir");
return;
}
while ((entry = readdir(dir)) != NULL) {
char fullPath[1024];
snprintf(fullPath, sizeof(fullPath), "%s/%s", dirPath, entry->d_name);
if (lstat(fullPath, &statBuf) == -1) {
perror("lstat");
continue;
}
if (S_ISDIR(statBuf.st_mode)) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
searchFile(fullPath, targetFile);
} else if (S_ISREG(statBuf.st_mode)) {
if (strcmp(entry->d_name, targetFile) == 0) {
printf("Found: %s\n", fullPath);
}
}
}
closedir(dir);
}
int main() {
int choice;
char fileName[256];
char content[256];
printf("Choose a function:\n");
printf("1. Create and Write to File\n");
printf("2. Read and Print File\n");
printf("3. Delete File\n");
printf("4. Search for File\n");
printf("0. Exit\n");
printf("Enter your choice (0-4): ");
scanf("%d", &choice);
if (choice == 0) {
break; // Exit the program
}
if (choice != 4) {
printf("Enter the file name (full path included): ");
scanf("%s", fileName);
}
switch (choice) {
case 1:
printf("Enter the content to write to the file: ");
scanf(" %[^\n]", content);
createAndWriteToFile(fileName, content);
break;
case 2:
readAndPrintFile(fileName);
break;
case 3:
deleteFile(fileName);
break;
case 4:
searchFile("/", fileName);
break;
default:
printf("Invalid choice.\n");
break;
}
}
return 0;
Future Increments
As for the future, I'll make sure to stick to my philosophy and values of learning and building projects through modular-development, I'll make sure to periodically post an update on this site of the Github commits that improve the project.
To finish all of this, it took me a total of 4 days to turn the concept and workflow from pseudo code to C-code, and to build and add upon the theme of this specific page from the general theme of the website. I hope to get better overall at this as I spend more writing and developing stuff. And I am thankful to you, reading this far into the project.
Please don't shy from sending suggestions or anything to the contacts listed in Whoami.
This page was made with HTML, CSS, & prism.js for syntax highlighting