Jump to content
Tuts 4 You

Edit History

zero_gear

zero_gear

Solution write-up

Manual Unpacking of UPX

Described here. x32dbg & scylla are your friends.

Explore unpacked program control flow

Using cross-references to strings "Registration name:", "Registration Serial:", "\nRegistration Succeeded", "\nRegistration Failed" i located print function(VA 0x007d6f40) and all calls to it. Single stepping between print("Registration name:") and print("Registration Serial:") i have empirically defined wrapper function that get input from terminal(VA 0x007D7C70). Ok, we have narrow range to analyze : from call to get input name at 0x0061641C to call of print("_\nRegistration Failed_") at 0x006FF521 or call of print("\nRegistrationSucceeded") at 0x006EFCE1.

Lets go.

Code analysis is hardened with innumerable absolute jump instructions, that interleave code, so called "spaghetti-code".
spaghetti-code.png

Between each asm instruction, there are 30-80 trash jump instructions.

x64dbg trace command TraceIntoConditional 1:[cip]!=0xe9 && 1:[cip]!=0xeb, which trace until meet not jump instruction save us tons of hours. Tracing such way, we stop only at useful instructions and set software breakpoints on them. Now we can switch to x32dbg 'Breakpoints' tab, and overview all instructions that validate name:serial pair without trash jumps.

pure_validation_code.png

Validation algorithm:

0 step: get name and serial from input

char name[0x22+1] = {0}; 
char input_serial[32+1+8+1] = {0}; 
puts("Registration name:");
fgets (name, 0x22+1, stdin);
puts("Registration Serial:");
fgets (name, 41+1, stdin);

 

1st step: check name length

2 < name <= 0x22

 

2nd step: get CPUID

uint32_t cpuid[4] = {0};
__cpuid(cpuid, 0);

 

3d step: get 4-byte hash from name

uint32_t Nhash = 0; 
for (size_t i = 0; i < strlen(name); i++) { 
     Nhash += name[i]; 
     Nhash ^= 0x00ABCDEF; 
     Nhash = Nhash + (cpuid[0] ^ Nhash); 
}

 

4th step: get valid serial from name and CPUID

char derived_serial[32 + 1 + 8] = {0};
sprintf(derived_serial, "%X%X%X%X-%X", cpuid[0], cpuid[1], cpuid[2], cpuid[3], Nhash);

 

5th step: compare input serial and serial derived from name and CPUID

if (strcmp(input_serial, derived_serial) == 0){ 
	puts("\nRegistration Succeeded"); 
} else { 
	puts("\nRegistration Failed"); 
}

 

Keygen is simply made from above code:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

char * name;
uint32_t Nhash = 0;
uint32_t cpuid[4];
char serial[2*16 + 1 + 2*4];

int main(int argc, char *argv[]) {
	if (argc != 2) {
		printf("[!] Usage: keygen.exe name\n");
		return 1;
	}
	name = argv[1];
	if (strlen(name) <= 2) {
		printf("[!] Name is too short. It`s length should be minimum 3 chars.\n");
		return 2;
	}
	if (strlen(name) > 0x22) {
		printf("[!] Name is too long. It`s length should be maximum 34 chars.\n");
		return 2;
	}
	printf("Provided name : %s\n", name);

	__cpuid(cpuid, 0);

	for (size_t i = 0; i < strlen(name); i++) {
		Nhash += name[i];
		Nhash ^= 0x00ABCDEF;
		Nhash = Nhash + (cpuid[0] ^ Nhash);
	}

	sprintf(serial, "%X%X%X%X-%X", cpuid[0], cpuid[1], cpuid[2], cpuid[3], Nhash);
	printf("Your serial : %s\n", serial);

	return 0;
}

Finally

Keygen is attached, sources are HERE.

Acknowledgement
thanks @SmilingWolf, @mrexodia for help with x32dbg trace commands and expressions.

Question:
Maybe there is another way to analyze such "spaghetti"-obfuscated binary code?, or maybe someone could recommend another approach in omitting trash jump instructions. Maybe you know de-obfuscation technique or tool that is applicable to simplify analyzing of this "spaghetti". Please, write here or PM.

Keygen.exe

zero_gear

zero_gear

Solution write-up

Manual Unpacking of UPX

Described here. x64dbg & scylla are your friends.

Explore unpacked program control flow

Using cross-references to strings "Registration name:", "Registration Serial:", "\nRegistration Succeeded", "\nRegistration Failed" i located print function(VA 0x007d6f40) and all calls to it. Single stepping between print("Registration name:") and print("Registration Serial:") i have empirically defined wrapper function that get input from terminal(VA 0x007D7C70). Ok, we have narrow range to analyze : from call to get input name at 0x0061641C to call of print("_\nRegistration Failed_") at 0x006FF521 or call of print("\nRegistrationSucceeded") at 0x006EFCE1.

Lets go.

Code analysis is hardened with innumerable absolute jump instructions, that interleave code, so called "spaghetti-code".
spaghetti-code.png

Between each asm instruction, there are 30-80 trash jump instructions.

x64dbg trace command TraceIntoConditional 1:[cip]!=0xe9 && 1:[cip]!=0xeb, which trace until meet not jump instruction save us tons of hours. Tracing such way, we stop only at useful instructions and set software breakpoints on them. Now we can switch to x32dbg 'Breakpoints' tab, and overview all instructions that validate name:serial pair without trash jumps.

pure_validation_code.png

Validation algorithm:

0 step: get name and serial from input

char name[0x22+1] = {0}; 
char input_serial[32+1+8+1] = {0}; 
puts("Registration name:");
fgets (name, 0x22+1, stdin);
puts("Registration Serial:");
fgets (name, 41+1, stdin);

 

1st step: check name length

2 < name <= 0x22

 

2nd step: get CPUID

uint32_t cpuid[4] = {0};
__cpuid(cpuid, 0);

 

3d step: get 4-byte hash from name

uint32_t Nhash = 0; 
for (size_t i = 0; i < strlen(name); i++) { 
     Nhash += name[i]; 
     Nhash ^= 0x00ABCDEF; 
     Nhash = Nhash + (cpuid[0] ^ Nhash); 
}

 

4th step: get valid serial from name and CPUID

char derived_serial[32 + 1 + 8] = {0};
sprintf(derived_serial, "%X%X%X%X-%X", cpuid[0], cpuid[1], cpuid[2], cpuid[3], Nhash);

 

5th step: compare input serial and serial derived from name and CPUID

if (strcmp(input_serial, derived_serial) == 0){ 
	puts("\nRegistration Succeeded"); 
} else { 
	puts("\nRegistration Failed"); 
}

 

Keygen is simply made from above code:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

char * name;
uint32_t Nhash = 0;
uint32_t cpuid[4];
char serial[2*16 + 1 + 2*4];

int main(int argc, char *argv[]) {
	if (argc != 2) {
		printf("[!] Usage: keygen.exe name\n");
		return 1;
	}
	name = argv[1];
	if (strlen(name) <= 2) {
		printf("[!] Name is too short. It`s length should be minimum 3 chars.\n");
		return 2;
	}
	if (strlen(name) > 0x22) {
		printf("[!] Name is too long. It`s length should be maximum 34 chars.\n");
		return 2;
	}
	printf("Provided name : %s\n", name);

	__cpuid(cpuid, 0);

	for (size_t i = 0; i < strlen(name); i++) {
		Nhash += name[i];
		Nhash ^= 0x00ABCDEF;
		Nhash = Nhash + (cpuid[0] ^ Nhash);
	}

	sprintf(serial, "%X%X%X%X-%X", cpuid[0], cpuid[1], cpuid[2], cpuid[3], Nhash);
	printf("Your serial : %s\n", serial);

	return 0;
}

Finally

Keygen is attached, sources are HERE.

Acknowledgement
thanks @SmilingWolf, @mrexodia for help with x32dbg trace commands and expressions.

Question:
Maybe there is another way to analyze such "spaghetti"-obfuscated binary code?, or maybe someone could recommend another approach in omitting trash jump instructions. Maybe you know de-obfuscation technique or tool that is applicable to simplify analyzing of this "spaghetti". Please, write here or PM.

Keygen.exe

×
×
  • Create New...