Simple tutorial for cracking Opera 5.0 for redhat 7.1 --------------------------------------------------------------------- From Todd , Astalavista Group www.astalavista.com Opera 5.0 for RedHat 7.1 Qt dynamically linked version ( RPM for RedHat 7.1/Mandrake 8 1.58MB ) www.opera.com/downloads Let's Begin.................. Disassembled /usr/bin/opera with dasm.pl dasm.pl /usr/bin/opera /somewhere/opera.dis.txt Opened opera.dis.txt in vi Searched for registered and registration strings /registered /registration Found a number of references. This one looked VERY interesting. 0x08196dc8 pushl 0x6fc(%eax) 0x08196dce call 0x08162944 0x08196dd3 add $0x10,%esp 0x08196dd6 test %eax,%eax 0x08196dd8 je 0x08196e49 0x08196dda lea 0xe0(%esp,1),%ebx 0x08196de1 sub $0x8,%esp Possible reference to string: "Opera is already registered.Do you want to change the registration information?" 0x08196de4 push $0x8341540 0x08196de9 push %ebx 0x08196dea call 0x081f037c 0x08196def push %ebx Reference to function : latin1__C7QString 0x08196df0 call 0x0806469c 0x08196df5 add $0xc,%esp 0x08196df8 push %eax Possible reference to string: "Already registered" 0x08196df9 push $0x8341591 0x08196dfe push $0x4 0x08196e00 push $0x0 0x08196e02 push $0x0 0x08196e04 push $0x0 0x08196e06 pushl 0x8387cb8 0x08196e0c call 0x08070718 We see from the above snippet that the registration check is the returned value to the call at 0x08162944. If eax==0 we are UNregistered. If eax==1 we are REGISTERED. Let's look at this call at 0x08162944. We are interested in the end of the call. 0x081629e9 test %al,%al 0x081629eb mov $0xff,%cl 0x081629ed jne 0x081629f1 0x081629ef xor %ecx,%ecx Referenced from jump at 081629ed ; 0x081629f1 xor %edx,%edx 0x081629f3 test %cl,%cl 0x081629f5 je 0x081629f9 Referenced from jump at 08162963 ; 08162984 ; 081629d7 ; 0x081629f7 mov $0xff,%dl Referenced from jump at 081629f5 ; 0x081629f9 xor %eax,%eax 0x081629fb test %dl,%dl 0x081629fd sete %al 0x08162a00 add $0x30,%esp 0x08162a03 pop %ebx 0x08162a04 pop %esi 0x08162a05 pop %edi 0x08162a06 ret At 0x081629fd, eax is set to 1 if we are registered and set to 0 if we are not registered. Since, we are not registered, eax is returning 0. ie %al is not being set sete %al as machine code is 0F 94 C0......3 bytes We can verify this with: objdump -d opera | grep 81629f ( see below ) inc %eax is 0x40 ( 1 byte ) No-op 94C0 with 9090 ( 2 bytes ) A 3 byte crack in all. i.e. We want to change 0F94C0 to 409090 This will now always set %al to 1, hence we are registered. >From this: 8162963: 0f 85 8e 00 00 00 jne 0x81629f7 8162984: 75 71 jne 0x81629f7 81629d7: 75 1e jne 0x81629f7 81629ed: 75 02 jne 0x81629f1 81629f1: 31 d2 xor %edx,%edx 81629f3: 84 c9 test %cl,%cl 81629f5: 74 02 je 0x81629f9 81629f7: b2 ff mov $0xff,%dl 81629f9: 31 c0 xor %eax,%eax 81629fb: 84 d2 test %dl,%dl 81629fd: 0f 94 c0 sete %eax to this: 8162963: 0f 85 8e 00 00 00 jne 0x81629f7 8162984: 75 71 jne 0x81629f7 81629d7: 75 1e jne 0x81629f7 81629ed: 75 02 jne 0x81629f1 81629f1: 31 d2 xor %edx,%edx 81629f3: 84 c9 test %cl,%cl 81629f5: 74 02 je 0x81629f9 81629f7: b2 ff mov $0xff,%dl 81629f9: 31 c0 xor %eax,%eax 81629fb: 84 d2 test %dl,%dl 81629fd: 40 inc %eax 81629fe: 90 nop 81629ff: 90 nop And here's the patch in c NOTE: The offset in the patch is deduced by opening the opera binary in a hex editor and searching for the correct hex string. The editor will show you the offset of the byte(s) you wish to begin patching at. I used a program called hexcurse to find the single offset for this patch. i.e. 0x0011A9FD #include #include #include #define BANNER "dELTA-9's Opera Crack" #define HEADER "Free for Linux Users" #define AUTHOR "Opera-5.0 for RedHat-7.1" #define FILENAME "/usr/bin/opera" #define OPENTYPE "r+" #define HI(x) (x >> 8) #define LO(x) (x & 0x00FF) #define MAXBYTESPERCRACK 8 #define BYTE unsigned char #define ULONG unsigned long /* crkLen indicates the number of different offsets to make changes to */ #define crkLen 1 /* the list of offsets to start patching the file at (count should match crkLen ) */ ULONG crkOfs[crkLen] = { 0x0011A9FD, }; ULONG crkTbl[crkLen][MAXBYTESPERCRACK] = { {3, 0x0F40, 0x9490, 0xC090}, }; main(int argc, char *argv[]) { BYTE inbuf[MAXBYTESPERCRACK], inchar, bSame, patchbyte; FILE *fp; int bts, Ix, Jx; ULONG fOffset; long io_num; int result, numread, dupchars, alreadpatched; /*-------------------------------------------*/ /* print out the Header and the Author lines */ /*-------------------------------------------*/ printf("%s\n%s\n%s\n", BANNER, HEADER, AUTHOR); /*-------------------------------------------*/ /* Open the File */ /*-------------------------------------------*/ if( (fp = fopen( FILENAME, OPENTYPE )) == NULL ) { printf("Cannot open %s. Please move %s to the directory containing %s. Exiting.\n", FILENAME, argv[0], FILENAME); return(0); } /*-------------------------------------------*/ /* Verify the patch data */ /*-------------------------------------------*/ bSame = 1; alreadpatched = 0; for (Ix = 0; Ix < crkLen; Ix++) { /* Go to the spot */ fOffset = crkOfs[Ix]; result = fseek( fp, fOffset, SEEK_SET); if( result ) { printf( "Error Seeking in File.(s)\n" ); fclose(fp); return(0); } /* Read the signature buffer */ bts = (int) crkTbl[Ix][0]; dupchars = 0; numread = fread( inbuf, sizeof( char ), bts, fp); for (Jx = 0; Jx < bts; Jx++) { inchar = inbuf[Jx]; patchbyte = (BYTE) HI(crkTbl[Ix][Jx+1]); if (patchbyte != inchar) { if (inchar == ((BYTE) LO(crkTbl[Ix][Jx+1]))) dupchars++; bSame = 0; } } if (bSame == 0) { if (dupchars == bts) { alreadpatched++; } } } /* Are they the same? */ if (!bSame) { if (((alreadpatched+1) == crkLen) || (alreadpatched == crkLen)) printf("Patch has already been applied.\n"); else printf("Incorrect version\n"); return(0); } /*-------------------------------------------*/ /* Patch the Data */ /*-------------------------------------------*/ for (Ix = 0; Ix < crkLen; Ix++) { /* Go to the spot */ fOffset = crkOfs[Ix]; result = fseek( fp, fOffset, SEEK_SET ); if( result ) { printf( "Error Seeking in File.(s)\n" ); fclose(fp); return(0); } /* Read the signature buffer */ bts = (int) crkTbl[Ix][0]; for (Jx = 0; (Jx < bts); Jx++) { patchbyte = (BYTE) LO(crkTbl[Ix][Jx+1]); io_num = fputc(patchbyte, fp ); } } /*-------------------------------------------*/ /* Verify the patch was successful data */ /*-------------------------------------------*/ bSame = 1; for (Ix = 0; Ix < crkLen; Ix++) { /* Go to the spot */ fOffset = crkOfs[Ix]; result = fseek( fp, fOffset, SEEK_SET); if( result ) { printf( "Error Seeking in File.(s)\n" ); fclose(fp); return(0); } /* Read the patched buffer */ bts = (int) crkTbl[Ix][0]; numread = fread( inbuf, sizeof( char ), bts, fp); for (Jx = 0; Jx < bts; Jx++) { inchar = inbuf[Jx]; patchbyte = (BYTE) LO(crkTbl[Ix][Jx+1]); if (patchbyte != inchar) { bSame = 0; } } } /* Are they the same? */ if (!bSame) { printf("File was not patched successfully (write protected?)\n"); return(0); } /* Notify Success */ printf( "File was patched Successfully.\n" ); /* Close the file */ fclose(fp); /* Now return positively */ return (1); }