I'm trying to solve this exercise for university. We have to "crack" a program, which is missing a license file that is required to start the program. We are only working with a Linux shell.
So what I've already done is creating this missing license file. It is an .ini that includes a license-holder and a license key. The only thing i'm looking for now is the correct license key. The task says we should use "strace" and "ltrace" to solve this problem.
This is what i get as an output:
fopen("license.ini", "r") =0x55c088307380
fgets("LicenseHolder=annabell.krause#ex"..., 4096, 0x55c088307380) = 0x7ffe72450860
strncmp("LicenseKey=", "LicenseHolder=annabell.krause#ex"..., 11) = 3
strncmp("LicenseHolder=", "LicenseHolder=annabell.krause#ex"..., 14) = 0
sscanf(0x7ffe72450860, 0x55c08753c16b, 0x7ffe72450800, 0xffffc000) = 1
fgets("LicenseKey=aoeklycf", 4096, 0x55c088307380) = 0x7ffe72450860
strncmp("LicenseKey=", "LicenseKey=aoeklycf", 11) = 0
sscanf(0x7ffe72450860, 0x55c08753c121, 0x7ffe72450840, 0xfffff800) = 1
fgets("LicenseKey=aoeklycf", 4096, 0x55c088307380) = 0
memfrob(0x7ffe72450840, 8, 0, 0xfbad2498) = 0x7ffe72450840
strncmp("KEOAFSIL", "aoeklycf", 8) = -22
fwrite("ERROR: License key is invalid.\n", 1, 31, 0x7faeabe60680
ERROR: License key is invalid.
) = 31
+++ exited (status 1) +++
So I guess the answer lies somewhere within the memfrob and strncmp function at the end. But i don't know what's the next step.
Let's look at the library trace call by call. The important part is in step 5.
Analysis
Open the file
fopen("license.ini", "r") =0x55c088307380
Opens the license file.
Parse the license holder
fgets("LicenseHolder=annabell.krause#ex"..., 4096, 0x55c088307380) = 0x7ffe72450860
Reads a line from the file: LicenseHolder=annabell.krause#ex….
strncmp("LicenseKey=", "LicenseHolder=annabell.krause#ex"..., 11) = 3
Does the line start with LicenseKey=? The return value of 3 means no, it does not.
strncmp("LicenseHolder=", "LicenseHolder=annabell.krause#ex"..., 14) = 0
Does the line start with LicenseHolder=? Yes, it does.
sscanf(0x7ffe72450860, 0x55c08753c16b, 0x7ffe72450800, 0xffffc000) = 1
Unfortunately, ltrace has not dereferenced any of the addresses to show us the contents. We know that 0x7ffe72450860 is the current line, so it's presumably pulling out the e-mail address from the current line.
Parse the license key
fgets("LicenseKey=aoeklycf", 4096, 0x55c088307380) = 0x7ffe72450860
It reads another line: LicenseKey=aoeklycf.
strncmp("LicenseKey=", "LicenseKey=aoeklycf", 11) = 0
Does the line start with LicenseKey=? Yes, it does.
sscanf(0x7ffe72450860, 0x55c08753c121, 0x7ffe72450840, 0xfffff800) = 1
It's parsing the current line. Presumably, it's extracting the license key you entered, aoeklycf, and saving it in a variable for later comparison against the expected license key. Something like sscanf(line, "LicenseKey=%s", licenseKey);.
End-of-file
fgets("LicenseKey=aoeklycf", 4096, 0x55c088307380) = 0
It tries to read another line and hits EOF. Ignore the first argument, it's just showing what was left in the buffer from the last call.
License key comparison
memfrob(0x7ffe72450840, 8, 0, 0xfbad2498) = 0x7ffe72450840
"Encrypts" 8 bytes of some memory area by XORing each byte with 42. This can be reversed by running memfrob() again. I put "encrypts" in air quotes because this can barely be called encryption. It's just a little bit of obfuscation.
Notice that 0x7ffe72450840 is the address from the sscanf() above. It's frobbing the variable I called licenseKey above, the LicenseKey= string it extracted from the input file.
strncmp("KEOAFSIL", "aoeklycf", 8) = -22
This is the money line. It compares actual and expected values and fails.
Error message
fwrite("ERROR: License key is invalid.\n", 1, 31, 0x7faeabe60680) = 31
An error is printed.
Synthesis
But the author doesn't want you to be able to run a simple string search like strings ./program to pull the license key out of the executable. To prevent that you have to enter the frobbed version of the license key in license.ini, not the raw string strings finds.
The code might look something like:
char *expected = "aoeklycf";
char actual[BUFSIZE];
sscanf(line, "LicenseKey=%s", actual);
memfrob(actual);
if (strncmp(actual, expected, strlen(expected)) != 0) {
error("ERROR: License key is invalid.\n");
}
Did you extract aoeklycf from the program? If so, you missed the memfrob() step. license.ini needs to list the "encrypted" version of the license key: KEOAFSIL.
Related
I have a legacy code and some unformatted data files that it reads, and it worked with gnu-4.1.2. I don't have access to the method that originally generated these data files. When I compile this code with a newer gnu compiler (gnu-4.7.2) and attempt to load the old data files on a different computer, it is having difficulty reading them. I start by opening the file and reading in the first record which consists of three 32-bit integers:
open(unit, file='data.bin', form='unformatted', status='old')
read(unit) x,y,z
I am expecting these three integers here to describe x,y,z spans so that next it can load a 3D matrix of float values with those same dimensions. However, instead it's loading a 0 for the first value, then the next two are offset.
Expecting:
x=26, y=127, z=97 (1A, 7F, 61 in hex)
Loaded:
x=0, y=26, z=127 (0, 1A, 7F in hex)
When I checked the data file in a hex editor, I think I figured out what was happening.
The first record marker in this case has a value of 12 (0C in hex) since it's reading three integers at 4 bytes each. This marker is stored both before and after the record. However, I notice that the 32bits immediately after each record marker is 00000000. So either the record markers are treated as 64bit integers (little-Endian) or there is a 32-bit zero padding after each record marker. Either way, the code generated with the new compiler is reading the record markers as 32-bit integers and not expecting any padding. This effectively intrudes/corrupts the data being read in.
Is there an easy way to fix this non-portable issue? The old and new hardware are 64 bit architecture and so is the executable I compiled. If I try to use the older compiler version again will it solve the problem, or is it hardware dependent? I'd prefer to use the newer compilers because they are more efficient, and I really don't want to edit the source code to open all the files as access='stream' and manually read in a trailing 0 integer after each record marker, both before and after each record.
P.S. I could probably write a C++ code to alter the data files and remove these zero paddings if there is no easier alternative.
See the -frecord-marker= option in the gfortran manual. With -frecord-marker=8 you can read the old style unformatted sequential files produced by older versions of gfortran.
Seeing as how Fortran doesn't have a standardization on this, I opted to convert the data files to a new format that uses 32-bit wide record lengths instead of 64-bit wide. In case anyone needs to do this in the future I've included some Visual C++ code here that worked for me and should be easily modifiable to C or another language. I have also uploaded a Windows executable (fortrec.zip) here.
CFile OldFortFile, OutFile;
const int BUFLEN = 1024*20;
char pbuf[BUFLEN];
int i, iIn, iRecLen, iRecLen2, iLen, iRead, iError = 0;
CString strInDir = "C:\folder\";
CString strIn = "file.dat";
CString strOutDir = "C:\folder\fortnew\"
system("mkdir \"" + strOutDir + "\""); //create a subdir to hold the output files
strIn = strInDir + strIn;
strOut = strOutDir + strIn;
if(OldFortFile.Open(strIn,CFile::modeRead|CFile::typeBinary)) {
if(OutFile.Open(strOut,CFile::modeCreate|CFile::modeWrite|CFile::typeBinary)) {
while(true) {
iRead = OldFortFile.Read(&iRecLen, sizeof(iRecLen)); //Read the record's raw data
if (iRead < sizeof(iRecLen)) //end of file reached
break;
OutFile.Write(&iRecLen, sizeof(iRecLen));//Write the record's raw data
OldFortFile.Read(&iIn, sizeof(iIn));
if (iIn != 0) {//this is the padding we need to ignore, ensure it's always zero
//Padding not found
iError++;
break;
}
i = iRecLen;
while (i > 0) {
iLen = (i > BUFLEN) ? BUFLEN : i;
OldFortFile.Read(&pbuf[0], iLen);
OutFile.Write(&pbuf[0], iLen);
i -= iLen;
}
if (i != 0) { //Buffer length mismatch
iError++;
break;
}
OldFortFile.Read(&iRecLen2, sizeof(iRecLen2));
if (iRecLen != iRecLen2) {//ensure we have reached the end of the record proeprly
//Record length mismatch
iError++;
break;
}
OutFile.Write(&iRecLen2, sizeof(iRecLen));
OldFortFile.Read(&iIn, sizeof(iIn));
if (iIn != 0) {//this is the padding we need to ignore, ensure it's always zero
//Padding not found
break;
}
}
OutFile.Close();
OldFortFile.Close();
}
else { //Could not create the ouput file.
OldFortFile.Close();
return;
}
}
else { //Could not open the input file
}
if (iError == 0)
//File successfully converted
else
//Encountered error
I am quite rusty with C and system calls and pointers in general, so this is a good refresher exercise to get back on track. All I need to do is, given a file such as this:
YYY.txt: "somerandomcharacters"
Change it to be like this:
YYY.txt: "somerandomabcdefghijklmnopqrstuvwxyzcharacters"
So all that is done is some characters added to the middle of the file. Obviously, this is quite simple, but in C you must keep track and manage the size of the file in advance before adding the additional characters.
Here is my naive try:
//(Assume a file called YYY.txt exists and an int YYY is the file descriptor.)
char ToBeInserted[26] = "abcdefghijklmnopqrstuvwxyz";
//Determine the current length of YYY
int LengthOfYYY = lseek(YYY, 0, 2);
if(LengthOfYYY < 0)
printf("Error upon using lseek to get length of YYY");
//Assume we want to insert at position 900 in YYY.txt, and length of YYY is over 1000.
//1.] Keep track of all characters past position 900 in YYY and store in a char array.
lseek(YYY, 900, 0); //Seeks to position 900 in YYY, so reading begins there.
char NextChar;
char EverythingPast900[LengthOfYYY-900];
int i = 0;
while(i < (LengthOfYYY - 900)) {
int NextRead = read(YYY, NextChar, 1); //Puts next character from YYY in NextChar
EverythingPast900[i] = NextChar;
i++;
}
//2.] Overwrite what used to be at position 900 in YYY:
lseek(YYY, 900, 0); //Moves to position 900.
int WriteToYYY = write(YYY, ToBeInserted, sizeof(ToBeInserted));
if(WriteToYYY < 0)
printf("Error upon writing to YYY");
//3.] Move to position 900 + length of ToBeInserted, and write the characters that were saved.
lseek(YYY, 926, 0);
int WriteMoreToYYY = write(YYY, EverythingPast900, sizeof(EverythingPast900));
if (WriteMoreToYYY < 0) {
printf("Error writing the saved characters back into YYY.");
}
I think the logic is sound, mostly, although there are much better ways to do it in C. I need help on my C pointers, basically, as well as the UNIX system calls. Does anyone mind walking me through how to properly implement this in C?
That's the basic idea. If you had to really conserve RAM and the file was a lot bigger you'd want to copy block by block in reverse order. But the simpler way is to read the entire thing into memory and rewrite the entire file.
also, I prefer the stream functions: fopen, fseek, fread. But the file descriptor method works.
Hello I have file with text:
14
5 4
45 854
14
4
47 5
I need to write a text to a specific line. For example to the line number 4 (Doesn't matter whether I will append the text or rewrite the whole line):
14
5 4
45 854
14 new_text
4
47 5
I have found function fseek(). But in the documentation is written
fseek(file pointer,offset, position);
"Offset specifies the number of positions (bytes) to be moved from the location specified bt the position."
But I do not know the number of bites. I only know the number of lines. How to do that? Thank you
You can't do that, (text) files are not line-addressable.
Also, you can't insert data in the middle of a file.
The best way is to "spool" to a new file, i.e. read the input line by line, and write that to a new file which is the output. You can then easily keep track of which line you're on, and do whatever you want.
I will assume that you are going to be doing this many times for a single file, as such you would be better indexing the position of each newline char, for example you could use a function like this:
long *LinePosFind(int FileDes)
{
long * LinePosArr = malloc(500 * sizeof(long));
char TmpChar;
long LinesRead = 0;
long CharsRead = 0;
while(1 == read(FileDes, &TmpChar, 1))
{
if (!(LinesRead % 500)
{
LinePosArr = realloc(LinePosArr, (LinesRead + 500) * sizeof(long));
}
if (TmpChar == '\n')
{
LinePosArr[LinesRead++] = CharsRead;
}
CharsRead++;
}
return LinePosArr;
}
Then you can save the index of all the newlines for repeated use.
After this you can use it like so:
long *LineIndex = LinePosFind(FileDes);
long FourthLine = LineIndex[3];
Note I have not checked this code, just written from my head so it may need fixes, also, you should add some error checking for the malloc and read and realloc if you are using the code in production.
I asked this a while ago but was really vague and I also made some changes to my code.
I have a file that I call "stats.txt" which I open with: (using "C" btw)
fopen("stats.txt", r+)
During the first run of my program, I will ask the user to fill in the variables used to write to the file:
fprintf(fp, "STR: %i(%i)\n", STR, smod);
fprintf(fp, "DEX: %i(%i)\n", DEX, dmod);
etc...
the file looks like this after the programs first run, with all the numbers corresponding to a variable in the program:
Level 1 Gnome Wizard:
STR: 8(-1)
DEX: 14(2)
CON: 14(2)
INT: 13(1)
WIS: 13(1)
CHR: 12(1)
APP: 11(0)
Fort save: 0
Reflex save: 0
Will save: 3
when the program closes and runs for a second time, I have an "IF" statement checking for and displaying text within the "stats.txt" file:
if (fgets(buf, 1000, fp) == NULL)
{
printf("Please enter in your base stats (no modifiers):\n");
enter_stats();
printf("Please indicate your characters level:\n");
printf("I am a level ");
level = GetInt();
Race_check();
spec_check();
printf("------Base saving throws (no modifiers)------\n");
saving_throws();
}
else
{
printf("%s",buf);
}
The problem that I am having is the fact that the program reads the file, but does not transfer any variable values it seems here:
Level 1 Gnome Wizard:
-------------------------
STR: 0(-5)
DEX: 0(-5)
CON: 0(-5)
INT: 0(-5)
WIS: 0(-5)
CHR: 0(-5)
APP: 0(-5)
-----Saving Throws------
Fortitude: 0
Reflex: 0
Will: 0
Can anyone give me their suggestions on how to read the variables as well?
PLease and Thank you
Computers only understand numbers - they don't understand text. This means that you have to write code to convert the numbers (that represent individual characters) back into the values you want and store them somewhere.
For example, you might load the entire file into an "array of char", then search that "array of char" for the 4 numbers that represent STR:, then skip any whitespace (between the STR: and the 0(0)), then convert the character/s 0 into the value 0 and store it somewhere, then check for a ( character, then convert the characters -1 into the value -1 and store it somewhere, then check for the ) character and the newline character \n.
More likely is to arrange the code as a "for each line" loop, where the first characters of a line determine how to process the other characters. E.g. if the first character is - then ignore the line; else if the first 5 characters are level call a function that processes the remainder of the line (1 Gnome Wizard); else if the first few characters are STR:, DEX:, CON, etc call a function to get both numbers (and check for the right brackets, etc); else...
In addition to all this, you should have good error handling. As a rough guide, about half of the code should be checks and error messages (like if( buffer[i] != '(' ) { printf("ERROR: Expecting left bracket after number on line %u", lineNumber); return -1;}).
Using Windows
So I'm reading from a binary file a list of unsigned int data values. The file contains a number of datasets listed sequentially. Here's the function to read a single dataset from a char* pointing to the start of it:
function read_dataset(char* stream, t_dataset *dataset){
//...some init, including setting dataset->size;
for(i=0;i<dataset->size;i++){
dataset->samples[i] = *((unsigned int *) stream);
stream += sizeof(unsigned int);
}
//...
}
Where read_dataset in such a context as this:
//...
char buff[10000];
t_dataset* dataset = malloc( sizeof( *dataset) );
unsigned long offset = 0;
for(i=0;i<number_of_datasets; i++){
fseek(fd_in, offset, SEEK_SET);
if( (n = fread(buff, sizeof(char), sizeof(*dataset), fd_in)) != sizeof(*dataset) ){
break;
}
read_dataset(buff, *dataset);
// Do something with dataset here. It's screwed up before this, I checked.
offset += profileSize;
}
//...
Everything goes swimmingly until my loop reads the number 2573. All of a sudden it starts spitting out random and huge numbers.
For example, what should be
...
1831
2229
2406
2637
2609
2573
2523
2247
...
becomes
...
1831
2229
2406
2637
2609
0xDB00000A
0xC7000009
0xB2000008
...
If you think those hex numbers look suspicious, you're right. Turns out the hex values for the values that were changed are really familiar:
2573 -> 0xA0D
2523 -> 0x9DB
2247 -> 0x8C7
So apparently this number 2573 causes my stream pointer to gain a byte. This remains until the next dataset is loaded and parsed, and god forbid it contain a number 2573. I have checked a number of spots where this happens, and each one I've checked began on 2573.
I admit I'm not so talented in the world of C. What could cause this is completely and entirely opaque to me.
You don't specify how you obtained the bytes in memory (pointed to by stream), nor what platform you're running on, but I wouldn't be surprised to find your on Windows, and you used the C stdio library call fopen(filename "r"); Try using fopen(filename, "rb");. On Windows (and MS-DOS), fopen() translates MS-DOS line endings "\r\n" (hex 0x0D 0x0A) in the file to Unix style "\n", unless you append "b" to the file mode to indicate binary.
A couple of irrelevant points.
sizeof(*dataset) doesn't do what you think it does.
There is no need to use seek on every read
I don't understand how you are calling a function that only takes one parameter but you are giving it two (or at least I don't understand why your compiler doesn't object)